2009-09-08 6 views
8

In PHP < 6, qual è il modo migliore per dividere una stringa in una matrice di caratteri Unicode? Se l'input non è necessariamente UTF-8?Qual è il modo migliore per dividere una stringa in una matrice di caratteri Unicode in PHP?

Desidero sapere se il set di caratteri Unicode in una stringa di input è un sottoinsieme di un altro set di caratteri Unicode.

Perché non eseguire direttamente la famiglia di funzioni mb_, poiché la prima coppia di risposte no?

+1

Ti rendi conto che confrontare i caratteri Unicode non è banale, a seconda del tipo di confronto che desideri? Ad esempio, è possibile scrivere ü come U + 00DC o come U + 0075 U + 0308. – derobert

+0

Sì, lo realizzo. Se fosse diventato un problema, avrei dovuto trasformare l'input in uno dei moduli normali Unicode prima della divisione. – joeforker

risposta

14

è possibile utilizzare la 'u' modificatore con PCRE regex; vedi Pattern Modifiers (citando):

u (PCRE8)

Questo modificatore attiva ulteriori funzionalità di PCRE che sono incompatibili con Perl. Le stringhe modello vengono considerate UTF-8. Questo modificatore è disponibile da PHP 4.1.0 o successivo su Unix e da PHP 4.2.3 su win32. La validità UTF-8 del modello è verificata da PHP 4.3.5.

Per esempio, considerando questo codice:

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

si otterrà un risultato inutilizzabile:

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) 

Ma, con questo codice:

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

(Si noti la 'u' alla fine della regex)

si ottiene ciò che si vuole:

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) 

Spero che questo aiuti :-)

+0

+1 buon esempio dettagliato! :) –

+0

@Shadi Almosri: grazie :-) –

5

Prova questo:

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

+1 È intelligente! – Gumbo

1

Se per qualche motivo il modo regex non è abbastanza per voi. Una volta ho scritto lo Zend_Locale_UTF8 che è stato abbandonato ma potrebbe aiutarti se decidi di farlo da solo.

In particolare uno sguardo alla classe Zend_Locale_UTF8_PHP5_String che legge nelle stringhe Unicode e di lavorare con loro li si divide in singoli caratteri (che possono consistere di più byte, ovviamente).

EDIT: Ho appena relaized che di ZF svn-browser è giù così ho copiato i metodi importanti per convenienza:

/** 
* 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

sono stato in grado di scrivere una soluzione che utilizza mb_*, tra cui un viaggio a UTF -16 e schienale in un probabilmente sciocco tentativo di accelerare la stringa di indicizzazione:

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

ho avuto più fortuna evitando mb_internal_encoding e solo specificando tutto a eac h chiamata mb_*. Sono sicuro che finirò con la soluzione preg.

6

Leggermente più semplice di quanto preg_match_all:

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

Questo si torna dà una matrice 1-dimensionale di caratteri. Non c'è bisogno di un oggetto di partite.

+0

Questa risposta è quella che ha più senso, cioè logicamente l'obiettivo è quello di dividere, non ci interessa fare corrispondere ogni singolo carattere (anche se lo stesso può essere fatto nel sfondo). Stavo per rispondere alla domanda con la tua soluzione, ma con una piccola differenza: il limite (3 ° parametro) potrebbe avere 'NULL' invece di' -1' perché «' -1', '0' o' NULL' significa " no limit "e, come standard su PHP, puoi usare' NULL' per [saltare al parametro flags] (http://php.net/manual/en/function.preg-split.php) ». – Armfoot

Problemi correlati