2010-02-02 35 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