strcasecmp算法有缺陷吗?


34

我试图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;
}

输出:

-1  # "A" is less than "_"
1   # "a" is more than "_"
2   # "A" is more than "_" with strcasecmp ???
2   # "a" is more than "_" with strcasecmp

看起来,如果in中的当前字符s1是字母,则无论in中当前的字符是否是字母,它总是会转换为小写s2字母。

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

先感谢您!

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

-30
2
2
2

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


2
添加另一个测试用例您设定:printf("%i\n", strcasecmp("a", "_"));这应该大概有相同的结果,printf("%i\n", strcasecmp("A", "_"));但是这意味着,一个两个不区分大小写的通话会与它的区分大小写对方不同意。
anton.burger

strcasecmp您所指的描述似乎不正确。建议的答案中有更多详细信息。
Jabberwocky

9
这是唯一有意义的事情。一个说A < _ && a > _ && A == a这样的函数会引起很多问题。
ikegami

另外:“我正在尝试在C中重新实现strcasecmp函数”->尽管未显示代码,但请务必将“好像”进行比较unsigned char。C17 / 18“字符串处理<string.h>”->“对于本节中的所有功能,每个字符应被解释为具有unsigned char“” 类型。一旦char值超出ASCII范围0-127,这将有所不同。
chux-恢复莫妮卡

1
关于带内置和不带内置输出的差异:两者都说相同,因为它们的结果分别为<0和> 0,并且您没有== 0的示例。但是,您可以看到算法大放异彩:某些返回值是第一个非等号字符的差异。
在busybee

Answers:


31

行为是正确的。

根据POSIX str\[n\]casecmp()规范

LC_CTYPE使用的语言环境的类别来自POSIX语言环境时,这些功能的行为应类似于将字符串转换为小写字母,然后执行字节比较。否则,结果不确定。

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

POSIX.1-2008标准说明了以下功能:

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

为什么?

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

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


仅在字母之间进行不区分大小写的比较不会导致'_' > 'A' && '_' < 'a'; 似乎不是最好的例子。
带翅膀的小行星

1
@AsteroidsWithWings这些是问题中使用的字符。而如果'a' == 'A' 按照定义,如果你做的“自然”值之间的比较'a''A'以及'_”,你不能做之间的区分大小写的比较'A''a'获得平等和获得一致的排序结果。
安德鲁·亨利

我对此没有争议,但是您提供的具体反例似乎无关紧要。
带翅膀的小行星

@AsteroidsWithWings完成了从'a''A'和构建一个二叉树的脑力劳动'_',将所有6个插入顺序插入树中,并将指定的“总是小写字母”的结果与问题的建议“仅转换字母”进行比较当是字母之间的比较时”。例如,使用后一种算法并以开头'_''a''A'在树的相对两侧缠绕,但它们被定义为相等。“仅在字母-字母比较中将字母转换为小写字母”算法被破坏了,这三个字符表明了这一点。
安德鲁·亨利

好的,那么我建议在答案中演示这一点,因为此刻它只是指向指出'_' > 'A' 并且'_' < 'a'不可能两者都正确”,而没有告诉我们为什么我们应该以为如此。(这是答案者的任务,而不是数百万读者的任务。)
带翅膀的小行星

21

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

出现这种情况的原因是,如果使用strcasecmp对字符串数组进行排序,则需要获取合理的结果。

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


3
否则,如果您尝试使用例如qsort对“ A”,“ C”,“ _”,“ b”进行排序,则结果将取决于比较的顺序。 好点子。这可能是POSIX指定行为的原因。
安德鲁·亨利

6
更具体地说,您需要一个排序顺序,如果您按照问题中的定义定义比较,则不会这样(因为它不会传递)。
Dukeling

8

看起来,如果s1中的当前字符是字母,则无论s2中的当前字符是否是字母,它总是会转换为小写字母。

没错- strcasecmp()函数应该执行的操作!它是POSIX功能而不是C标准的一部分,但是来自“ 开放团体基础规范,第6期 ”:

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

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

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


2

对于ASCII十进制码A65_95a97,所以strcmp()它做什么,这是该做的。从词典上来讲_,小于a和大于A

strcasecmp()A被视为a*,并且由于a大于_输出也是正确的。

* POSIX.1-2008标准说明了以下函数(strcasecmp()和strncasecmp()):

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

来源:http//man7.org/linux/man-pages/man3/strcasecmp.3.html


3
OP的观点是,与不区分大小写的比较相比,A它“更大” _,并且想知道为什么结果与区分大小写的比较时的结果不同。
anton.burger

6
语句Since strcasecmp()不区分大小写,它将A视为a是无效的推论。不区分大小写的例程可以将所有大写字母都视为小写字母,可以将所有小写字母都视为大写字母,或者可以将每个大写字母视为与其对应的小写字母相等,反之亦然,但仍将它们进行比较非字母字符及其原始值。这个答案没有说明偏爱任何一种可能性的原因(正确的理由是文档说使用小写字母)。
埃里克·Postpischil

@EricPostpischil POSIX.1-2008标准对这些函数(strcasecmp()和strncasecmp())进行了说明:当所使用的语言环境的LC_CTYPE类别来自POSIX语言环境时,这些函数的行为应类似于将字符串转换为小写,然后执行字节比较。否则,结果不确定。
anastaciu
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.