2009-09-08 13 views
8

W PHP < 6, jaki jest najlepszy sposób na podzielenie łańcucha znaków na tablicę znaków Unicode? Jeśli wejście niekoniecznie musi być UTF-8?Jaki jest najlepszy sposób na podzielenie łańcucha znaków na tablicę znaków Unicode w PHP?

Chcę wiedzieć, czy zestaw znaków Unicode w ciągu wejściowym jest podzbiorem innego zestawu znaków Unicode.

Dlaczego nie uruchomić bezpośrednio dla rodziny funkcji mb_, ponieważ pierwsza para odpowiedzi nie?

+1

Czy zdajesz sobie sprawę, że porównywanie znaków Unicode jest nietrywialne, w zależności od rodzaju porównania, jakiego chcesz? Np. Można wpisać ü jako U + 00DC lub U + 0075 U + 0308. – derobert

+0

Tak, zdaję sobie z tego sprawę. Jeśli stanie się to problemem, będę musiał przekształcić dane wejściowe na jedną z normalnych formularzy Unicode przed podziałem. – joeforker

Odpowiedz

14

można użyć 'u' modyfikator z PCRE regex; patrz Pattern Modifiers (cytowanie)

U (PCRE8)

Modyfikator włącza dodatkowe funkcjonalności PCRE, który jest niezgodny z Perl. Wzorce są traktowane jako UTF-8. Ten modyfikator jest dostępny z PHP 4.1.0 lub nowszego na Unixie iz PHP 4.2.3 na win32. Poprawność UTF-8 wzorca jest sprawdzana od czasu PHP 4.3.5.

Na przykład, biorąc pod uwagę ten kod:

header('Content-type: text/html; charset=UTF-8'); // So the browser doesn't make our lives harder 
$str = "abc 文字化け, efg"; 

$results = array(); 
preg_match_all('/./', $str, $results); 
var_dump($results[0]); 

dostaniesz bezużyteczny wynik:

array 
    0 => string 'a' (length=1) 
    1 => string 'b' (length=1) 
    2 => string 'c' (length=1) 
    3 => string ' ' (length=1) 
    4 => string '�' (length=1) 
    5 => string '�' (length=1) 
    6 => string '�' (length=1) 
    7 => string '�' (length=1) 
    8 => string '�' (length=1) 
    9 => string '�' (length=1) 
    10 => string '�' (length=1) 
    11 => string '�' (length=1) 
    12 => string '�' (length=1) 
    13 => string '�' (length=1) 
    14 => string '�' (length=1) 
    15 => string '�' (length=1) 
    16 => string ',' (length=1) 
    17 => string ' ' (length=1) 
    18 => string 'e' (length=1) 
    19 => string 'f' (length=1) 
    20 => string 'g' (length=1) 

Ale z tym kodem:

header('Content-type: text/html; charset=UTF-8'); // So the browser doesn't make our lives harder 
$str = "abc 文字化け, efg"; 

$results = array(); 
preg_match_all('/./u', $str, $results); 
var_dump($results[0]); 

(Zwróć uwagę na "u" na końcu wyrażenia regularnego)

dostać to, co chcesz:

array 
    0 => string 'a' (length=1) 
    1 => string 'b' (length=1) 
    2 => string 'c' (length=1) 
    3 => string ' ' (length=1) 
    4 => string '文' (length=3) 
    5 => string '字' (length=3) 
    6 => string '化' (length=3) 
    7 => string 'け' (length=3) 
    8 => string ',' (length=1) 
    9 => string ' ' (length=1) 
    10 => string 'e' (length=1) 
    11 => string 'f' (length=1) 
    12 => string 'g' (length=1) 

Nadzieja to pomaga :-)

+0

+1 dobry szczegółowy przykład! :) –

+0

@Shadi Almosri: dziękuję :-) –

5

Spróbuj tego:

preg_match_all('/./u', $text, $array); 
+0

+1 To sprytnie! – Gumbo

1

Jeśli z jakiegoś powodu regex sposób nie jest dla ciebie za mało. Kiedyś napisałem Zend_Locale_UTF8, który jest porzucony, ale może ci pomóc, jeśli zdecydujesz się zrobić to samemu.

W szczególności spójrz na klasę Zend_Locale_UTF8_PHP5_String, która czyta w ciągach Unicode i do pracy z nimi dzieli je na pojedyncze znaki (które oczywiście mogą składać się z wielu bajtów oczywiście).

EDIT: Właśnie relaized że svn-browser ZF jest w dół, więc skopiowaniu ważnych metod wygody:

