PHP Preg引擎中的Bug:环视Unicode问题

6
为什么以下js代码无效:
"آرد@".replace(/(?=.)/g,'!'); // returns: ""!آ!ر!د""

但是它的PHP等价物返回'!�!�!�!�!�!�'
preg_replace('/(?=.)/u', '!', 'آرد'); //returns '!�!�!�!�!�!�'

这仅适用于4.3.5-5.0.5、5.1.1-5.1.6版本。

参见:http://3v4l.org/jrV0W


1
@ 不是有效的分隔符。请查看 PHP 的分隔符页面。 - hjpotter92
2
@BackinaFlash 这是有效的! - Handsome Nerd
你确定它返回的是 null 吗?因为我得到的是 乱码,但绝对不是 null - Ja͢ck
请检查您的 PHP 文件编码。 - Tomas Ramirez Sarduy
@PHPst:这是因为pLp{L}的简写形式,可以查看我的答案。 - Tomas Ramirez Sarduy
显示剩余3条评论
2个回答

4
如果仅添加/u修饰符,则应将该模式视为utf-8。 第二个示例有效的原因是:
  1. 从PHP 5.1开始,您可以使用\p{L},它可以被翻译为:"是来自任何语言的任何字母"
  2. 除了标准符号表示法\p{L}之外,Java,Perl,PCRE和现在的PHP都允许您使用速记符号\pL。速记符号仅适用于单个字母的Unicode属性。

更新:为什么preg_replace('/(?=.)/u', '!', 'آرد'); //返回'!�!�!�!�!�!�'

如@MarkFox所述,在preg_replace()的上下文中,假定每个字符占用一个字节,并且您正在“正则表达式”多字节字符。这就是为什么您的替换输出具有两倍于预期的匹配项的原因,它匹配每个字符的每个字节(我推断为两个字节) -

无论您如何处理文档编码,都需要使用Unicode字符属性才能使其正常工作。

那个奇怪的符号怎么办?

当您看到那个“带有问号的奇怪的方块符号”(也称为替换字符)时,通常表示您具有80-FF(128-255)字节范围内的一个字节,并且系统正在尝试以UTF-8格式呈现它。

该整个字节范围对于UTF-8中的单字节字符无效,但在西方编码(如ISO-8859-1)中非常常见。


2
答案解释了为什么\pL与preg_replace一起使用。点号'.'元字符失败的原因是,在preg_replace的上下文中,它假设每个字符占用一个字节,而你正在处理的字符是多字节的。这就是为什么你的替换输出有两倍于预期的匹配数,它匹配了每个字符的每个字节(我推测每个字符占用两个字节)。 - Mark Fox
@PHPst:MarkFox 绝对正确,事实上,那将成为我的回答的一部分:D - Tomas Ramirez Sarduy
@PHPst:什么不是真的?我认为所有的都在那里。 - Tomas Ramirez Sarduy
当应用/u时,.变得兼容Unicode。例如:echo preg_replace('/./', '!', 'آرد');返回'!!!!!!'echo preg_replace('/./u', '!', 'آرد');返回'!!!' - Handsome Nerd

1

在测试了一些字符串后,我认为PREG引擎存在错误。前三行输出了预期的内容,但第四行有问题。

<?php
echo preg_replace('/./'       , '#', 'آرد')   . PHP_EOL; //✓
echo preg_replace('/./u'      , '#', 'آرد')   . PHP_EOL; //✓
echo preg_replace('/(?=.)/'   , '#', 'آرد')   . PHP_EOL; //✓
echo preg_replace('/(?=.)/u'  , '#', 'آرد')   . PHP_EOL; //✗
echo preg_replace('/(?=\pL)/' , '#', 'آرد')   . PHP_EOL; //?
echo preg_replace('/(?=\pL)/u', '#', 'آرد')   . PHP_EOL; //?

输出如下:
######
###
#�#�#�#�#�#�
#�#�#�#�#�#�
#آ#ر#د
#آ#ر#د

没有错误,请问您是否真正阅读了我的回答?我解释了为什么带点的那一行不起作用。 - Tomas Ramirez Sarduy
如你在我的例子中所看到的,/./u 在非环视的情况下是有效的。 - Handsome Nerd

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接