我有 preg_match_all('/[aäeëioöuáéíóú]/u', $in, $out, PREG_OFFSET_CAPTURE);
如果$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)
}
}
}
Run Code Online (Sandbox Code Playgroud)
位置o应该是4.我已经在线阅读了这个问题(ë计算为2).这有解决方案吗?我见过mb_substr并且类似,但有这样的东西preg_match_all吗?
有点相关:它们与preg_match_allPython 相当吗?(返回与其在字符串中的位置匹配的数组)
这不是bug,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);
Run Code Online (Sandbox Code Playgroud)
得到:
Array
(
[0] => 1
[1] => 4
)
您还可以将二进制位置转换为代码单元位置.对于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;
}
Run Code Online (Sandbox Code Playgroud)
有一个简单的解决方法,可在 preg_match() 结果匹配后使用。您需要迭代每个匹配结果并使用以下内容重新分配位置值:
$utfPosition = mb_strlen(substr($wholeSubjectString, 0, $capturedEntryPosition), 'utf-8');
Run Code Online (Sandbox Code Playgroud)
在 Windows 下的 php 5.4 上测试,仅依赖于多字节 PHP 扩展。
PHP 不太支持 unicode,因此许多字符串函数(包括 preg_*)仍然计算字节而不是字符。
我尝试通过对字符串进行编码和解码来找到解决方案,但最终一切都归结为 preg_match_all 函数。
关于Python的事情:Python正则表达式匹配对象默认包含匹配位置mo.start()和mo.end()。请参阅:http ://docs.python.org/library/re.html#finding-all-adverbs-and-their-positions