2010-02-02 8 views
8

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

Wenn $in = 'hëllo'$out haben ist:

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

Die Position o 4. sein soll Ich habe im Internet über dieses Problem zu lesen (die ë wird als 2) gezählt. Gibt es dafür eine Lösung? Ich habe mb_substr und ähnliche gesehen, aber gibt es so etwas für preg_match_all?

Art von verwandten: Ist ihre Entsprechung von preg_match_all in Python? (Rückgabe eines Arrays von Übereinstimmungen mit ihrer Position in der Zeichenfolge)

+1

sollten Sie das fragen in einer anderen Frage, aber ja ... ein Python Regex Matchobject enthält das Spiel Position standardmäßig mo.start() und mo.end() –

Antwort

0

PHP unterstützt Unicode nicht sehr gut, daher zählen viele Zeichenfolgenfunktionen, einschließlich preg_ *, immer noch Byte statt Zeichen.

Ich versuchte, eine Lösung zu finden, indem ich Strings kodierte und decodierte, aber letztlich ging es um die Funktion preg_match_all.

Über die Python-Sache: Ein Python-Regex-Matchobjekt enthält standardmäßig die passende Position, mo.start() und mo.end(). Siehe: http://docs.python.org/library/re.html#finding-all-adverbs-and-their-positions

+0

Offenbar war es geplant, in PHP6 behoben zu werden, aber bis 2016 (6 Jahre später) ist es noch in der Diskussion. Ich muss PHP-Entwickler lieben. Sie haben keine wirkliche Ahnung. –

6

Dies ist kein Fehler, PREG_OFFSET_CAPTURE bezieht sich auf den Byte-Offset des Zeichens in der Zeichenfolge.

mb_ereg_search_pos verhält sich auf die gleiche Weise. Eine Möglichkeit ist die Codierung UTF-32 zu ändern, bevor und teilen sich dann die Position von 4 (da alle Unicode-Codeeinheiten dargestellt werden als 4-Byte-Sequenzen in 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); 

ergibt:

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

Sie können die Binärpositionen auch in Codeeinheitspositionen konvertieren. Für UTF-8, eine suboptimale Implementierung ist:

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

Es einfache Abhilfe ist, um nach preg_match() Ergebnisse angepasst werden. Sie müssen jedes Spiel Ergebnis iterieren und Positionswert zuweisen mit folgenden:

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

Getestet auf PHP 5.4 unter Windows, hängt von Multibyte-PHP-Erweiterung nur.

0

Eine andere Möglichkeit, UTF-8 $string durch einen regulären Ausdruck zu teilen, ist die Verwendung der Funktion preg_split(). Hier ist meine Arbeitslösung:

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

PHP 5.3.17