在 C 中排序时无法让 strcoll() 使用语言环境

Can't get strcoll() to use locales when sorting in C

提问人:Thomas Hedden 提问时间:12/28/2022 更新时间:12/29/2022 访问量:131

问:

我无法让依赖于语言环境的函数(例如 strcoll())在 C 中工作。 我想知道我是否做错了什么和/或如何让它工作。 以下是本书中的一个示例程序: 普林茨、彼得和托尼·克劳福德。2016. 简而言之,第 2 版,第 574 页。北京-波士顿-法纳姆-塞瓦斯托波尔-东京:O'Reilly。国际标准书号-13:978-1-491-90475-6。

#include <stdio.h>
#include <string.h>
#include <locale.h>

int main(void) {
   char *samples[ ] = { "curso", "churro" };
   setlocale(LC_COLLATE, "es_ES.UTF-8");
   int result = strcoll(samples[0], samples[1]);
   if(result == 0) {
      printf("The strings \"%s\" and \"%s\" are "
             "alphabetically equivalent.\n",
             samples[0], samples[1]);
   } else if(result < 0) {
      printf("The string \"%s\" comes before \"%s\" "
             "alphabetically.\n",
             samples[0], samples[1]);
   } else if(result > 0) {
      printf("The string \"%s\" comes after \"%s\" "
             "alphabetically.\n",
             samples[0], samples[1]);
   }
   return(0);
}

书中说,“curso”应该放在“churro”之前,因为在西班牙语中,“ch”被认为是一个单独的字母,以便按字母顺序排列。但是,当我运行这个程序时,它会打印“curso”在“churro”之后。我不懂西班牙语,但我用我所知道的其他几种语言测试了这个程序,结果总是 strcmp(),这是一个严格的数字比较。

$ gcc --version
gcc (Ubuntu 11.3.0-1ubuntu1~22.04) 11.3.0
$ locale -a | grep es_ES.utf8             
es_ES.utf8

我知道这个问题: 让语言环境函数在 glibc 中工作 作者说,像 strcoll 这样的语言环境相关函数在 glibc 中表现不佳,他正在编写自己的修改。

我错过了什么吗?这根本行不通吗?

C 区域设置 排序规则

评论

1赞 tadman 12/28/2022
这在不知道 UTF-8 是什么的 C 中是否有效?
0赞 Thomas Hedden 12/29/2022
这是个好问题。书中说:“语言环境信息类别的值LC_COLLATE决定了适用的规则集,可以通过 setlocale() 函数进行更改。该示例使用 UTF-8 语言环境,并且我用 C 编写了许多可以处理 UTF-8 的程序,因此我假设 strcoll() 的编写方式也可以做到这一点。
1赞 Marco 12/29/2022
这段代码在 macOS 上按预期工作,但在 Ubuntu 上则不然。意思是在我的 mac 上它说,但在 Ubuntu 上它说.我确保在两个系统上都安装了。The string "curso" comes before "churro" alphabetically.The string "curso" comes after "churro" alphabetically.es_ES.UTF-8
1赞 n. m. could be an AI 12/29/2022
您的图书包含过时的信息。自 1994 年以来,二合字不被视为单个字母。请参见 rae.es/dpd/abecedario。“1994年,西班牙人协会全体会议,1994年,通过对拉丁裔的普遍性,不考虑独立性。”ch
1赞 n. m. could be an AI 12/29/2022
西班牙语没有问题。程序输出预期的内容。如果您发现其他语言有问题,请显示出来。

答:

1赞 ryyker 12/29/2022 #1

“我错过了什么吗?这根本行不通吗?

我认为这归结为您的环境是否识别“es_ES。UTF-8 英寸

请注意,我无法访问 Linux 环境,这削弱了将苹果与苹果进行比较的能力。但我希望以下内容能突出一些可能有帮助的事情......

在Windows上,使用标准的LabWindows/CVI编译器(我的版本基于Clang 3.3),它输出以下内容:

