如何在Javascript中比较Unicode字符串?

45

当我在JavaScript中写下"Ł" > "Z",它返回true。按Unicode顺序应该是false。如何修复?我的网站正在使用UTF-8。


1
你想要做什么?也许有一些变通的方法。 - Pekka
1
我正在尝试根据用户名对表进行排序,但是我有像“Ł”这样的字母。 - Tomasz Wysocki
1
换句话说,它必须紧随在 L 之后吗?也就是说,..J,K,L,Ł,M,N,O.. - BalusC
7
你要找的术语是“整理顺序”(collation),它非常难处理。没有所谓的“Unicode顺序”;Unicode明确承认不同语境具有不同的排序方式。请参见http://unicode.org/reports/tr10/ - “不提供以下功能:……语言适用性”。 - MSalters
2
我已将问题标题中的“UTF-8”更改为“Unicode”,因为该问题不取决于特定的传输编码。(此外,即使HTML文档的编码为UTF-8,JavaScript内部也使用UTF-16。) - Jukka K. Korpela
显示剩余3条评论
6个回答

40

如果你需要对一个多语言单词列表进行排序,例如来自不同国家的人名列表,那么这种方法行不通,对吧? - Mic

20

这里有一个法语字母表的例子,可以帮助您进行自定义排序:

var alpha = function(alphabet, dir, caseSensitive){
  return function(a, b){
    var pos = 0,
      min = Math.min(a.length, b.length);
    dir = dir || 1;
    caseSensitive = caseSensitive || false;
    if(!caseSensitive){
      a = a.toLowerCase();
      b = b.toLowerCase();
    }
    while(a.charAt(pos) === b.charAt(pos) && pos < min){ pos++; }
    return alphabet.indexOf(a.charAt(pos)) > alphabet.indexOf(b.charAt(pos)) ?
      dir:-dir;
  };
};

要在字符串数组 a 上使用它:

a.sort(
  alpha('ABCDEFGHIJKLMNOPQRSTUVWXYZaàâäbcçdeéèêëfghiïîjklmnñoôöpqrstuûüvwxyÿz')
);

alpha()的第二个参数中添加1-1以进行升序或降序排序。
在第三个参数中添加true以进行区分大小写的排序。

你可能需要将数字和特殊字符添加到字母表中。


如果您正在使用此代码,请参见:https://dev59.com/9HA65IYBdhLWcg3wyh17#3633725 - Tomasz Wysocki
哎呀!你真的要经历所有这些吗?如果忘记先将其放入规范化形式D中怎么办?PHP真的没有类似于Perl的Unicode::CollateUnicode::Collate::Locale模块的等价物吗?真的吗?尝试自己重新实现所有这些看起来像是彻头彻尾的疯狂! - tchrist
2
@tchrist,这里不是PHP,而是JavaScript,就像它一样。 - Mic

14

你可以使用localeCompare()来构建自己的排序函数,根据MDC文章的说法,它应该能够正确地排序。

如果这种方法不起作用,这里有一个有趣的SO问题,其中OP使用字符串替换来构建“暴力”排序机制。

在那个问题中,OP还展示了如何为jQuery tablesorter插件构建自定义的textExtract函数,用于区域设置感知排序-也值得一看。

编辑:作为一个非常奇特的想法 - 我完全不知道这是否可行,特别是由于性能的考虑 - 如果你已经在后端使用PHP/mySQL,我想提到发送一个Ajax查询到mySQL实例进行排序的可能性。 mySQL非常擅长排序区域设置感知数据,因为您可以使用例如ORDER BY xyz COLLATE utf8_polish_ciCOLLATE utf8_german_ci ...的命令将排序操作强制为特定的排序规则。这些排序规则会同时解决所有排序问题。


