Preg_match_all UTF-8 karakter (PHP)

3 Cevap php

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

$in = 'hëllo' $out ise:

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

Pozisyonu o 4 olmalıdır. I (ë 2 olarak sayılır) çevrimiçi bu sorun hakkında okudum. Bunun için bir çözüm var mı? I mb_substr ve benzeri gördüm, ama preg_match_all için böyle bir şey var mı?

Ilgili Tür: preg_match_all Python onların eşdeğer midir? (Dize kendi pozisyonu ile maçı bir dizi dönen)

3 Cevap

PHP çok iyi unicode desteklemiyor, bu yüzden preg_ dahil * string fonksiyonları, bir sürü hala byte yerine karakterleri saymak. Bu PHP6 sabit olacak, ama o zamana kadar sadece onunla yaşamak gerekir.

Ben kodlama ve kod çözme dizeleri ile bir çözüm bulmaya çalıştım, ama sonuçta tüm preg_match_all işlevi aşağı geldi.

Python şey hakkında: Bir python regex matchobject maç varsayılan mo.start göre pozisyon () ve mo.end () içerir. Bkz: http://docs.python.org/library/re.html#finding-all-adverbs-and-their-positions

Bu PREG_OFFSET_CAPTURE dizesinde karakterin ofset byte ifade eder, bir hata değildir.

mb_ereg_search_pos aynı şekilde davranır. Bir olasılık önce UTF-32 kodlamasını değiştirmek ve (tüm unicode kod birimleri UTF-32 4-bayt dizileri olarak temsil edilmektedir, çünkü) daha sonra 4 ile pozisyonunu bölmek için:

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

verir:

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

Ayrıca kod birimi pozisyonlara ikili pozisyonları dönüştürebilirsiniz. UTF-8 için, optimal olmayan bir uygulamasıdır:

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

Preg_match () sonuçları eşleştirilmiş sonra kullanılmak üzere basit bir geçici çözüm vardır. Her maç sonucu yineleme ve aşağıdaki ile pozisyon değerini yeniden atamak gerekir:

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

Windows altında php 5.4 üzerinde test edilmiştir, sadece Multibyte PHP uzantısı bağlıdır.