【问题标题】:Avoiding processing special preg characters in replacement string避免处理替换字符串中的特殊 preg 字符
【发布时间】:2011-04-16 13:20:03
【问题描述】:

当在 PHP 中使用 preg_replace() 并在运行时生成字符串时,可以使用 preg_quote() 保护搜索字符串中的特殊正则表达式字符(例如 '$' 或 '+')。但是在替换字符串中处理这个的正确方法是什么?以这段代码为例:

<?php

$haystack = '...a bit of sample text...';
$replacement = '\\HELLO WORLD$1.+-';
$replacement_quoted = preg_quote($replacement);

var_dump('--replacement', $replacement, '--replacement_quoted',
    $replacement_quoted, '--haystack', $haystack);

$result1 = preg_replace("@(bit) (of) (sample)@is", "\${1}" . $replacement ."$3", $haystack);
$result2 = preg_replace("@(bit) (of) (sample)@is", "\${1}" . $replacement_quoted ."$3", $haystack);

$replacement_new1 = str_replace('$', '\$', $replacement);
$replacement_new2 = str_replace('\\', '\\\\', $replacement_new1);

$result3 = preg_replace("@(bit) (of) (sample)@is", "\${1}" . $replacement_new1 ."$3", $haystack);
$result4 = preg_replace("@(bit) (of) (sample)@is", "\${1}" . $replacement_new2 ."$3", $haystack);

var_dump('--result1 (not quoted)', $result1, '--result2 (quoted)', $result2,
    '--result3 ($ escaped)', $result3, '--result4 (\ and $ escaped)', $result3);

?>

这是输出:

string(13) "--replacement"
string(17) "\HELLO WORLD$1.+-"
string(20) "--replacement_quoted"
string(22) "\\HELLO WORLD\$1\.\+\-"
string(10) "--haystack"
string(26) "...a bit of sample text..."
string(22) "--result1 (not quoted)"
string(40) "...a bit\HELLO WORLDbit.+-sample text..."
string(18) "--result2 (quoted)"
string(42) "...a bit\HELLO WORLD$1\.\+\-sample text..."
string(21) "--result3 ($ escaped)"
string(39) "...a bit\HELLO WORLD$1.+-sample text..."
string(27) "--result4 (\ and $ escaped)"
string(39) "...a bit\HELLO WORLD$1.+-sample text..."

如您所见,您无法使用 preg_quote() 取胜。如果你不调用它并且只传递未修改的字符串(result1),任何看起来像捕获令牌(上面的 $1)的东西都会被替换为 无论相应的捕获组包含什么。如果您确实调用它(result2),则捕获组没有问题,但是任何其他特殊的 PCRE 字符(例如 *)也会被转义,并且转义的字符设法继续存在于输出中。对我来说还很有趣的是,两个版本都在输出中产生了一个 \。

只有通过手动引用字符,特别是 $,才能使它起作用。这可以在 result3 和 result4 中看到。然而,继续使用 \ 的奇怪之处,为 \ 添加转义的 result3 和 result4 再次在输出中产生单个 \。在替换字符串的开头添加六个 \ 字符只会在 result1、result3 和 result4 的最终输出中生成两个 \,而其中三个用于 result2。

因此,似乎大多数问题都是通过手动转义 $ 字符来解决的。似乎 \ 字符也需要转义,但我需要再考虑一下,以弄清楚到底发生了什么。无论如何,这一切都很丑陋 - 在烦人的 \${1} 语法和必须手动转义某些字符之间,代码闻起来真的很烂而且容易出错。有什么我想念的吗?有没有干净的方法来做到这一点?

【问题讨论】:

    标签: php regex preg-replace pcre


    【解决方案1】:

    好吧,好吧,我不认为有任何真正令人满意的方式来处理这个问题。问题有两个:\ 字符和$ 字符。其他 PCRE 特殊字符在替换中似乎并不特殊。

    在\ 的情况下,事情的行为实际上与预期的一样,您需要使用\ 对其进行转义,既可以通过PHP 定义它,也可以将其传递给preg_replace()。在我的测试代码中,我只是将自己与两层转义混淆了。至于$,它应该单独留在PHP 端并用\ 转义到preg_replace()。就是这样。

    这里有一些代码来演示这一切:

    <?php
    
    ini_set('display_errors', 1);
    ini_set('error_reporting', E_ALL | E_STRICT);
    
    //real string: "test1 $1 test2 \\1 test3 \${1}"
    
    //real string manually \-escaped once for representing as a PHP string
    $test = 'test1 $1 test2 \\\\1 test3 \\${1}';
    var_dump('--test (starting PHP string - should match real string)', $test);
    
    $test = str_replace(array('\\', '$'), array('\\\\', '\\$'), $test);
    var_dump('--test (PHP string $-escaped and \-escaped again for preg_replace)', $test);
    
    $result = preg_replace("/bar/", $test, 'foo bar baz');
    
    var_dump('--result - bar should be replaced with original real string', $result);
    
    ?>
    

    输出:

    string(55) "--test (starting PHP string - should match real string)"
    string(30) "test1 $1 test2 \\1 test3 \${1}"
    string(66) "--test (PHP string $-escaped and \-escaped again for preg_replace)"
    string(35) "test1 \$1 test2 \\\\1 test3 \\\${1}"
    string(59) "--result - bar should be replaced with original real string"
    string(38) "foo test1 $1 test2 \\1 test3 \${1} baz"
    

    我的感觉是preg_quote() 应该是这里的解决方案,如果preg_replace() 会忽略除\ 本身和$ 之外的转义字符(例如+)。但是,它没有,迫使一个人进行手动转义。事实上,我认为这是一个错误,并将继续在 php.net 上提交它。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-12-07
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多