1
谢谢提供链接。虽然JavaScript在核心中不支持它,但仍然是一个可行的解决方案,这有点遗憾。 - Tomasz Wysocki
2
在IE6中要小心使用localeCompare():http://blog.schmichael.com/2008/07/14/javascript-collation-fail/ - BalusC
1
@BalusC,该文章中的评论声称这实际上是Wine的问题,而不是IE6的问题。找不到其他关于此问题的信息来确认或否定它,而且我现在太懒了,不想构建一个测试用例... @Tomasz如果您选择这条路线,有趣的是听听在IE6中是否运行良好。 - Pekka
1
哦,我之前没有看到评论。总之,为了避免未预料的浏览器不一致(我仍然对localeCompare()没有很好的感觉),我会像Tomalak在你提供的链接中所做的那样实现自定义的函数。 - BalusC
1
@BalusC 我同意这可能是最好和最可靠的方法。 - Pekka
从“硬编码”的方法中,我最喜欢Mic的方法。它比替换和比较的方法更明确。虽然localeCompare很好用,但不适用于所有浏览器/配置(例如,在我的Google Chrome上无法使用,但在Fifrefox(同一台计算机)上可以正常工作)。 - Tomasz Wysocki

10

对于未提及的字符,Mic的代码进行了改进:

var alpha = function(alphabet, dir, caseSensitive){
  dir = dir || 1;
  function compareLetters(a, b) {
    var ia = alphabet.indexOf(a);
    var ib = alphabet.indexOf(b);
    if(ia === -1 || ib === -1) {
      if(ib !== -1)
        return a > 'a';
      if(ia !== -1)
        return 'a' > b;
      return a > b;
    }
    return ia > ib;
  }
  return function(a, b){
    var pos = 0;
    var min = Math.min(a.length, b.length);
    caseSensitive = caseSensitive || false;
    if(!caseSensitive){
      a = a.toLowerCase();
      b = b.toLowerCase();
    }
    while(a.charAt(pos) === b.charAt(pos) && pos < min){ pos++; }
    return compareLetters(a.charAt(pos), b.charAt(pos)) ? dir:-dir;
  };
};

function assert(bCondition, sErrorMessage) {
      if (!bCondition) {
          throw new Error(sErrorMessage);
      }
}

assert(alpha("bac")("a", "b") === 1, "b is first than a");
assert(alpha("abc")("ac", "a") === 1, "shorter string is first than longer string");
assert(alpha("abc")("1abc", "0abc") === 1, "non-mentioned chars are compared as normal");
assert(alpha("abc")("0abc", "1abc") === -1, "non-mentioned chars are compared as normal [2]");
assert(alpha("abc")("0abc", "bbc") === -1, "non-mentioned chars are compared with mentioned chars in special way");
assert(alpha("abc")("zabc", "abc") === 1, "non-mentioned chars are compared with mentioned chars in special way [2]");

0

你需要保留两个排序关键字符串。一个是用于主排序的,其中德语中的ä=a(主排序 a->a),法语中的é=e(主排序 e->e),而另一个是用于次要排序的,例如ä在a之后(将a转换为azzzz作为次要关键字)或者é在e之后(次要关键字e->ezzzz)。特别是在捷克语中,一些字母是字母的变体(如áéí…),而其他字母则在列表中占有其完整的权利(ABCČD…GHChI…RŘSŠT…)。此外,还需要考虑将二合字视为单个字母(例如主排序ch->hzzzz)。这不是一个简单的问题,但应该可以在JS中找到解决方案。


1
通过使用默认格式选项来改进您的答案 - Harsh Patel
解决方案是:myArray.sort(new Intl.Collator('pl').compare); - xandru

-1
有趣的是,我在思考这个问题并在这里搜索时,突然想到可以使用自己的JavaScript模块。我编写了一个生成干净URL的模块,因此我必须将输入字符串转换为拼音... (http://pid.github.io/speakingurl/)
var mySlug = require('speakingurl').createSlug({
    maintainCase: true,
    separator: " "
});

var input = "Schöner Titel läßt grüßen!? Bel été !";
var result;

slug = mySlug(input);
console.log(result); // Output: "Schoener Titel laesst gruessen bel ete"

现在您可以使用这个结果进行排序。例如,您可以将原始标题存储在“title”字段中,并将排序字段存储在“title_sort”中,使用mySlug的结果。

2
这几乎是一个好的解决方案。问题在于“ä”会与“a”混合在一起,但应该分开。 - Tomasz Wysocki

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接