“字符串”curso“按字母顺序排在”churro“之后。”

根据您在使用西班牙语字母化规则时声明的期望,这似乎是不正确的。

我怀疑库的实现和版本有助于我们所看到的。

请注意,后来我检查了以下各项的返回:setlocale

char *new = setlocale(LC_COLLATE, "es_ES.UTF-8");

它返回 NULL,指示以下内容:

“如果 locale 为非 NULL 且可以遵循,则返回指向与指定类别关联的字符串的指针。如果“全部” 选择类别设置,则字符串包含 不同类别的区域设置串联。如果 选择不能被接受,该函数返回一个 NULL 指针和 程序的区域设置保持不变

表示“es_ES.UTF-8“未被遵守,使语言环境保持不变。
本文对在 C 语言中使用 UTF-8 有一些有趣且相关的见解。以及它与此处看到的区域设置问题的关系。

评论

0赞 n. m. could be an AI 12/29/2022
Windows 曾经(也许现在仍然有)一组与 Linux 截然不同的语言环境。 是一个标准区域设置,应该存在于任何开箱即用的 Ubuntu 安装中。在 Windows 上并非如此,"es_ES.UTF-8"
1赞 n. m. could be an AI 12/29/2022
godbolt 上的 MSVC 具有语言环境,它说可以在某种 Windows 服务器上运行。他们安装的 gcc 没有,但他们可能不会运行开箱即用的桌面 Linux 操作系统。
0赞 Thomas Hedden 12/29/2022
@ryyker:谢谢你的提示,但我确实检查过了。我添加了使用 # locale-gen <locale name> 测试的所有语言环境。在这种情况下,这不是问题所在。
5赞 n. m. could be an AI 12/29/2022 #2

您的图书包含过时的信息。自 1994 年以来,西班牙语双字母不再被视为单个字母。请参见 https://rae.es/dpd/abecedarioch

1994年,西班牙人协会全体会议,1994年,通过对拉丁裔的普遍性、对独立性进行考虑。

(希望不需要翻译)

您还可以在此处查看 Unicode 排序规则数据。这是 glibc 从中获取其排序规则数据的来源。如您所见,有几个排序规则顺序。标准的不考虑和特殊,而传统的则考虑。Glibc 实现了标准排序规则。chll

您可以通过尝试使用带重音字符的字符串来检查西班牙语区域设置排序规则是否正常工作。如果系统正常工作,则这些字母应按排序规则顺序描述的顺序出现(即紧跟在相应的非重音字符之后),如果系统无法正常工作,则应按所有非重音字母(即,如果您忘记调用或不支持区域设置)。演示请注意,在 godbolt 上,GCC 不支持语言环境,而 MSVC 支持(并使用类似 Unix 的语言环境名称启动)。setlocale

如果要测试多字符排序规则,请使用捷克语区域设置 (),它确实可以识别为单个字母,并且在排序规则顺序中位于 h 之后演示cs_CZ.UTF-8ch

评论

0赞 ryyker 12/29/2022
我需要把它翻译:):“在1994年举行的第十届西班牙语学院协会大会上,同意采用通用的拉丁字母顺序,其中ch和ll不被视为独立的字母。
0赞 Thomas Hedden 12/29/2022
你好,@n.m.我认为您已经确定了问题所在,即有多个可能的排序顺序。使排序规则问题非常令人困惑的另一件事是,可以有不同的排序规则级别。我不会在这里解释这一点,但这个网页解释了它:docs.oracle.com/database/121/NLSPG/ch5lingsort.htm#NLSPG271 也就是说,即使一个字符“在”另一个字符之后,当考虑下一个字符时,它们也可能在一定的排序级别上被认为是相同的。这使得对法语进行排序非常困难。谢谢你和所有发帖的人!
0赞 n. m. could be an AI 12/29/2022
@ThomasHedden 不同级别的排序规则是实现排序规则的人员所关心的问题。对于最终用户来说,它们通常不是问题。