如何排序UTF-8字符串数组?(How to sort an array of UTF-8 stri

2019-06-17 18:54发布

我currentyl对如何排序包含在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"
}

这完全是胡说八道。 使用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-8字符串数组语言环境感知?

刚指出,这似乎是PHP在Windows上的问题,与同一片段de_DE.utf8作为区域工作的Linux机器上。 然而这个Windows的具体问题的解决方案将是很好...

Answer 1:

$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国际延伸 。 它分布在PHP 5.3来源,但可能被禁用某些版本。 例如,在Debian的它是在包装的php5-国际机场。

Collator::compare是有用的usort



Answer 2:

更新对这个问题:

即使解决此问题的讨论表明,我们可以发现一个PHP错误strcoll()和/或setlocale() ,这显然并非如此。 问题是相当Windows的CRT实现的限制setlocale() (PHP的setlocale()是就在CRT电话薄包装)。 下面是一个引用MSDN网页“的setlocale,_wsetlocale” :

该组可用的语言,国家/地区代码和代码的网页的包括除每个字符要求多于两个字节,例如UTF-7和UTF-8代码页由Win32 NLS API支持的所有那些 如果你提供一个代码页像UTF-7或UTF-8的setlocale将失败,返回NULL。 使用setlocale支持的一套语言和国家/地区代码的列在语言和国家/地区字符串。

因此,它是不可能在Windows中使用PHP语言环境感知的字符串操作时,字符串多字节编码。



Answer 3:

最终,这个问题不能以简单的方式来解决,而无需使用由ΤΖΩΤΖΙΟΥ的建议重新编码字符串(UTF-8→Windows的1252或ISO-8859-1),由于通过Huppie发现了一个明显的错误PHP。 总结这个问题,我创建了下面的代码片段,清楚地表明,这个问题是与strcoll()函数使用65001 Windows的UTF-8代码页时。

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_ *编码和语言环境,必须再改)时,片段也适用。

我提交的bug报告bugs.php.net : 错误#46165与strcoll()不能在Windows UTF-8字符串的工作 。 如果您遇到了同样的问题,你可以给你的反馈,错误报告页面上的PHP团队(其他两个,可能是相关的,是伪造的错误进行了分类-我不认为这个bug是 ;-)。

感谢大家。



Answer 4:

这是一个非常复杂的问题 ,因为UTF-8编码的数据可包含(从整理不同的方式在不同的区域设置许多8位编码即字符)任何Unicode字符。

或许,如果你转换您的UTF-8的数据转换成Unicode(不熟悉PHP的unicode功能,比较遗憾),然后归他们进入NFD或NFKD ,然后排序的代码点可能会给一些整理,将是有意义的,你(即“A”之前的“A”)。

检查我提供的链接。

编辑:既然你提到你的输入数据是明确的(我假设他们都属于“窗口1252”代码页),那么你应该做下面的转换:UTF-8→统一→Windows的1252,其上的Windows 1252编码后的数据做了某种选择“CP1252”的语言环境。



Answer 5:

使用具有代码页1252你的例子在这里工作完全没有我的Windows开发机器上。

$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 Äpfel 2147483647
Ungetüme Birnen 2147483647
Ungetüme Apfel 2147483647
Ungetüme Ungetiere 2147483647
Österreich Ungetüme 2147483647
Äpfel Ungetiere 2147483647
Äpfel Birnen 2147483647
Apfel Äpfel 2147483647
Ungetiere Birnen 2147483647

我发现一些bug报告已被标记为假的 ......你所拥有的最好的办法是在提交错误报告,我想虽然...



Answer 6:

我发现这个下面辅助函数的字符串的所有字母转换为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"
}


Answer 7:

我面临着与德国“Umlaute”同样的问题。 经过一番研究,这个工作对我来说:

$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] =>埃及
[1] =>英国
[2] =>法国
[3] =>奥地利
[4] =>瑞士



Answer 8:

你整理需要匹配的字符集。 因为你的数据是UTF-8编码,您应该使用UTF-8排序规则。 它可以以不同的不同的平台上被命名,但良好的猜测是de_DE.utf8

在UNIX系统上,你可以用命令当前安装的语言环境列表

locale -a


文章来源: How to sort an array of UTF-8 strings?