没有正则表达式:报价之间的字符串?

时间:2017-10-21 06:08:17

标签: php string

我正在创建一个单词替换脚本。我遇到了一个障碍,忽略了引号之间的字符串,并且未能在这里找到一个不涉及正则表达式的合适解决方案。

我有一个工作片段,循环遍历字符串中的每个字符,并确定最近的引用是开始或结束引用(无论是单引号还是双引号)并忽略转义引号。问题是,为了提供100%准确的体验,它必须在每次字符串更改时运行(由于它的工作方式,它可以在单个函数中更改超过60K次),并且由于字符串长度潜在的,即使在相当短的脚本上,代码也需要很长时间。

有没有一种快速的方法可以确定字符串是在开放引号和双引号之间(单引号还是双引号)?忽略转义“和”。或者,您是否有关于如何优化代码段以使其运行速度更快的建议?删除此功能后,该过程几乎以首选速度运行(即时)。

作为练习,请考虑使用包含文本的变量将代码段复制并粘贴到脚本中。例如$ thisIsAQuote =“这是一个引用。”;而且,从那时起,一切都应该正确替换,除了$ thisIsAQuote应该保留其确切的文本。

但问题在于:我发现的其他解决方案将处理“这是一个引用”之间的所有内容。和... $ this->格式化[$ i - 1]!=“...好像它仍然在引号之间。因为就这些解决方案而言,”这是一个引用“中的最后一个引用。 if-check中的第一个引号是开放和引用的引号。另一个明显的问题是一些字符串包含带撇号的单词。不应将撇号视为单引号,但在我发现的所有解决方案中,它们都是。 / p>

换句话说,他们是“不知情”的解决方案。

    $quoteClosed = true;
    $singleQuoteClosed = true;

    $codeLength = mb_strlen($this->formatted);
    if ($codeLength == false)
        return;

    for ($i = 0; $i < $codeLength; $i++)
    {
        if ((!$quoteClosed || !$singleQuoteClosed) && ($this->formatted[$i] == '"' || $this->formatted[$i] == "'"))
        {
            if (!$quoteClosed && $this->formatted[$i - 1] != "\\")
                $quoteClosed = true;
            else if (!$singleQuoteClosed && $this->formatted[$i - 1] != "\\")
                $singleQuoteClosed = true;
        }
        else if ($this->formatted[$i] == '"' && ($i <= 0 || $this->formatted[$i - 1] != "\\"))
        {
            if ($quoteClosed && $singleQuoteClosed)
                $quoteClosed = false;
        }
        else if ($this->formatted[$i] == "'" && ($i <= 0 || $this->formatted[$i - 1] != "\\"))
        {
            if ($singleQuoteClosed && $quoteClosed)
                $singleQuoteClosed = false;
        }

        if ($quoteClosed && $singleQuoteClosed)
            $this->quoted[$i] = 0;
        else
            $this->quoted[$i] = 1;
    }

如果没有办法使上述方法更有效,是否有一种非正则表达式方法可以快速替换数组中所有子串的第二个数组中的子串而不会错过任何整个字符串?

substr_replace和str_replace似乎只替换整个字符串的“某些”部分,这就是迭代次数到位的原因。它循环一个while循环,直到strpos认为一个字符串不存在(它似乎从未做过......我可能使用它错了),或者循环10K次,以先发生者为准。

每轮运行上述代码片段会解决速度问题,但会留下“完全替代”问题,当然,仍然要注意避免替换引号内的任何内容。

    for ($a = 0; $a < count($this->keys); $a++)
    {
        $escape = 0;
        if ($a > count($this->keys) - 5)
            $this->formatted = $this->decodeHTML($this->formatted);

        while (strpos($this->formatted, $this->keys[$a]) !== false)
        {
            $valid = strpos($this->formatted, $this->keys[$a]);
            if ($valid === false || $this->quoted[$valid] === 1)
                break;

            $this->formatted = substr_replace($this->formatted, $this->answers[$a], $valid, mb_strlen($this->keys[$a]));
            $this->initializeQuoted();
            $escape++;

            if ($escape >= 10000)
                break;
        }

        if ($a > count($this->keys) - 5)
            $this->formatted = html_entity_decode($this->formatted);
    }
    $this->quoted = array();
    $this->initializeQuoted();
    return $this->formatted;

