arsort和krsort对中文排序不稳定,根本原因是其底层快排算法不保证稳定性,且中文多字节特性易导致字节序比较模糊、键值不规范及locale未显式设置。

arsort 和 krsort 对中文字符串排序不稳定,根本原因不是“中文”本身的问题,而是 PHP 这两个函数底层用的快速排序(quicksort)算法不保证稳定性,且中文字符串在比较时容易触发相等或边界模糊的比较结果。
arsort 按值排序时,中文字符串相等判断不可靠
PHP 的 arsort 默认使用松散比较(string comparison),对中文字符串按字节序(ASCII 或 UTF-8 编码字节流)逐字节比对。
但 UTF-8 中文字符是多字节(如 你 是 3 字节:E4 BD A0),而不同中文字符可能在某些编码/locale 环境下被截断、转换或隐式降级(比如被当成二进制串处理),导致:
- 相同语义的字符串(如全角/半角空格、带 BOM 的文件读入、不同输入法产生的变体)实际字节不一致
- 排序前未统一 normalize(如 NFC/NFD),
arsort就会把它们视为“不同值”,但又因内部哈希或分段排序逻辑,使相同字节序列的项在重复调用中顺序浮动
例如:
$data = [
'a' => '苹果',
'b' => '苹果', // 看似一样,但可能一个含零宽空格或来自剪贴板污染
'c' => '香蕉'
];
arsort($data); // 'a' 和 'b' 谁在前?不确定
这不是 bug,是 arsort 不承诺稳定性的直接体现。
krsort 按中文键名排序,键重复或编码混乱会放大不稳定性
krsort 排序依据是键名(key),如果键是中文字符串(如 ['苹果'=>10, '香蕉'=>20]),它会按 UTF-8 字节序降序排。问题在于:
- PHP 数组键强制转为字符串,若原始键来自用户输入、JSON 解析或数据库字段,可能混入不可见字符(如
\u200b、\ufeff) - 不同 PHP 版本或编译选项(如是否启用 ICU)对 UTF-8 多字节键的排序策略略有差异,同一数组在 PHP 8.1 和 8.3 中
krsort结果可能不一致 - 键名完全相同时(理论上不该发生),PHP 会覆盖;但“看起来相同”的键(如繁简体、拼音近似键)会被当作不同键,却因排序算法非稳定,相对位置无保障
所以你看到“中文键排序每次结果不一样”,大概率不是中文的问题,而是键本身不规范 + krsort 不保序。
真正稳定的中文排序得绕开 arsort/krsort
要让中文字符串排序可预测、跨版本一致,必须放弃内置函数的“捷径”:
- 使用
uasort+collator_compare(ICU 支持下) - 或先用
iconv('UTF-8', 'ASCII//TRANSLIT', $str)转拼音再排序(适合简单场景) - 或预生成排序权重字段(如用
pinyin()库生成拼音字符串作为辅助键),再用array_multisort
关键点:
-
uasort本身也不稳定,但你可以控制比较逻辑:当主键相等时,用原始数组键或索引做次级比较,从而模拟稳定排序 - 所有涉及中文的排序,必须显式指定 locale(如
setlocale(LC_COLLATE, 'zh_CN.UTF-8')),否则系统默认 C locale 会按纯字节序排,和人直觉完全脱节
不稳定性最常暴露在上线后——测试数据少、没覆盖重复值、没压测多版本 PHP。一旦业务依赖“相同中文值就该保持录入顺序”,只用 arsort 就等于埋雷。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











