2010-03-31 9 views
10

Provare a suddividere questa stringa "主楼 怎么 走" in caratteri separati (ho bisogno di un array) usando mb_split senza fortuna ... Qualche suggerimento?PHP: Split multibyte stringa (parola) in caratteri separati

Grazie!

+0

Partenza http://stackoverflow.com/questions/1032674/string-to-array-and-back-php – Smandoli

+5

Si prega di prestare attenzione, si tratta di una stringa multibyte. – Peterim

risposta

20

provare un'espressione regolare con 'u' opzione, per esempio

$chars = preg_split('//u', $string, -1, PREG_SPLIT_NO_EMPTY); 
+0

Funzionerà solo con codifica UTF-8. –

+0

D'accordo con Petr. L'ho provato con BIG5, non funziona! –

9

Un modo brutto per farlo è:

mb_internal_encoding("UTF-8"); // this IS A MUST!! PHP has trouble with multibyte 
           // when no internal encoding is set! 
$string = "....."; 
$chars = array(); 
for ($i = 0; $i < mb_strlen($string); $i++) { 
    $chars[] = mb_substr($string, $i, 1); // only one char to go to the array 
} 

Si dovrebbe anche provare la tua strada con mb_split con l'impostazione del internal_encoding prima di esso.

+0

'mb_internal_encoding (" UTF-8 ");' questo mi ha aiutato molto. – ivkremer

+0

Amo questa risposta, dal momento che ho faticato molto per trovare il modo semplice utf-8 per dividere il testo in parti altrettanto lunghe. Non solo per cantare personaggi ma parti. Dovevo solo modificare $ i + 5 e 5 come ultimo parametro mb_substr e ottenere il mio testo diviso in 5 stringhe di caratteri utf8. Molto bene. –

+0

ottima soluzione :) – clarkk

3

È possibile utilizzare funzioni grafema (PHP 5.3 o 1.0 Intl) e IntlBreakIterator (PHP 5.5 o Intl 3.0). Il codice seguente mostra la diffrenza tra le funzioni intl e mbstring e PCRE.

// http://www.php.net/manual/function.grapheme-strlen.php 
$string = "a\xCC\x8A" // 'LATIN SMALL LETTER A WITH RING ABOVE' (U+00E5) 
     ."o\xCC\x88"; // 'LATIN SMALL LETTER O WITH DIAERESIS' (U+00F6) 

$expected = ["a\xCC\x8A", "o\xCC\x88"]; 
$expected2 = ["a", "\xCC\x8A", "o", "\xCC\x88"]; 

var_dump(
    $expected === str_to_array($string), 
    $expected === str_to_array2($string), 
    $expected2 === str_to_array3($string), 
    $expected2 === str_to_array4($string), 
    $expected2 === str_to_array5($string) 
); 

function str_to_array($string) 
{ 
    $length = grapheme_strlen($string); 
    $ret = []; 

    for ($i = 0; $i < $length; $i += 1) { 
     $ret[] = grapheme_substr($string, $i, 1); 
    } 

    return $ret; 
} 

function str_to_array2($string) 
{ 
    $it = IntlBreakIterator::createCharacterInstance('en_US'); 
    $it->setText($string); 

    $ret = []; 
    $prev = 0; 

    foreach ($it as $pos) { 

     $char = substr($string, $prev, $pos - $prev); 

     if ('' !== $char) { 
      $ret[] = $char; 
     } 

     $prev = $pos; 
    } 

    return $ret; 
} 

function str_to_array3($string) 
{ 
    $it = IntlBreakIterator::createCodePointInstance(); 
    $it->setText($string); 

    $ret = []; 
    $prev = 0; 

    foreach ($it as $pos) { 

     $char = substr($string, $prev, $pos - $prev); 

     if ('' !== $char) { 
      $ret[] = $char; 
     } 

     $prev = $pos; 
    } 

    return $ret; 
} 

function str_to_array4($string) 
{ 
    $length = mb_strlen($string, "UTF-8"); 
    $ret = []; 

    for ($i = 0; $i < $length; $i += 1) { 
     $ret[] = mb_substr($string, $i, 1, "UTF-8"); 
    } 

    return $ret; 
} 

function str_to_array5($string) { 
    return preg_split('//u', $string, -1, PREG_SPLIT_NO_EMPTY); 
} 

Quando si lavora su ambiente di produzione, è necessario sostituire la sequenza di byte non valido con il carattere sostitutivo in quanto la quasi totalità grafema e funzioni mbstring non possono gestire sequenza di byte non valida. Se si ha un interesse, vedere la mia risposta precedente: https://stackoverflow.com/a/13695364/531320

Se non si utilizza la perfomance, è possibile utilizzare htmlspecialchars e htmlspecialchars_decode. Il merito di questo modo è supportare varie codifiche diverse da UTF-8.

function str_to_array6($string, $encoding = 'UTF-8') 
{ 
    $ret = []; 
    str_replace_callback($string, function($char, $index) use (&$ret) { $ret[] = $char; return ''; }, $encoding); 
    return $ret; 
} 

