UTF-8 caracteres em preg_match_all (PHP)

Tenho preg_match_all('/[aäeëioöuáéíóú]/u', $in, $out, PREG_OFFSET_CAPTURE);

Se $in = 'hëllo' $out for:

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

A posição de o deve ser 4. Eu li sobre este problema online (o ë é contado como 2). Existe uma solução para isso? Eu vi mb_substr e similar, mas existe algo assim para preg_match_all ?

Tipo de relação: o seu equivalente a preg_match_all em Python? (Retornando uma série de correspondências com sua posição na string)

O PHP não suporta muito o Unicode, então muitas funções de string, incluindo preg_ *, ainda contam bytes em vez de caracteres.

Eu tentei encontrar uma solução codificando e descodificando strings, mas, em última análise, tudo acabou com a function preg_match_all.

Sobre a coisa de python: um jogo de combinação de python regex contém a posição de correspondência por padrão mo.start () e mo.end (). Veja: http://docs.python.org/library/re.html#finding-all-adverbs-and-the-positions

Este não é um erro, PREG_OFFSET_CAPTURE refere-se ao deslocamento de byte do caractere na string.

mb_ereg_search_pos se comporta da mesma maneira. Uma possibilidade é mudar a codificação para UTF-32 antes e depois dividir a posição por 4 (porque todas as unidades de código unicode são representadas como seqüências de 4 bytes no UTF-32):

 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); 

dá:

 Array
 (
     [0] => 1
     [1] => 4
 )

Você também pode converter as posições binárias em posições da unidade de código. Para UTF-8, uma implementação subóptima é:

 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; } 

Existe uma solução simples, a ser usada após os resultados preg_match () correspondentes. Você precisa iterar cada resultado de jogo e reatribuir o valor da posição com o seguinte:

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

Testado no php 5.4 no Windows, depende somente da extensão do PHP Multibyte.

Outra maneira de dividir UTF-8 $string por uma expressão regular é usar a function preg_split() . Aqui está a minha solução de trabalho:

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

PHP 5.3.17