/** 
* Returns the UTF-8 code sequence as an array for any given $string. 
* 
* @access protected 
* @param string|integer $string 
* @return array 
*/ 
protected function _decode($string) { 

    $string  = (string) $string; 
    $length  = strlen($string); 
    $sequence = array(); 

    for ($i=0; $i<$length;) { 
     $bytes  = $this->_characterBytes($string, $i); 
     $ord  = $this->_ord($string, $bytes, $i); 

     if ($ord !== false) 
      $sequence[] = $ord; 

     if ($bytes === false) 
      $i++; 
     else 
      $i += $bytes; 
    } 

    return $sequence; 

} 

/** 
* Returns the UTF-8 code of a character. 
* 
* @see http://en.wikipedia.org/wiki/UTF-8#Description 
* @access protected 
* @param string $string 
* @param integer $bytes 
* @param integer $position 
* @return integer 
*/ 
protected function _ord(&$string, $bytes = null, $pos=0) 
{ 
    if (is_null($bytes)) 
     $bytes = $this->_characterBytes($string); 

    if (strlen($string) >= $bytes) { 

     switch ($bytes) { 
      case 1: 
       return ord($string[$pos]); 
       break; 

      case 2: 
       return ((ord($string[$pos]) & 0x1f) << 6) + 
         ((ord($string[$pos+1]) & 0x3f)); 
       break; 

      case 3: 
       return ((ord($string[$pos]) & 0xf) << 12) + 
         ((ord($string[$pos+1]) & 0x3f) << 6) + 
         ((ord($string[$pos+2]) & 0x3f)); 
       break; 

      case 4: 
       return ((ord($string[$pos]) & 0x7) << 18) + 
         ((ord($string[$pos+1]) & 0x3f) << 12) + 
         ((ord($string[$pos+1]) & 0x3f) << 6) + 
         ((ord($string[$pos+2]) & 0x3f)); 
       break; 

      case 0: 
      default: 
       return false; 
     } 
    } 

    return false; 
} 
/** 
* Returns the number of bytes of the $position-th character. 
* 
* @see http://en.wikipedia.org/wiki/UTF-8#Description 
* @access protected 
* @param string $string 
* @param integer $position 
*/ 
protected function _characterBytes(&$string, $position = 0) { 
    $char  = $string[$position]; 
    $charVal = ord($char); 

    if (($charVal & 0x80) === 0) 
     return 1; 

    elseif (($charVal & 0xe0) === 0xc0) 
     return 2; 

    elseif (($charVal & 0xf0) === 0xe0) 
     return 3; 

    elseif (($charVal & 0xf8) === 0xf0) 
     return 4; 
    /* 
    elseif (($charVal & 0xfe) === 0xf8) 
     return 5; 
    */ 

    return false; 
} 
0

byłem w stanie napisać rozwiązanie używając mb_*, w tym wycieczkę do UTF -16 iz powrotem w prawdopodobnie głupie próbując przyspieszyć indeksowanie wyrażenie:

$japanese2 = mb_convert_encoding($japanese, "UTF-16", "UTF-8"); 
$length = mb_strlen($japanese2, "UTF-16"); 
for($i=0; $i<$length; $i++) { 
    $char = mb_substr($japanese2, $i, 1, "UTF-16"); 
    $utf8 = mb_convert_encoding($char, "UTF-8", "UTF-16"); 
    print $utf8 . "\n"; 
} 

miałem więcej szczęścia unikając mb_internal_encoding i po prostu określając wszystko w EAC h mb_* połączenie. Jestem pewien, że zakończę korzystanie z rozwiązania preg.

6

Nieco prostsza niż preg_match_all:

preg_split('//u', $str, -1, PREG_SPLIT_NO_EMPTY) 

To daje z powrotem 1-wymiarową tablicę znaków. Nie ma potrzeby stosowania obiektu dopasowującego.

+0

Ta odpowiedź jest najbardziej sensowna, tzn. Logicznie celem jest podział, nie zależy nam na dopasowaniu każdej postaci (choć to samo można zrobić w tło). Chciałem odpowiedzieć na to pytanie twoim rozwiązaniem, ale z niewielką różnicą: limit (trzeci parametr) może mieć wartość "NULL" zamiast "-1", ponieważ "-1", "0" lub "NULL" oznacza " no limit "i, jak jest standardem w PHP, możesz użyć' NULL' do [przejdź do parametru flag] (http://php.net/manual/en/function.preg-split.php) ». – Armfoot

Powiązane problemy