2009-05-07 16 views
19

自然順序アルゴリズムを使用してPHPでUnicode/UTF-8文字で配列をソートすることはできますか?例えば(この配列の順序が正しく注文される):UnicodeをサポートするPHPのNaturalソートアルゴリズム?

$array = array 
(
    0 => 'Agile', 
    1 => 'Ágile', 
    2 => 'Àgile', 
    3 => 'Âgile', 
    4 => 'Ägile', 
    5 => 'Ãgile', 
    6 => 'Test', 
); 

私はASORT($配列)をしようとすると、私は次のような結果を得る:

Array 
(
    [0] => Agile 
    [6] => Test 
    [2] => Àgile 
    [1] => Ágile 
    [3] => Âgile 
    [5] => Ãgile 
    [4] => Ägile 
) 

とnatsort($配列)を使用しました:

Array 
(
    [2] => Àgile 
    [1] => Ágile 
    [3] => Âgile 
    [5] => Ãgile 
    [4] => Ägile 
    [0] => Agile 
    [6] => Test 
) 

PHP 5で正しい結果の順序(0,1,2,3,4,5,6)を返す関数を実装するにはどうすればよいですか?すべてのマルチバイト文字列関数(mbstring、iconv、...)は私のシステムで利用可能です。

EDIT:キーではなく値をnatsort()したいのですが、明示的にキーを定義していて、sort()の代わりにasort()を使用している唯一の理由は、ここでユニコード値のソートが間違っていました。

答えて

11

Nailed it!

$array = array('Ägile', 'Ãgile', 'Test', 'カタカナ', 'かたかな', 'Ágile', 'Àgile', 'Âgile', 'Agile'); 

function Sortify($string) 
{ 
    return preg_replace('~&([a-z]{1,2})(acute|cedil|circ|grave|lig|orn|ring|slash|tilde|uml);~i', '$1' . chr(255) . '$2', htmlentities($string, ENT_QUOTES, 'UTF-8')); 
} 

array_multisort(array_map('Sortify', $array), $array); 

出力:

Array 
(
    [0] => Agile 
    [1] => Ágile 
    [2] => Âgile 
    [3] => Àgile 
    [4] => Ãgile 
    [5] => Ägile 
    [6] => Test 
    [7] => かたかな 
    [8] => カタカナ 
) 

さらに良い:@tchristする

if (extension_loaded('intl') === true) 
{ 
    collator_asort(collator_create('root'), $array); 
} 

ありがとう!

+3

実際に必要なのは、Unicode照合アルゴリズム(UCA)です。私は[この回答では] Perlのデモンストレーションをしています(http://stackoverflow.com/questions/1097908/how-do-i-sort-unicode-strings-alphabetically-in-python/5024116#5024116)、ここで私は適切なライブラリを持っていない人のためにシェル呼び出し可能なバージョンを提供してください。多分それはここでも助けになるかもしれません。 – tchrist

+0

@tchrist:UCAは私が探しているものです。ちょっとあなたの答えを詳しく見ていきます。 ;) –

1
natsort($array); 
$array = array_values($array); 
+0

いいです。私の投票を得ました。 – Babiker

+0

私の例のキーは問題ではなく、ユニコード値のソートに役立つだけです。 –

24

質問は、最初のように見えるほど簡単ではありません。これは、PHPがUnicodeをサポートしていないことが、あなたの強みを最大限に発揮する分野の1つです。

他のポスターで示唆されている通り、すべてのnatsort()は、並べ替えるタイプの配列の並べ替えとは関係ありません。あなたが探しているのはロケール対応ソートメカニズムです。拡張文字を含む文字列をソートすることは、常に使用されている言語の問題です。たとえば、AとÄは同じ文字(DIN 5007/1)のように並べ替えることができますが、実際には "AE"(DIN 5007/2)のように並べ替えることもできます。対照的に、スウェーデン語ではアルファベットの末尾にÄがあります。

Windowsを使用していない場合は、PHPが正確にこれを行うためのいくつかの機能を提供するので、あなたは幸いです。 setlocale()usort()strcoll()の組み合わせと言語の正しいUTF-8ロケールを使用すると、このような何かを得る:

$array = array('Àgile', 'Ágile', 'Âgile', 'Ãgile', 'Ägile', 'Agile', 'Test'); 
$oldLocal = setlocale(LC_COLLATE, '<<your_RFC1766_language_code>>.utf8'); 
usort($array, 'strcoll'); 
setlocale(LC_COLLATE, $oldLocal); 

