preg_replace不适用于某些单词/字符

时间:2017-05-12 02:19:01

标签: php regex encoding

$str = 'کس نے موسیٰ کے بارے میں سنا ہے؟';
$str = preg_replace('/(?<=\b)موسیٰ(?=\b)/u', 'Musa', $str);
$str = preg_replace('/(?<=\b)سنا(?=\b)/u', 'suna', $str);
echo $str;

无法替换موسیٰ。它应该提供کس نے Musa کے بارے میں suna ہے؟,而是提供کس نے موسیٰ کے بارے میں suna ہے؟

所有以ٰ结尾的字词都会发生这种情况,例如تعالیٰ。它适用于ٰ位于单词中间的单词(没有单词以ٰ开头)。这是否意味着\b不能与ٰ一起使用?这是一个错误吗?

2 个答案:

答案 0 :(得分:1)

原因是字边界在以下位置匹配:

  
      
  • 在字符串中的第一个字符之前,如果第一个字符是单词字符。
  •   
  • 在字符串中的最后一个字符之后,如果最后一个字符是单词字符。
  •   
  • 字符串中的两个字符之间,其中一个是单词字符,另一个不是单词字符。
  •   

“违规”符号是U+0670 ARABIC LETTER SUPERSCRIPT ALEF 属于 \p{Mn}(非间距标记Unicode类别),因此非单词符号。如果\b前面有一个属于\w的字符(字母,数字,_),则$str = 'کس نے موسیٰ کے بارے میں سنا ہے؟'; $str = preg_replace('/(?<!\w)موسیٰ(?!\w)/u', 'Musa', $str); $str = preg_replace('/(?<!\w)سنا(?!\w)/u', 'suna', $str); echo $str; // => کس نے Musa کے بارے میں suna ہے؟ 将匹配。

使用明确的边界,只有在搜索短语之前/后面没有单词字符时才使用:

(?<!\w)

请参阅PHP demo

(?!\w)是一个负面的背后隐藏,确保在随后的消费模式之前没有单词char,而Array.reduce()是一个负向前瞻,确保在前一个消费之后没有单词char图案。

答案 1 :(得分:0)

Be careful that

  

\b\B ...是根据\w\W定义的。

\w匹配属于ASCII表的字符,但使用(*UCP)选项或u unicode修饰符定义\w更改时,还包括来自其他语言的所有其他字母但不是组合标记

这样说,\b永远不会匹配ٰ这样的标记看到非单词字符的位置,因为标记本身被视为非单词字符。

你想要做的更像是弄清楚在موسیٰ之前或之后是否有任何非单词字符,因此断言\S元字符可以完成这项任务:

(?<!\S)موسیٰ(?!\S)

完成此类任务的另一种方法是使用ICU库transliterating整个输入字符串删除所有重音,然后尝试匹配不包含组合标记{{1}的单词موسی }}:

ٰ

输出:

<?php

$strings = [
    'is' => 'کس نے موسیٰ کے بارے میں سنا ہے؟', // input string
    'wts' => 'موسیٰ' // word to search
];

array_walk($strings, function(&$value) {
    $value = transliterator_transliterate('[:Nonspacing Mark:] Remove;', $value);
});

// word boundaries now can be used
echo preg_replace('/\b' . $strings['wts'] . '\b/u', 'musa', $strings['is']);