如何对UTF-8字符串数组进行排序?

2022-08-30 16:32:38

我目前不知道如何对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 字符串区域设置感知的数组进行排序?

刚刚注意到这似乎是Windows上的PHP问题,因为使用de_DE.utf8作为区域设置的相同代码段适用于Linux机器。尽管如此,这个特定于Windows的问题的解决方案会很好...


答案 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)

该类在 PECL 国际扩展中定义。它与 PHP 5.3 源代码一起分发,但在某些版本中可能会被禁用。例如,在 Debian 中,它是在 php5-intl 软件包中。Collator

Collator::compare对于 很有用。usort


答案 2

有关此问题的更新:

尽管围绕这个问题的讨论表明,我们可能已经发现了一个带有strcoll()和/或setlocale()的PHP错误,但事实显然并非如此。问题在于 setlocale() 的 Windows CRT 实现的局限性(PHPs 只是围绕 CRT 调用的一个薄包装)。以下是MSDN页面“setlocale,_wsetlocale”的引用setlocale()

可用语言、国家/地区代码和代码页集包括 Win32 NLS API 支持的所有语言、国家/地区代码页,但每个字符需要两个字节以上的代码页(如 UTF-7 和 UTF-8)除外。如果提供像 UTF-7 或 UTF-8 这样的代码页,则 setlocale 将失败,返回 NULL。setlocale 支持的语言和国家/地区代码集在“语言”和“国家/地区字符串”中列出。

因此,当字符串是多字节编码时,不可能在 Windows 上的 PHP 中使用区域设置感知字符串操作。