それはソートするために、UTF-8ロケールのバリアントを使用することが必須だということに注意してくださいUTF-8文字列。上記の例のロケールを元の値にリセットするには、setlocale()を使用してロケールを設定すると、他の実行中のPHPスクリプトで副作用が発生する可能性があります。詳細はPHPマニュアルを参照してください。

Windowsマシンを使用している場合、現在この問題の解決方法はで、の解決策はありません。PHP 6より前にはありません。この特定の問題をターゲットにして、私自身のquestionを参照してください。

+1

素晴らしい洞察力、私はWindows上で開発していますが、これは* nixマシン上で動作します。もし私が間違っていないのであれば、PHP 5.3はこの種のソートをサポートしますが、私はset_locale()に頼ることを控えています。1)予測できない(OSが利用できるロケールに依存します) 2)スレッドセーフではなく、サーバーで予期しない動作が発生する可能性があります。 –

+0

マルチバイト版のord()関数を使用してソートすると、単純なsort()とまったく同じ結果が得られます。 =( –

+0

申し訳ありませんが、私はあなたの第2のコメントに従うことができません... –

0

私はこの問題でアソートで苦労しました。並べ替え

Array 
(
    [xa] => África 
    [xo] => Australasia 
    [cn] => China 
    [gb] => Reino Unido 
    [us] => Estados Unidos 
    [ae] => Emiratos Árabes Unidos 
    [jp] => Japón 
    [lk] => Sri Lanka 
    [xe] => Europa Del Este 
    [xw] => Europa Del Oeste 
    [fr] => Francia 
    [de] => Alemania 
    [be] => Bélgica 
    [nl] => Holanda 
    [es] => España 
) 

は終わりにアフリカを置きます。私はまた、これらのsetlocaleのための別の回避策は動作しませんてきたとintlモジュールを持っていない

$sort = array(); 
foreach($retval AS $key => $value) { 
    $v = str_replace('ä', 'a', $value); 
    $v = str_replace('Ä', 'A', $v); 
    $v = str_replace('Á', 'A', $v); 
    $v = str_replace('é', 'e', $v); 
    $v = str_replace('ö', 'o', $v); 
    $v = str_replace('ó', 'o', $v); 
    $v = str_replace('Ö', 'O', $v); 
    $v = str_replace('ü', 'u', $v); 
    $v = str_replace('Ü', 'U', $v); 
    $v = str_replace('ß', 'S', $v); 
    $v = str_replace('ñ', 'n', $v); 
    $sort[] = "$v|$key|$value"; 
} 
sort($sort); 

$retval = array(); 
foreach($sort AS $value) { 
    $arr = explode('|', $value); 
    $retval[$arr[1]] = $arr[2]; 
} 
+0

あなたはフランス語ですか?あなたはこの質問に私の答えをチェックしたいと思うかもしれません、私の 'preg_replace'アプローチは少し音質を良くし、' array_multisort'関数は値と非数値キーの関連付けも保持します。 –

0

:私は(私の目的と私の時間枠に適合している)このコードの汚い小さな作品でそれを解決しました有効:

// The array to be sorted 
$countries = array(
    'AT' => Österreich, 
    'DE' => Deutschland, 
    'CH' => Schweiz, 
); 

// Extend this array to your needs. 
$utf_sort_map = array(
    "ä" => "a", 
    "Ä" => "A", 
    "Å" => "A", 
    "ö" => "o", 
    "Ö" => "O", 
    "ü" => "u", 
    "Ü" => "U", 
); 

uasort($my_array, function($a, $b) use ($utf_sort_map) { 
    $initial_a = mb_substr($a, 0, 1); 
    $initial_b = mb_substr($b, 0, 1); 

    if (isset($utf_sort_map[$initial_a]) || isset($utf_sort_map[$initial_b])) { 
    if (isset($utf_sort_map[$initial_a])) { 
     $initial_a = $utf_sort_map[$initial_a]; 
    } 

    if (isset($utf_sort_map[$initial_b])) { 
     $initial_b = $utf_sort_map[$initial_b]; 
    } 

    if ($initial_a == $initial_b) { 
     return mb_substr($a, 1) < mb_substr($b, 1) ? -1 : 1; 
    } 
    else { 
     return $initial_a < $initial_b ? -1 : 1; 
    } 
    } 

    return $a < $b ? -1 : 1; 
});