Intl.Collator 排文件名:数字片段、语言差异与稳定次序
- 发布时间:2026-10-23 22:12:14
- 本文热度:浏览 2 赞 0 评论 0
- 文章标签: JavaScript Intl 排序
- 全文共1字,阅读约需1分钟
文件名按普通字符串排序,file10 经常排在 file2 前面。手写一个拆数字的正则可以修掉这个例子,却很快会碰到大小写、重音字母、不同语言和相同排序权重的问题。先别急着把比较器扩成一套不完整的语言规则。
Intl.Collator 负责按语言排序字符串,numeric: true 让数字片段参与数值式比较。它很适合文件名这类“文本夹着数字”的展示列表,但不负责版本号协议、带单位金额或任意业务编码排序。
这一页做一个可调整语言与比较选项的文件名排序器。它保留原输入,不修改文件名;相同排序权重时保留输入次序。使用支持Intl.Collator的现代浏览器即可运行,不需要框架和外部服务。
完整页面先把排序合同显示出来
保存为 collator-sort.html:
<!doctype html>
<html lang="zh-CN">
<meta charset="utf-8">
<title>文件名语言排序</title>
<label>排序语言
<select id="locale">
<option value="en">英语</option>
<option value="de">德语</option>
<option value="sv">瑞典语</option>
<option value="zh-CN">中文</option>
</select>
</label>
<label><input id="numeric" type="checkbox" checked>按数字片段比较</label>
<label><input id="ignore-case" type="checkbox">忽略大小写,保留重音</label>
<p><label for="source">每行一个文件名,最多200行</label></p>
<textarea id="source" rows="9" cols="50">file10.txt
file2.txt
file02.txt
A.txt
a.txt
ä.txt
z.txt</textarea><br>
<button id="sort" type="button">排序</button>
<p id="status" role="status"></p>
<pre id="options"></pre>
<ol id="result"></ol>
<script>
function sortNames(names, locale, numeric, ignoreCase) {
const collator = new Intl.Collator(locale, {
usage: 'sort',
numeric,
sensitivity: ignoreCase ? 'accent' : 'variant'
});
const records = names.map((text, index) => ({ text, index }));
records.sort((left, right) => {
const order = collator.compare(left.text, right.text);
return order || left.index - right.index;
});
return { records, options: collator.resolvedOptions() };
}
const source = document.querySelector('#source');
const result = document.querySelector('#result');
const status = document.querySelector('#status');
document.querySelector('#sort').onclick = () => {
try {
const names = source.value.split(/\r?\n/).filter(text => text !== '');
if (names.length > 200 || names.some(text => text.length > 200)) {
throw new Error('最多200行,每行最多200个UTF-16代码单元');
}
const sorted = sortNames(names,
document.querySelector('#locale').value,
document.querySelector('#numeric').checked,
document.querySelector('#ignore-case').checked);
result.replaceChildren(...sorted.records.map(record => {
const item = document.createElement('li');
item.textContent = record.text;
return item;
}));
document.querySelector('#options').textContent =
JSON.stringify(sorted.options, null, 2);
status.textContent = `已排序 ${names.length} 项`;
} catch (error) {
status.textContent = error.message;
}
};
</script>
</html>
保持numeric勾选,file2应排在file10前;取消勾选,再观察字符串式数字片段比较。切换德语与瑞典语,可以观察ä相对z的语言排序差异。实际使用的语言与选项显示在resolvedOptions中,不必靠浏览器界面语言猜测。
代码只过滤完全空行,没有trim文件名。真实文件名可能合法包含首尾空格,排序工具不应悄悄把它们改掉。产品若明确禁止这些名字,应在输入规则中拒绝,而不是在比较器里顺便清洗。
比较结果只看正负,不要要求恰好等于 1
Collator的比较结果可能是负数、零或正数,合同不要求固定返回-1与1。if (compare(a,b) === -1) 这类判断把实现细节当成规则,会在本来合法的比较结果上走错分支。
示例直接把compare结果交给sort,相等时再按原始index排序。现代稳定排序本身已经能保留相等项次序,这里显式写出次级键,是为了把“相同权重按输入顺序”变成可见合同。
但输入顺序只对当前这份列表稳定。服务端每次返回顺序都不同,重新输入以后相等项目当然可能换位置。若要求跨次请求的确定顺序,应添加稳定业务编号作为次级键,而不是继续增加语言选项,期待语言排序替你生成唯一性。
比较器也不应修改记录本身。排序期间多个对象可能被反复比较,里面写计数、改名称或发请求,会让结果依赖调用次数。Collator可以复用,比较过程应只根据固定输入与固定配置给出顺序。
numeric 比较数字片段,不定义业务数字语法
file2 与 file02 可能在numeric规则下获得相同排序权重,因此程序用次级键处理。数字里的前导零是否代表不同业务编码,仍是业务决定。
负号、小数点、千分位和单位,也不能因此被当成一个完整数值解析。给“温度-2”“温度10”排序,不能仅凭numeric就宣称支持数学上的带符号比较;给“2 MB”“100 KB”排序,更没有发生单位换算。
版本号也是一样。数字片段排序有时会让v1.2排在v1.10前,但语义化版本还包括预发布标识、构建元数据等正式规则。需要SemVer比较时使用对应协议,不要拿一个碰巧正确的文件名样本替代版本规范。
sensitivity控制哪些语言差异参与比较。例子选择accent时忽略大小写但保留重音,variant则保留更细差异。若选择base,可能同时折叠大小写与重音权重,不能把它标成只忽略大小写。
语言回退与数据版本会影响结果
请求某个locale,不代表运行时一定具备完全相同的语言数据。可以先用supportedLocalesOf核对支持情况,再读取resolvedOptions确认最终使用的配置。构造器接受了输入,只说明它能建立比较器,不说明产品要求的所有语言细节都已经被满足。
不同浏览器、操作系统或运行时的国际化数据版本也可能造成细节差异。因此,跨系统导出要求完全可重复的业务排序时,应固定执行环境和比较合同,或者由单一后端生成稳定排序键,不能让每台客户端各自“差不多按本地语言排”。
分页尤其需要这条纪律。服务器按一种顺序切了第一页,浏览器再换另一种语言排序这一页,只能改变本页显示,不能得到全量数据在新顺序下的真正第一页。跨页去重与游标继续读取也会受影响。全局排序应在分页之前统一执行,局部排序要在界面里明确范围。
这份页面没有做性能排名。它只在一次排序开始时创建一个Collator并复用compare,避免在每次比较中反复构造配置。是否值得进一步优化,应基于实际列表规模与渲染成本,而不是凭API名字声称快多少倍。
大小限制与比较规则同样应分开:本页限制行数和文本长度,只为让交互实验可控,不参与文件名本身的语言权重。
加入一对只在大小写上不同的名字,勾选忽略大小写,它们应按输入顺序出现;交换输入顺序,显示顺序也随之交换。这就是当前次级键的具体含义。要跨请求保持相同次序,应把index换成不随返回顺序变化的业务标识。