【问题标题】:Is the strcasecmp algorithm flawed?strcasecmp 算法有缺陷吗?
【发布时间】:2020-06-06 02:34:37
【问题描述】:

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

来自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

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

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

提前谢谢你!

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

-30
2
2
2

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

【问题讨论】:

  • 向您的集合添加另一个测试用例:printf("%i\n", strcasecmp("a", "_")); 这应该与printf("%i\n", strcasecmp("A", "_")); 具有相同的结果,但这意味着这两个不区分大小写的调用中的 一个 是将不同意其区分大小写的对应项。
  • 您所指的strcasecmp的描述似乎不准确。更多详细信息请参阅投票的答案。
  • 这是唯一有意义的事情。一个写着A &lt; _ &amp;&amp; a &gt; _ &amp;&amp; A == a 的函数会导致很多问题。
  • 旁白:“我正在尝试用 C 重新实现 strcasecmp 函数” --> 虽然代码没有显示,但请务必比较“as if”unsigned char。 C17/18 "字符串处理 " --> "对于本子条款中的所有函数,每个字符都应被解释为其类型为 unsigned char"。一旦 char 值超出 ASCII 范围 0-127,这就会产生影响。
  • 关于内置和不内置输出的差异:两者都说相同,因为它们的结果相同 0,并且您没有 ==0 的示例。但是您可以看到算法大放异彩:一些返回值是第一个不相等字符的差异。

标签: c strcmp


【解决方案1】:

行为正确。

the POSIX str\[n\]casecmp() specification:

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

这也是the NOTES section of the Linux man page的一部分:

POSIX.1-2008 标准规定了这些功能:

当使用的语言环境的 LC_CTYPE 类别来自 POSIX 语言环境,这些函数的行为就像 字符串已被转换为小写,然后是一个字节 进行了比较。否则,结果未指定。

为什么?

As @HansOlsson pointed out in his answer,仅在字母之间进行不区分大小写的比较,并允许所有其他比较具有它们在 strcmp() 中所做的“自然”结果,这会破坏排序。

如果'A' == 'a'(不区分大小写比较的定义),则'_' &gt; 'A''_' &lt; 'a'(ASCII 字符集中的“自然”结果)不能同时为真。

【讨论】:

  • 只在字母之间进行不区分大小写的比较不会产生'_' &gt; 'A' &amp;&amp; '_' &lt; 'a';似乎不是最好的例子。
  • @AsteroidsWithWings 这些是问题中使用的字符。如果'a' == 'A'根据定义,如果您在'a''A''_' 的“自然”值之间进行比较,您不能'A''a' 之间进行不区分大小写的比较,以获得相等并获得一致的排序结果。
  • 我对此没有异议,但您提供的具体反例似乎并不相关。
  • @AsteroidsWithWings 完成从'a''A''_' 构建二叉树的心理练习,遍历树中的所有 6 个插入顺序,并比较以下结果问题提出的“仅在字母到字母比较时转换字母”的指定“始终小写字母”。例如,使用后一种算法并从'_''a''A' 开始在树的相对侧结束,但它们被定义为相等。 “仅在字母-字母比较中将字母转换为小写”算法已损坏,而这 3 个字符表明了这一点。
  • 好的,那么我建议在答案中证明这一点,因为目前它只是跳到指出 "'_' &gt; 'A' '_' &lt; 'a' 不能同时为真"没有告诉我们为什么我们应该想到会这样。 (这是回答者的任务,而不是数百万读者中的一个。)
【解决方案2】:

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

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

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

【讨论】:

  • 否则,如果您尝试使用 qsort 对“A”、“C”、“_”、“b”进行排序,结果将取决于比较的顺序。好点子。这可能是 POSIX 指定行为的原因。
  • 更具体地说,您需要一个 total order 进行排序,如果您在问题中定义比较,则情况并非如此(因为它不会传递)。
【解决方案3】:

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

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

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

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

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

【讨论】:

    【解决方案4】:

    A 的 ASCII 十进制代码是 65_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

    【讨论】:

    • OP的观点是A在不区分大小写比较时比_“大”,并且想知道为什么结果与区分大小写比较时不一样。
    • 语句Since strcasecmp()`不区分大小写,它会将A视为a`是一个无效的推导。不区分大小写的例程可以将所有大写字母视为小写字母,可以将所有小写字母视为大写字母,或者可以将每个大写字母视为与其对应的小写字母相同,反之亦然,但仍将它们进行比较到具有原始值的非字母字符。这个答案没有说明首选任何这些可能性的原因(正确的原因是文档说使用小写字母)。
    • @EricPostpischil POSIX.1-2008 标准说明了这些函数(strcasecmp() 和 strncasecmp()):当使用的语言环境的 LC_CTYPE 类别来自 POSIX 语言环境时,这些函数的行为好像字符串已被转换为小写,然后执行字节比较。否则,结果未指定。
    猜你喜欢
    • 2011-01-16
    • 1970-01-01
    • 2012-12-30
    • 1970-01-01
    • 1970-01-01
    • 2021-05-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多