PHP 替换字符串中的字符代码 8217 时出现意外输出

PHP unexpected output when replacing character code 8217 in string

我 运行 遇到了意外的字符替换问题。字符编码为8217,.

我试过用斜杠转义字符,但没有什么区别。

php > $a = preg_replace('/([.,\'"’:?!])<\/a>/', '</a>', 'letter">Evolution’</a> </li>');
php > echo($a);
// => letter">Evolution/a> </li>

// Just to show that it works if the character is different
php > $a = preg_replace('/([.,\'"’:?!])<\/a>/', '</a>', 'letter">Evolution"</a> </li>');
php > echo($a);
letter">Evolution</a>" </li>

我希望它能输出

letter">Evolution</a>’ </li>

而不是

letter">Evolution/a> </li>

默认情况下,pcre(php 正则表达式引擎)将您的模式视为一连串的单字节编码字符。因此,当您编写 [’] 时,您将获得一个字符 class,其中三个字节对正确的单引号 (U+2019) 进行了编码,即:\xE2\x80 , \x99.

换句话说,在此默认模式下写入 "/[’]/" 就像写入 "/[\xE2\x80\x99]/""/[\x80\xE2\x99]/""/[\x99\xE2\x80]/" 等,正则表达式引擎看不到表示字符 但只有三个字节的字节序列。

这就是你得到奇怪结果的原因,因为 [.,\'"’:?!] 只会匹配 的最后一个字节,所以 \x99.

要解决此问题,您必须强制正则表达式引擎将您的模式读取为 UTF-8 编码字符串。您可以通过以下方式之一完成此操作:

  • preg_replace('~(*UTF)([.,\'"’:?!])</a>~', '</a>', 'letter">Evolution’</a> </li>');
  • preg_replace('~([.,\'"’:?!])</a>~u', '</a>', 'letter">Evolution’</a> </li>');

这次三个字节 \xE2\x80\x99 被视为字符 .

的原子序列

注意:(*UTF) 仅用于读取模式,但 u 修饰符做更多的事情:它扩展了 shorthand 个字符 classes(如 \s, \w,\d) 到 unicode 字符并检查主题字符串是否为 utf-8 编码。

只需在正则表达式中添加 unicode 标志:

$a = preg_replace('/([.,\'"’:?!])<\/a>/u', '</a>', 'letter">Evolution’</a> </li>');
#                              here ___^
echo($a);