如何sortingUTF-8string的数组?
我currentyl没有线索如何sorting在PHP中包含UTF-8编码string的数组。 该数组来自LDAP服务器,所以通过数据库sorting(没有问题)是没有解决scheme。 以下不能在我的Windows开发机器上工作(虽然我认为这应该是至less一个可能的解决scheme):
$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" }
这是完全废话。 使用1252作为setlocale()
的代码页提供了另一个输出,但仍然是一个明显错误的:
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-8string区域识别来sorting数组?
只是注意到,这似乎是Windows上的PHP问题,因为de_DE.utf8
作为语言环境使用相同的片段在Linux机器上工作。 尽pipe如此,这个Windows特定的问题的解决scheme将是很好的…
$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扩展中定义的。 它与PHP 5.3源分发,但可能会被禁用某些版本。 例如在Debian中,它在包php5-intl中。
Collator::compare
对于usort
很有用。
此问题的更新:
尽pipe围绕这个问题的讨论揭示了我们可以用strcoll()
和/或setlocale()
发现一个PHP错误,但事实并非如此。 问题是setlocale()
的Windows CRT实现的一个限制(PHPs setlocale()
只是CRT调用的一个薄包装)。 以下是引用MSDN页“setlocale,_wsetlocale” :
除了需要每个字符超过两个字节的代码页(例如UTF-7和UTF-8)之外 ,可用语言,国家/地区代码和代码页的集合包括Win32 NLS API支持的所有这些页面。 如果您提供的代码页如UTF-7或UTF-8,则setlocale将失败,返回NULL。 语言和国家/地区string中列出了setlocale支持的语言和国家/地区代码集。
因此,当string是多字节编码时,在Windows上的PHP中使用区域感知string操作是不可能的。
最终,由于Huppie发现明显的PHP错误,无法使用loggingstring(UTF-8→Windows-1252或ISO-8859-1),因此无法以简单的方式解决此问题。 总结这个问题,我创build了下面的代码片断,清楚地表明问题是使用65001 Windows-UTF-8代码页时的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)编码的string(当然必须改变mb_ *编码和区域设置)时,该代码段也可以工作。
我提交了关于bugs.php.net的错误报告: 错误#46165 strcoll()在Windows上不支持UTF-8string 。 如果您遇到同样的问题,您可以在错误报告页面上给PHP小组提供反馈(其他两个可能相关的错误已被归类为伪造 – 我不认为这个错误是假的 ;-)。
感谢大家。
这是一个非常复杂的问题 ,因为UTF-8编码的数据可以包含任何Unicode字符(即来自许多8位编码的字符,这些编码在不同的区域中以不同的方式进行比较)。
也许如果你把你的UTF-8数据转换成Unicode(不熟悉PHP unicode函数,对不起),然后将它们归一化为NFD或NFKD ,然后在代码点上sorting可能会给出一些对你有意义的归类(即“A”在“Ä”之前)。
检查我提供的链接。
编辑:因为你提到你的input数据是清楚的(我假设他们都落在“windows-1252”代码页),那么你应该做下面的转换:UTF-8→Unicode→Windows-1252,在Windows-1252编码数据进行sorting,select“CP1252”语言环境。
请注意,sorting顺序取决于语言。 在德语中,A和Ä有时可以按照同一个字母来sorting,有时可以将其sorting,因为它实际上是“AE”。
但是,瑞典语是在字母表的后面。
卡尔
在我的Windows开发机器上使用你的代码页1252的例子工作得很好。
$array=array('Birnen', 'Äpfel', 'Ungetüme', 'Apfel', 'Ungetiere', 'Österreich'); $oldLocal=setlocale(LC_COLLATE, "0"); var_dump(setlocale(LC_COLLATE, 'German_Germany.1252')); usort($array, 'strcoll'); var_dump(setlocale(LC_COLLATE, $oldLocal)); var_dump($array);
…略…
这是用PHP 5.2.6。 顺便说一句。
上面的例子是错误的 ,它使用ASCII编码而不是UTF-8。 我跟踪了strcoll()调用,看看我发现了什么:
function traceStrColl($a, $b) { $outValue = strcoll($a, $b); echo "$a $b $outValue\r\n"; return $outValue; } $array=array('Birnen', 'Äpfel', 'Ungetüme', 'Apfel', 'Ungetiere', 'Österreich'); setlocale(LC_COLLATE, 'German_Germany.65001'); usort($array, 'traceStrColl'); print_r($array);
得到:
UngetümeÄpfel2147483647 UngetümeBirnen 2147483647 UngetümeApfel 2147483647 UngetümeUngetiere 2147483647 ÖsterreichUngetüme2147483647 ÄpfelUngetiere 2147483647 ÄpfelBirnen 2147483647 ApfelÄpfel2147483647 Ungetiere Birnen 2147483647
我确实发现了一些被标记为错误的错误报告 …你所拥有的最好的办法是提交一个错误报告,但我想尽pipe…
我发现这个以下帮助函数将string的所有字母转换为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);
当然,你可以使帮助function适应更高级的需求。 但现在看起来不错。
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" }
我面对与德国“不知道”的同样的问题。 经过一番研究,这对我有效:
$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] =>Österreich
[4] => Schweiz
)
您的整理需要匹配字符集。 由于您的数据是UTF-8编码,您应该使用UTF-8归类。 在不同的平台上可以命名不同,但是一个好的猜测是de_DE.utf8
。
在UNIX系统上,您可以使用该命令获取当前安装的语言环境的列表
locale -a