【问题标题】:PHP: Regex to ignore escaped quotes within quotesPHP:正则表达式忽略引号内的转义引号
【发布时间】:2011-08-07 09:42:12
【问题描述】:

我在发布之前查看了相关问题,我无法修改任何相关答案以使用我的方法(不擅长正则表达式)。

基本上,这是我现有的线路:

$code = preg_replace_callback( '/"(.*?)"/', array( &$this, '_getPHPString' ), $code );

$code = preg_replace_callback( "#'(.*?)'#", array( &$this, '_getPHPString' ), $code );

它们都匹配包含在''"" 之间的字符串。我需要正则表达式来忽略它们之间包含的转义引号。因此'' 之间的数据将忽略\'"" 之间的数据将忽略\"

任何帮助将不胜感激。

【问题讨论】:

  • 您还需要能够处理转义的斜杠吗?换句话说,它是否应该假设任何以斜线开头的引号都被转义了,即使该斜线本身前面有一个斜线?
  • @Phoenix,如果你指的是\\"\\',那么不,我没有。
  • 如果你不处理转义字符,那么转义特定字符是无效的。

标签: php regex


【解决方案1】:

对于大多数字符串,您需要允许转义 anything(不仅仅是转义引号)。例如您很可能需要允许转义字符,例如 "\n""\t",当然还有转义字符:"\\"

这是一个常见问题,很久以前就已解决(和优化)。 Jeffrey Friedl 在他的经典著作中深入探讨了这个问题(例如):Mastering Regular Expressions (3rd Edition)。这是您要查找的正则表达式:

好:

"([^"\\]|\\.)*"
版本 1:工作正常,但效率不高。

更好:

"([^"\\]++|\\.)*""((?>[^"\\]+)|\\.)*"
版本 2:如果您有所有格量词或原子组,则效率更高(参见:使用原子组方法的 sin 的正确答案)。

最佳:

"[^"\\]*(?:\\.[^"\\]*)*"
版本 3:更高效。实现 Friedl 的:“unrolling-the-loop” 技术。不需要所有格或原子组(即,这可以在 Javascript 和其他功能较少的正则表达式引擎中使用。)

以下是双引号和单引号子字符串的 PHP 语法中推荐的正则表达式:

$re_dq = '/"[^"\\\\]*(?:\\\\.[^"\\\\]*)*"/s';
$re_sq = "/'[^'\\\\]*(?:\\\\.[^'\\\\]*)*'/s";

【讨论】:

  • +1 "[^"\\]*(?:\\.[^"\\]*)*""(\\.|[^"\\]+)*" 更好地避免交替和替补
  • 如果我想在双引号 (C#) 前面包含一个“可能的”@ 符号,我该怎么做?我尝试使用组和类,但无济于事。
  • @Brian Graham - 要在表达式前添加可选的@,只需在前导引号前添加@?。然而,事情并没有那么简单。对于 C# @"..." 字符串,嵌入的引号不是 \"(用反斜杠转义)而是 ""(连续两个引号)。在这种情况下,您想要的表达式是:@"[^"]*(""[^"]*)*".
  • +1 以获得解决此常见问题的选项的出色概述。 :)
  • @Brian Graham - 在我之前的评论中,我建议匹配@"..." C# 字符串的正则表达式是:@"[^"]*(""[^"]*)*",这是正确的。但是,要在 C# 中对该正则表达式进行编码,正则表达式字符串将被写入:@"@""[^""]*(""""[^""]*)*""".
【解决方案2】:

试试这样的正则表达式:

'/"(\\\\[\\\\"]|[^\\\\"])*"/'

一个(简短的)解释:

"                 # match a `"`
(                 # open group 1
  \\\\[\\\\"]     #   match either `\\` or `\"`
  |               #   OR
  [^\\\\"]        #   match any char other than `\` and `"`
)*                # close group 1, and repeat it zero or more times
"                 # match a `"`

下面的sn-p:

<?php
$text = 'abc "string \\\\ \\" literal" def';
preg_match_all('/"(\\\\[\\\\"]|[^\\\\"])*"/', $text, $matches);
echo $text . "\n";
print_r($matches);
?>

产生:

abc "string \\ \" literal" def
Array
(
    [0] => Array
        (
            [0] => "string \\ \" literal"
        )

    [1] => Array
        (
            [0] => l
        )

)

正如您在Ideone 上看到的那样。

【讨论】:

  • 我举了你的例子,但似乎无法让它发挥作用。直接复制粘贴不行,我也试过编辑也没用。
  • @Dark Slipstream,复制粘贴(不改变!)sn-p 不起作用?我觉得很难相信。您使用的是什么 PHP 版本?您是否尝试过 Ideone 链接?
  • 感谢您的帮助 Bart,在对字符串开头进行了调整后,我设法让它工作了。
  • 不匹配:"String with a linefeed\n"
【解决方案3】:

这有可能:

/"(?&gt;(?:(?&gt;[^"\\]+)|\\.)*)"/