'keys'和'answers'是包含各种长度的单词的数组。 'formatted'是包含更改信息的新字符串。 'initializeQuoted'是上面的代码片段。我使用htmlentities和html_entity_decode来帮助摆脱使用键/答替换的空格。

忽略幻数(5s和10K)。

2 个答案:

答案 0 :(得分:1)

如果我理解正确,那么你可以这样做:

$replacements = [
    "test" => "banana",
    "Test" => "Banana"
];  

$brackets = [[0]];
$lastOpenedQuote = null;



for ($i = 0;$i < strlen($string);$i++) {

    if ($string[$i] == "\\") { $i++; continue; } //Skip escaped chars

    if ($string[$i] == $lastOpenedQuote) {
        $lastOpenedQuote = null;
        $brackets[count($brackets)-1][] = $i; 
        $brackets[] = [ $i+1 ];
    } elseif ($lastOpenedQuote == null && ($string[$i] == "\"" || $string[$i] == "'")) {
        $lastOpenedQuote = $string[$i];
        $brackets[count($brackets)-1][] = $i-1; 
        $brackets[] = [ $i ];
    }
}
$brackets[count($brackets)-1][] = strlen($string)-1;

$prev = 0;
$bits = [];
foreach ($brackets as $index => $pair) {
    $bits[$index] = substr($string,$pair[0],$pair[1]-$pair[0]+1);
    if ($bits[$index][0] != "\"" && $bits[$index][0] != "'") {
        $bits[$index] = str_replace(array_keys($replacements),array_values($replacements), $bits[$index]);
    }
}

请查看:http://sandbox.onlinephpfunctions.com/code/0453cb7941f1dcad636043fceff30dc0965541ee

现在,如果性能仍然是一个问题,请记住,每次执行每个字符串字符,并且每次都需要进行最少数量的检查,因此很难减少它。如果你需要更快的东西,例如你可能应该自下而上修改你的方法。在客户端逐步进行一些拆分,而不是在服务器端的整个字符串上进行拆分。

答案 1 :(得分:0)

我只是在做这个。希望这会给您一些其他的想法。

MATCH: ["]([\w\s\(\)\.\d\_\-\[\]\{\}]+|\s*)["]

REPLACE: ""

<?xml version="1.0" encoding="UTF-8"?>
<NotepadPlus>
    <ScintillaContextMenu>
        <!--
        NOTES: BLAH
        -->
        [WEBSITE]
        https://github.com/notepad-plus-plus/notepad-plus-plus/blob/master/PowerEditor/installer/nativeLang/english.xml
            -->
        <Item MenuId="Tools" MenuItemName="Generate..."/>
        <Item MenuEntryName="Edit" FolderName="Remove Lines" MenuItemName="Remove Empty Lines" ItemNameAs="Empty Lines"/>
        <Item MenuEntryName="Plugins" FolderName="Remove Lines" MenuItemName="Remove duplicate lines" ItemNameAs="Duplicate Lines (Plugin)"/>
        <Item MenuEntryName="Edit" FolderName="Remove Lines" MenuItemName="Remove Consecutive Duplicate Lines" ItemNameAs="Duplicate Lines"/>
        <Item MenuEntryName="Search" FolderName="Add Style Tokens" MenuItemName="Using 1st Style" ItemNameAs="1"/>
        <Item id="45003" Foldername="Convert" ItemNameAs="Macintosh (CR)"/>
        <Item id="0" FolderName="XML Tools"/>
        <Item MenuEntryName="Plugins" FolderName="XML Tools" MenuItemName="Options..." ItemNameAs="Options"/>

    </ScintillaContextMenu>
</NotepadPlus>

让我知道您是否还有其他想法。