私は現在、PHPでUTF-8でエンコードされた文字列を含む配列をソートする方法についての手がかりを持っていません。配列はLDAPサーバーから取得されるため、データベースを介した並べ替え(問題はありません)は解決策ではありません。以下は私のWindows開発マシンでは機能しません(これは少なくとも可能な解決策であると思いますが):
_$array=array('Birnen', 'Äpfel', 'Ungetüme', 'Apfel', 'Ungetiere', 'Österreich');
$oldLocal=setlocale(LC_COLLATE, "0");
var_dump(setlocale(LC_COLLATE, 'German_Germany.65001'));
usort($array, 'strcoll');
var_dump(setlocale(LC_COLLATE, $oldLocal));
var_dump($array);
_
出力は次のとおりです。
_string(20) "German_Germany.65001"
string(1) "C"
array(6) {
[0]=>
string(6) "Birnen"
[1]=>
string(9) "Ungetiere"
[2]=>
string(6) "Äpfel"
[3]=>
string(5) "Apfel"
[4]=>
string(9) "Ungetüme"
[5]=>
string(11) "Österreich"
}
_
これはまったくナンセンスです。 setlocale()
のコードページとして1252を使用すると、別の出力が得られますが、それでも明らかに間違った出力が得られます。
_string(19) "German_Germany.1252"
string(1) "C"
array(6) {
[0]=>
string(11) "Österreich"
[1]=>
string(6) "Äpfel"
[2]=>
string(5) "Apfel"
[3]=>
string(6) "Birnen"
[4]=>
string(9) "Ungetüme"
[5]=>
string(9) "Ungetiere"
}
_
ロケールを認識したUTF-8文字列で配列を並べ替える方法はありますか?
これはWindowsの問題ではPHPのようです。ロケールとして使用される_de_DE.utf8
_の同じスニペットがLinuxマシンで機能するためです。それにもかかわらず、このWindowsのソリューション-特定の問題はニースです...
最終的に、この問題は、明らかなPHPバグが発見されたため、ΤΖΩΤΖΙΟΥによって提案されたように、再コード化された文字列(UTF-8→Windows-1252またはISO-8859-1)を使用せずに簡単な方法で解決することはできません。問題を要約するために、65001 Windows-UTF-8-codepageを使用する場合の問題がstrcoll()関数であることを明確に示す次のコードスニペットを作成しました。
function traceStrColl($a, $b) {
$outValue=strcoll($a, $b);
echo "$a $b $outValue\r\n";
return $outValue;
}
$locale=(defined('PHP_OS') && stristr(PHP_OS, 'win')) ? 'German_Germany.65001' : 'de_DE.utf8';
$string="ABCDEFGHIJKLMNOPQRSTUVWXYZÄÖÜabcdefghijklmnopqrstuvwxyzäöüß";
$array=array();
for ($i=0; $i<mb_strlen($string, 'UTF-8'); $i++) {
$array[]=mb_substr($string, $i, 1, 'UTF-8');
}
$oldLocale=setlocale(LC_COLLATE, "0");
var_dump(setlocale(LC_COLLATE, $locale));
usort($array, 'traceStrColl');
setlocale(LC_COLLATE, $oldLocale);
var_dump($array);
結果は次のとおりです。
string(20) "German_Germany.65001"
a B 2147483647
[...]
array(59) {
[0]=>
string(1) "c"
[1]=>
string(1) "B"
[2]=>
string(1) "s"
[3]=>
string(1) "C"
[4]=>
string(1) "k"
[5]=>
string(1) "D"
[6]=>
string(2) "ä"
[7]=>
string(1) "E"
[8]=>
string(1) "g"
[...]
同じスニペットがLinuxマシンで問題なく動作し、次の出力が生成されます。
string(10) "de_DE.utf8"
a B -1
[...]
array(59) {
[0]=>
string(1) "a"
[1]=>
string(1) "A"
[2]=>
string(2) "ä"
[3]=>
string(2) "Ä"
[4]=>
string(1) "b"
[5]=>
string(1) "B"
[6]=>
string(1) "c"
[7]=>
string(1) "C"
[...]
このスニペットは、Windows-1252(ISO-8859-1)でエンコードされた文字列を使用する場合にも機能します(もちろん、mb_ *エンコードとロケールを変更する必要があります)。
bugs.php.net にバグレポートを提出しました: バグ#46165 strcoll()はWindowsのUTF-8文字列では機能しません 。同じ問題が発生した場合は、バグレポートページのPHPチームにフィードバックを送信できます(他の2つの、おそらく関連するバグはbogus-このバグはbogus;-)ではないと思います。
あなた方全員に感謝します。
$a = array( 'Кръстев', 'Делян1', 'делян1', 'Делян2', 'делян3', 'кръстев' );
$col = new \Collator('bg_BG');
$col->asort( $a );
var_dump( $a );
プリント:
array
2 => string 'делян1' (length=11)
1 => string 'Делян1' (length=11)
3 => string 'Делян2' (length=11)
4 => string 'делян3' (length=11)
5 => string 'кръстев' (length=14)
0 => string 'Кръстев' (length=14)
Collator
クラスは PECL intl extension で定義されています。 PHP 5.3ソースで配布されますが、一部のビルドでは無効になる場合があります。たとえば、Debianではパッケージphp5-intlに含まれています。
Collator::compare
はusort
に役立ちます。
この問題に関する更新:
この問題に関する議論により、PHPバグ strcoll()
および/または setlocale()
、これは明らかにそうではありません。問題は、Windows CRT実装の制限です setlocale()
(PHPs setlocale()
はCRT呼び出しの単なる薄いラッパーです)。以下は MSDNページ "setlocale、_wsetlocale" の引用です。
使用可能な言語、国/地域コード、およびコードページのセットには、Win32 NLS APIでサポートされているものがすべて含まれます。ただし、UTF-7やUTF-7などの1文字あたり2バイト以上を必要とするコードページは除きます。 UTF-8。 UTF-7やUTF-8のようなコードページを提供すると、setlocaleは失敗し、NULLを返します。setlocaleでサポートされている言語および国/地域コードのセットは、Language and Country /にリストされています。リージョン文字列。
したがって、文字列がマルチバイトエンコードされている場合、WindowsではPHP内でロケール対応の文字列操作を使用することはできません。
これは非常に複雑です issue UTF-8でエンコードされたデータには任意のUnicode文字(つまり、ロケールごとに異なる照合を行う多くの8ビットエンコードの文字)を含めることができるためです。
おそらく、UTF-8データをUnicodeに変換し(PHP unicode関数、申し訳ありません))、それらを NFDまたはNFKD に正規化してから、コードでソートした場合ポイントは、あなたにとって意味のある照合を与えるかもしれません(つまり、「Ä」の前の「A」)。
私が提供したリンクを確認してください。
編集:入力データが明確であると述べているので(すべて「windows-1252」コードページにあると思います)、次の変換を行う必要があります:UTF-8→Unicode→Windows-1252、Windows-1252エンコードされたデータは、「CP1252」ロケールを選択してソートを実行します。
私はドイツ語の「ウムラウト」でも同じ問題に直面しています。いくつかの調査の後、これは私のために働きました:
$laender =array("Österreich", "Schweiz", "England", "France", "Ägypten");
$laender = array_map("utf8_decode", $laender);
setlocale(LC_ALL,"de_DE@euro", "de_DE", "deu_deu");
sort($laender, SORT_LOCALE_STRING);
$laender = array_map("utf8_encode", $laender);
print_r($laender);
結果:
アレイ
(
[0] =>Ägypten
[1] =>イングランド
[2] =>フランス
[3] =>オーストリア
[4] => Schweiz
)
I この次のヘルパー関数が見つかりました 文字列のすべての文字をASCIIここで非常に役立つ文字に変換します。
_function _all_letters_to_ASCII($string) {
return strtr(utf8_decode($string),
utf8_decode('ŠŒŽšœžŸ¥µÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝßàáâãäåæçèéêëìíîïðñòóôõöøùúûüýÿ'),
'SOZsozYYuAAAAAAACEEEEIIIIDNOOOOOOUUUUYsaaaaaaaceeeeiiiionoooooouuuuyy');
}
_
その後、単純なarray_multisort()
が必要なものを提供します。
_$array = array('Birnen', 'Äpfel', 'Ungetüme', 'Apfel', 'Ungetiere', 'Österreich');
$reference_array = $array;
foreach ($reference_array as $key => &$value) {
$value = _all_letters_to_ASCII($value);
}
var_dump($reference_array);
array_multisort($reference_array, $array);
var_dump($array);
_
もちろん、ヘルパー関数をより高度なニーズに合わせることができます。しかし、今のところ、それはかなり良さそうです。
_array(6) {
[0]=> string(6) "Birnen"
[1]=> string(5) "Apfel"
[2]=> string(8) "Ungetume"
[3]=> string(5) "Apfel"
[4]=> string(9) "Ungetiere"
[5]=> string(10) "Osterreich"
}
array(6) {
[0]=> string(5) "Apfel"
[1]=> string(6) "Äpfel"
[2]=> string(6) "Birnen"
[3]=> string(11) "Österreich"
[4]=> string(9) "Ungetiere"
[5]=> string(9) "Ungetüme"
}
_