strcasecmp 算法有缺陷吗?

Hal*_*rys 34 c strcmp

我正在尝试strcasecmp用 C重新实现该函数,但我注意到比较过程中似乎存在不一致之处。

man strcmp

strcmp() 函数比较两个字符串 s1 和 s2。不考虑区域设置(有关区域设置的比较,请参阅 strcoll(3))。如果发现 s1 分别小于、匹配或大于 s2,则它返回一个小于、等于或大于零的整数。

man strcasecmp

strcasecmp() 函数对字符串 s1 和 s2 执行逐字节比较,忽略字符的大小写。如果发现 s1 分别小于、匹配或大于 s2,则它返回一个小于、等于或大于零的整数。

int strcmp(const char *s1, const char *s2);
int strcasecmp(const char *s1, const char *s2);

鉴于此信息,我不明白以下代码的结果:

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

int main()
{
    // ASCII values
    // 'A' = 65
    // '_' = 95
    // 'a' = 97

    printf("%i\n", strcmp("A", "_"));
    printf("%i\n", strcmp("a", "_"));
    printf("%i\n", strcasecmp("A", "_"));
    printf("%i\n", strcasecmp("a", "_"));
    return 0;
}

Run Code Online (Sandbox Code Playgroud)

输出:

-1  # "A" is less than "_"
1   # "a" is more than "_"
2   # "A" is more than "_" with strcasecmp ???
2   # "a" is more than "_" with strcasecmp
Run Code Online (Sandbox Code Playgroud)

看来,如果当前输入的字符s1是字母,不管当前输入的字符是否是字母,它总是被转换为小写s2

有人可以解释这种行为吗?第一行和第三行不应该相同吗?

先感谢您!

PS:
gcc 9.2.0在 Manjaro 上使用。
另外,当我用-fno-builtin标志编译时,我得到:

-30
2
2
2
Run Code Online (Sandbox Code Playgroud)

我猜是因为该程序没有使用gcc的优化功能,但问题仍然存在。

And*_*nle 30

行为是正确的。

根据POSIXstr\[n\]casecmp()规范

LC_CTYPE使用的语言环境类别来自 POSIX 语言环境时,这些函数的行为就像字符串已被转换为小写,然后执行字节比较。否则,结果是不确定的。

这也是Linux 手册页NOTES部分的一部分:

POSIX.1-2008 标准描述了这些函数:

当使用的语言环境的 LC_CTYPE 类别来自 POSIX 语言环境时,这些函数的行为就像字符串已转换为小写,然后执行字节比较。否则,结果是不确定的。

为什么?

正如@HansOlsson 在他的回答中指出的那样,仅在字母之间进行不区分大小写的比较,并允许所有其他比较具有它们的“自然”结果,这strcmp()会破坏排序。

如果'A' == 'a'(不区分大小写的比较的定义)那么'_' > 'A''_' < 'a'(ASCII 字符集中的“自然”结果)不能都为真。


Han*_*son 21

其他链接,http: //man7.org/linux/man-pages/man3/strcasecmp.3p.html 的 strcasecmp 说转换为小写是正确的行为(至少在 POSIX 语言环境中)。

这种行为的原因是,如果您使用 strcasecmp 对字符串数组进行排序,则需要获得合理的结果。

否则,如果您尝试使用例如 qsort 对“A”、“C”、“_”、“b”进行排序,结果将取决于比较的顺序。

  • 更具体地说,您需要一个 [total order](https://en.wikipedia.org/wiki/Total_order) 进行排序,如果您按照问题中的方式定义比较,则情况不会如此(因为它不会及物)。 (6认同)
  • *否则,如果您尝试使用 qsort 对“A”、“C”、“_”、“b”进行排序,结果将取决于比较的顺序。* 好点。这可能就是 POSIX 指定该行为的原因。 (3认同)

Adr*_*ica 8

看起来,如果 s1 中的当前字符是一个字母,它总是被转换为小写,而不管 s2 中的当前字符是否是一个字母。

这是正确的 - 这就是strcasecmp()函数应该做的!它是一个POSIX函数,而不是C标准的一部分,而是来自“ The Open Group Base Specifications, Issue 6 ”:

在 POSIX 语言环境中, strcasecmp() 和 strncasecmp() 的行为就像将字符串转换为小写然后执行字节比较一样。结果在其他语言环境中未指定。

顺便说一句,此行为也与_stricmp()函数有关(如 Visual Studio/MSCV 中使用的):

_stricmp 函数在将每个字符转换为小写后依次比较 string1 和 string2,并返回一个表示它们关系的值。