function str_replace_callback($string, $callable, $encoding = 'UTF-8') 
{ 
    $str_size = strlen($string); 
    $string = str_scrub($string, $encoding); 

    $ret = ''; 
    $char = ''; 
    $index = 0; 

    for ($pos = 0; $pos < $str_size; ++$pos) { 

     $char .= $string[$pos]; 

     if (str_check_encoding($char, $encoding)) { 

      $ret .= $callable($char, $index); 
      $char = ''; 
      ++$index; 
     } 

    } 

    return $ret; 
} 

function str_check_encoding($string, $encoding = 'UTF-8') 
{ 
    $string = (string) $string; 
    return $string === htmlspecialchars_decode(htmlspecialchars($string, ENT_QUOTES, $encoding)); 
} 

function str_scrub($string, $encoding = 'UTF-8') 
{ 
    return htmlspecialchars_decode(htmlspecialchars($string, ENT_SUBSTITUTE, $encoding)); 
} 

Se si desidera conoscere le specifiche di UTF-8, la manipolazione dei byte è il modo migliore per esercitarsi.

function str_to_array6($string) 
{ 
    // REPLACEMENT CHARACTER (U+FFFD) 
    $substitute = "\xEF\xBF\xBD"; 
    $size = strlen($string); 
    $ret = []; 

    for ($i = 0; $i < $size; $i += 1) { 

     if ($string[$i] <= "\x7F") { 

      $ret[] = $string[$i]; 

     } elseif ("\xC2" <= $string[$i] && $string[$i] <= "\xDF") { 

      if (!isset($string[$i+1])) { 

       $ret[] = $substitute; 
       return $ret; 

      } elseif ($string[$i+1] < "\x80" || "\xBF" < $string[$i+1]) { 

       $ret[] = $substitute; 

      } else { 

       $ret[] = substr($string, $i, 2); 
       $i += 1; 

      } 

     } elseif ("\xE0" <= $string[$i] && $string[$i] <= "\xEF") { 

      $left = "\xE0" === $string[$i] ? "\xA0" : "\x80"; 
      $right = "\xED" === $string[$i] ? "\x9F" : "\xBF"; 

      if (!isset($string[$i+1])) { 

       $ret[] = $substitute; 
       return $ret; 

      } elseif ($string[$i+1] < $left || $right < $string[$i+1]) { 

       $ret[] = $substitute; 

      } else { 

       if (!isset($string[$i+2])) { 

        $ret[] = $substitute; 
        return $ret; 

       } elseif ($string[$i+2] < "\x80" || "\xBF" < $string[$i+2]) { 

        $ret[] = $substitute; 
        $i += 1; 

       } else { 

        $ret[] = substr($string, $i, 3); 
        $i += 2; 

       } 

      } 

     } elseif ("\xF0" <= $string[$i] && $string[$i] <= "\xF4") { 

      $left = "\xF0" === $string[$i] ? "\x90" : "\x80"; 
      $right = "\xF4" === $string[$i] ? "\x8F" : "\xBF"; 

      if (!isset($string[$i+1])) { 

       $ret[] = $substitute; 
       return $ret; 

      } elseif ($string[$i+1] < $left || $right < $string[$i+1]) { 

       $ret[] = $substitute; 

      } else { 

       if (!isset($string[$i+2])) { 

        $ret[] = $substitute; 
        return $ret; 

       } elseif ($string[$i+2] < "\x80" || "\xBF" < $string[$i+2]) { 

        $ret[] = $substitute; 
        $i += 1; 

       } else { 

        if (!isset($string[$i+3])) { 

         $ret[] = $substitute; 
         return $ret; 

        } elseif ($string[$i+3] < "\x80" || "\xBF" < $string[$i+3]) { 

         $ret[] = $substitute; 
         $i += 2; 

        } else { 

         $ret[] = substr($string, $i, 4); 
         $i += 3; 

        } 

       } 

      } 

     } else { 

      $ret[] = $substitute; 

     } 

    } 

    return $ret; 

} 

Il risultato del benchmark tra queste funzioni è qui.

grapheme 
0.12967610359192 
IntlBreakIterator::createCharacterInstance 
0.17032408714294 
IntlBreakIterator::createCodePointInstance 
0.079245090484619 
mbstring 
0.081080913543701 
preg_split 
0.043133974075317 
htmlspecialchars 
0.25599694252014 
byte maniplulation 
0.13132810592651 

Il codice di riferimento è qui.

$string = '主楼怎么走'; 

foreach (timer([ 
    'grapheme' => 'str_to_array', 
    'IntlBreakIterator::createCharacterInstance' => 'str_to_array2', 
    'IntlBreakIterator::createCodePointInstance' => 'str_to_array3', 
    'mbstring' => 'str_to_array4', 
    'preg_split' => 'str_to_array5', 
    'htmlspecialchars' => 'str_to_array6', 
    'byte maniplulation' => 'str_to_array7' 
], 
[$string]) as $desc => $time) { 

    echo $desc, PHP_EOL, 
     $time, PHP_EOL; 
} 

function timer(array $callables, array $arguments, $repeat = 10000) { 

    $ret = []; 
    $save = $repeat; 

    foreach ($callables as $key => $callable) { 

     $start = microtime(true); 

     do { 

      array_map($callable, $arguments); 

     } while($repeat -= 1); 

     $stop = microtime(true); 
     $ret[$key] = $stop - $start; 
     $repeat = $save; 

    } 

    return $ret; 
} 
Problemi correlati