strcmp()和strcoll()有什么区别?

12

我试图理解它们两个,但我没有发现任何差异,除了strcoll()这个参考说:

根据LC_COLLATE类别定义的当前语言环境比较两个空终止字符串。

经过再次思考,我知道我正在提出另一个需要详细回答的问题,那么在C和C++中,这个locale到底是什么?


10
我认为这个示例非常清晰明了。 - Rapptz
@Rapptz... 哎呀...发帖之前应该看到那个链接... - Recker
2个回答

31

strcmp() 函数逐个比较字符串的字节,不管这些字节是什么。

strcoll() 函数将字节按照所使用的语言环境进行转换,然后再进行比较。转换根据所使用的语言而不同。在法语中,带有重音符号的字母排在没有重音符号的字母之后。因此,ée 之后。但是,é 却在f之前。strcoll() 函数可以正确地排序,而 strcmp() 函数则不能。

然而,在许多情况下,strcmp() 函数已经足够了,因为您不需要按照使用的语言环境对结果进行排序。例如,如果您只需要快速访问由字符串索引的大量数据,则会使用以该字符串索引的映射。对于这些数据进行排序通常是完全无用的,因为相对于 strcoll() 函数来说,strcmp() 函数通常更快。

有关字符的详细信息,您可能还想查看 Unicode网站。

关于语言环境,它就是指语言。默认情况下,它设置为“C”(或多或少没有语言环境)。选择位置后,语言环境会相应地设置。您也可以设置 LC_LOCALE 环境变量。实际上有许多这样的变量。但通常使用预定义函数自动考虑这些变量并为您执行正确的操作(例如格式化日期/时间、格式化数字/度量、计算大写/小写等)。


1
这个回答中的字符比较示例非常清晰明了,特别是“但是,é在f之前”的例子。同样,关于使用strcmp()进行内部排序管理与显示的注释也同样出色。有些答案简洁明了,却能表达深刻的意义,我希望我能给它们点赞多次。这就是其中之一。 - WhozCraig

2
在我测试的所有unicode语言环境中,无论是在几个不同版本的glibc上,strcoll()对于任何两个平假名字符都返回零。这破坏了sort,uniq和所有以某种方式相互作用的字符串顺序。
例如: $ echo -e -n 'い\nろ\nは\nに\nほ\nへ\nと\n' | sort | uniq
结果是无法修复的错误。世界各地的人们可能对“い”应该放在“ろ”之前或之后有不同的想法,但没有理智的人会认为它们是相同的。
而且,将语言环境设置为日本语也没有关系: $ LC_ALL=ja_JP.utf8 LANG=ja_JP.utf8 LC_COLLATE=ja_JP.utf8 echo -e -n 'い\nろ\nは\nに\nほ\nへ\nと\n' | sort | uniq
在一些官方邮件列表中有讨论,但是猜猜看,那是在2002年,因为人们不关心,所以从未修复:https://www.mail-archive.com/linux-utf8@nl.linux.org/msg02658.html 那个bug在某一天也发生在我们身上,最后我们唯一的出路就是将collate locale设置为“C”,并依赖于utf-8编码的良好特性。这是一次可怕的经历,因为处理全日文数据时不应该真正使用“C”locale。
因此,为了您的健康,请不要直接使用strcoll。更安全的变体可能是:
int safe_strcoll(const char *a, const char *b)
{
  int ret = strcoll(a, b);
  if (ret != 0) return ret;
  return strcmp(a, b);
}

“以防万一 strcoll() 函数出了问题...”
“编辑:我出于好奇又重复了这个实验,我的当前系统(使用 glibc 2.29)现在可以正常工作了,而且区域设置也不重要。”

3
我很幸运地发现了这个问题。你只对echo应用了环境变量的更改。你也应该将语言更改应用到sortuniq中;最简单的方法就是使用export LANG(export LANG=ja_JP.UTF-8; echo -e -n 'い\nろ\nは\nに\nほ\nへ\nと\n' | sort | uniq)可以完美地运行。 - qsantos
1
谢谢你指出这一点。但实际上,我在第一次进行这个实验时使用的是export LANG=ja_JP.UTF-8。我在多台机器上尝试了一遍,仍然可以在其中一些机器上重现问题,但不是全部。我想我需要进一步挖掘导致差异的原因。glibc版本似乎不是我需要控制的唯一变量... - Yì Yáng

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