2010-02-02 29 views
8

preg_match_all('/[aäeëioöuáéíóú]/u', $in, $out, PREG_OFFSET_CAPTURE);UTF-8 preg_match_all(PHP)字符

如果$in = 'hëllo'$out是:

array(1) { 
[0]=> 
    array(2) { 
    [0]=> 
    array(2) { 
     [0]=> 
     string(2) "ë" 
    [1]=> 
    int(1) 
} 
[1]=> 
array(2) { 
    [0]=> 
    string(1) "o" 
    [1]=> 
    int(5) 
    } 
} 
} 

o的位置应该是4,我读过有关这个问题在网上(在ë被计为2)。有没有解决方案?我见过mb_substr以及类似的东西,但是preg_match_all有没有这样的东西?

相关类型:它们在Python中等于preg_match_all? (返回与它们在字符串中的位置匹配的数组)

+1

你应该问的是在一个不同的问题,但是是...蟒蛇正则表达式matchobject默认包含mo.start匹配位置()和mo.end() –

回答

0

PHP不支持unicode非常好,所以很多字符串函数(包括preg_ *)仍然会计数字节而不是字符。

我试着通过编码和解码字符串来找到解决方案,但最终都归结为preg_match_all函数。

关于python的东西:python正则表达式matchobject默认包含匹配位置mo.start()和mo.end()。请参阅:http://docs.python.org/library/re.html#finding-all-adverbs-and-their-positions

+0

显然它计划在PHP6中修复,但到2016年(6年后)仍然只是在讨论中。喜欢PHP开发人员。他们没有实际的线索。 –

6

这不是一个错误,PREG_OFFSET_CAPTURE引用字符串中字符的字节偏移量。

mb_ereg_search_pos表现方式相同。一种可能性是前向编码更改为UTF-32,然后除以4的位置(因为所有的Unicode代码单元被表示为UTF-32 4字节序列):

mb_regex_encoding("UTF-32"); 
$string = mb_convert_encoding('hëllo', "UTF-32", "UTF-8"); 
$regex = mb_convert_encoding('[aäeëioöuáéíóú]', "UTF-32", "UTF-8"); 
mb_ereg_search_init ($string, $regex); 
$positions = array(); 
while ($r = mb_ereg_search_pos()) { 
    $positions[] = reset($r)/4; 
} 
print_r($positions); 

给出:

​​

您也可以将二进制位置转换为代码单元位置。为UTF-8,次优的实现是:

function utf8_byte_offset_to_unit($string, $boff) { 
    $result = 0; 
    for ($i = 0; $i < $boff;) { 
     $result++; 
     $byte = $string[$i]; 
     $base2 = str_pad(
      base_convert((string) ord($byte), 10, 2), 8, "0", STR_PAD_LEFT); 
     $p = strpos($base2, "0"); 
     if ($p == 0) { $i++; } 
     elseif ($p <= 4) { $i += $p; } 
     else { return FALSE; } 
    } 
    return $result; 
} 
2

有简单的解决方法,的preg_match之后使用()的结果进行匹配。您需要使用以下迭代每场比赛的结果,并重新分配位置值:

$utfPosition = mb_strlen(substr($wholeSubjectString, 0, $capturedEntryPosition), 'utf-8'); 

Windows下的测试在PHP 5.4,只依赖于多字节PHP扩展。

0

如何通过正则表达式分割UTF-8 $string的另一种方式是使用函数preg_split()。这是我工作的解决方案:

$result = preg_split('~\[img/\d{1,}/img\]\s?~', $string, -1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE); 

PHP 5.3.17