2009-09-08 22 views
8

PHP < 6では、文字列をUnicode文字の配列に分割する最も良い方法は何ですか?入力が必ずしもUTF-8でない場合は?文字列をPHPでUnicode文字の配列に分割する最も良い方法は何ですか?

入力文字列内のUnicode文字セットが別のUnicode文字セットのサブセットであるかどうかを知りたい。

回答の最初のカップルは機能しなかったので、mb_ファミリーの機能に対してはまっすぐに実行してみませんか?

+1

あなたが望む比較のタイプに応じて、Unicode文字を比較することは自明ではないことに気づいていますか?たとえば、U + 00DCまたはU + 0075 U + 0308と書くことができます。 – derobert

+0

はい、私はそれを認識しています。問題になった場合は、分割前に入力をUnicode標準形式の1つに変換する必要があります。 – joeforker

答えて

14

あなたはPCRE正規表現と 'U' 修飾子を使用することができます。 Pattern Modifiers(引用)を参照してください。

U(PCRE8)

この修飾子は、Perlと互換性 あるPCREの追加 機能をオンにします。パターン 文字列はUTF-8として扱われます。この 修飾子は、UnixではPHP 4.1.0 以上、PHPではwin32ではPHP 4.2.3 から入手できます。 パターンのUTF-8妥当性は、PHP 4.3.5からチェックされています。例えば

、このコードを考慮:

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

あなたが使用できない結果得られます。

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) 

しかし、このコードで:

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

を(正規表現の最後に 'u'があることに注意してください)

は、あなたが望む結果を得る:何らかの理由で正規表現の方法はあなたのために十分でない場合

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) 

ホープこれは

+0

+1良い詳細な例! :) –

+0

@ Shadi Almosri:ありがとう:-) –

5

はこれを試してみてください:

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

+1それは賢いです! – Gumbo

1

:-)に役立ちます。私は以前に放棄されたZend_Locale_UTF8を書きましたが、あなたが自分でそれをやろうとするなら、あなたを助けるかもしれません。

特に、ユニコード文字列を読み込んで扱うクラスZend_Locale_UTF8_PHP5_Stringを見て、それらを使って1つの文字(複数のバイトで構成されていることがあります)に分割します。

EDIT

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

私はUTFへの旅行を含むmb_*を使用してソリューションを、書くことができました: 私はちょうどので、私は利便性のための重要な方法をコピーしたZFのSVNブラウザがダウンしていることをrelaized -16とバックの文字列のインデックスをスピードアップするために、おそらく愚かな試みで:

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

私はmb_internal_encodingを避け、より良い運を持っていたし、ちょうどEACにすべてを指定しますh mb_*コール。私はpregソリューションを使用して巻き上げると確信しています。

6

preg_match_allよりやや単純:

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

これは、あなたの文字の1次元配列をバック与えます。マッチオブジェクトの必要はありません。

+0

この回答は、理にかなっています。論理的には、目標は分割することです。すべての文字を一致させることはありません(同じことがバックグラウンド)。私はあなたの解決策について質問に答えるつもりでしたが、少し違いがありました。 "-1"、 "0"、または "NULL"は、 "-1"の代わりに " PHPの標準であるように、「NULL」を使用して[flagsパラメータにスキップ](http://php.net/manual/en/function.preg-split.php)»することができます。 – Armfoot