如何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