/'(?&gt;(?:(?&gt;[^'\\]+)|\\.)*)'/

【讨论】:

  • 它与 Perl my ($new_str) = ($str =~ /'((?&gt;(?:(?&gt;[^'\\]+)|\\.)*))'/); 完美配合 - 例如。 '#10's footer' 会给#10'#10\'s footer'#10\'s footer'10\\\'s footer'10\\\'s footer 等等。
【解决方案4】:

根据一些粗略的基准,这似乎与展开循环一样快,但更容易阅读和理解。它首先不需要任何回溯。

"[^"\\]*(\\.[^"\\]*)*"

【讨论】:

  • 展开的循环。它与ridgerunner's answer 中的第三个正则表达式完全相同,只是您使用了捕获组(使其效率稍低)。
  • 嗯,我在发布之前对其进行了基准测试,但无法在两者之间产生一致的速度差异。
  • 我不知何故没有注意到这与 ridgerunner 的第三个答案基本相同。我的错。
  • 回首过去,我不应该提到效率。捕获组和非捕获组之间的性能差异非常小,几乎不会对整体性能产生显着影响。对于像这样简单的正则表达式来说,这当然无关紧要。
【解决方案5】:

这会将引号留在外面

(?<=['"])(.*?)(?=["'])

并使用global /g 将匹配所有组

【讨论】:

    【解决方案6】:

    根据 W3 资源: https://www.w3.org/TR/2010/REC-xpath20-20101214/#doc-xpath-StringLiteral

    一般的正则表达式是:

    "(\\.|[^"])*"
    

    (+捕获组先检查时不需要加反斜杠)

    解释:

    • "..." 引号之间的任何匹配
    • (...)*里面的长度可以从0到无穷大
    • \\.|[^"] 首先接受任何后面有斜线的字符 | (或)然后接受任何不是引号的字符

    为了更好地处理Any Quotes而更好地分组的正则表达式的PHP版本可以是这样的:

    <?php
        $str='"First \\" \n Second" then \'This \\\' That\'';
        echo $str."\n";
        // "First \" \n Second" then 'This \' That'
    
        $RX_inQuotes='/"((\\\\.|[^"])*)"/';
        preg_match_all($RX_inQuotes,$str,$r,PREG_SET_ORDER);
        echo $r[0][1]."\n";
        // First \" \n Second
    
        $RX_inAnyQuotes='/("((\\\\.|[^"])*)")|(\'((\\\\.|[^\'])*)\')/';
        preg_match_all($RX_inAnyQuotes,$str,$r,PREG_SET_ORDER);
        echo $r[0][2]." --- ".$r[1][5];
        // First \" \n Second --- This \' That
    ?>
    

    试试看:http://sandbox.onlinephpfunctions.com/code/4328cc4dfc09183f7f1209c08ca5349bef9eb5b4

    重要提示:在这个时代,对于不确定的内容,你必须在正则表达式的末尾使用u标志,如/.../u,以避免破坏multi-byte字符串,如@987654332 @,或者像mb_ereg_match这样的函数。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-05-01
      • 2013-05-10
      • 1970-01-01
      • 2014-05-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多