strcasecmp():一种非标准函数?

14

前几天我在CodeReview上发布了一篇帖子。其中一个回答我的问题的人建议我不要使用strcasecmp(),因为“该函数是非标准的[而且]这会使[我的]代码不可移植。” 这是我如何使用它:

int playGame()
{

    char scanned[3];
    printf("Do you wish to play tick-tack-toe?\n");
    scanf("%s", scanned);
    if(strcasecmp(scanned,"yes")==0)
        startGame();

    else
    {
        if (strcasecmp(scanned,"no")==0 || strcasecmp(scanned,"nah")==0 || strcasecmp(scanned,"naw")==0)
        {
            printf("That's too bad!/nThis program will now end.");
            return 1;
        }
        printf("Not valid input!/nThis program will now end.");
        return 1;
    }
return 0;
}

有人能更深入地解释一下strcasecmp()为什么有这些限制吗?


3
为什么不直接查看标准 - too honest for this site
3
哪个标准是“那个”标准呢?标准的美妙之处在于有很多种!事实上,strcasecmp函数在BSD / POSIX的libc标准库中定义。 - Dan Korn
就我个人而言,我只会检查第一个字符是否为“N”或“n”,然后结束,而不是试图考虑特定的俚语实例。但在这里,我们正在接近计算机尝试解析人类生成的文本输入的更大问题。 - Dan Korn
还有,“Tic-Tac-Toe”中没有“K”。;^) - Dan Korn
2
@Dan-korn,你真是太有知识了 :P - SuperGoA
1
当你有疑问时,可以查看man 3 strcasecmp:"符合4.4BSD、POSIX.1-2001标准。" - David C. Rankin
4个回答

7

strcasecmp并不是C或C++标准中的函数。它是由POSIX.1-2001和4.4BSD定义的。

如果您的系统符合POSIX或BSD标准,则不会存在问题。否则,该函数将无法使用。


2
这是一个完全错误的解释。请仔细阅读:“以str开头,后跟小写字母的名称由C标准保留”。这意味着如果您编写了一个名为strcasewhatever的函数,那么您已经离开了C标准。strcmp是C标准的一部分。 - gnasher729
2
@SuperGoA:我的意思就是这样。C标准定义了strcmp()作为其函数之一,并保留了其他以str和小写字母开头的名称。POSIX使用strcasecmp()占用了该保留名称空间,但仍然可以使用。如果您创建一个名为strcasecmp()的函数,则不能保证能够使用,但strCaseCmp()str_casecmp()str9cmp()都可以(对于这三个名称中的任何一个,str后面都没有小写字母)。当然,如果系统不像应该的那样有纪律性,您仍可能遇到问题。 - Jonathan Leffler
3
请注意,strcasecmp() 在1997年被单一Unix规范v2定义。我不确定它当时是否在POSIX中,但是strcasecmp()的历史比2001年的参考资料所暗示的要更久远。 - Jonathan Leffler
2
这与平台关系不大,而与C编译器及其库有关。您可以在Windows上安装MinGW并调用strcasecmp,就像在Unix或OS X上一样,即使在使用Visual Studio时它也无法工作。或者您可以根据需要#define strcasecmp stricmp或反之亦然,以适应工具集。 - Dan Korn
2
可能需要担心的是,函数的运行时实现如何确定哪些大写字母和小写字母在进行不区分大小写比较时是等效的?你可能会发现,对于英语字符串,一切都很正常,但如果你给它带有重音或其他语言(如德语、法语、西班牙语)的变音符号的字符串,则其是否有效取决于实现方式或“C”语言环境。然后还有像日语和中文这样的宽字符语言;那就是另一个故事了。欢迎来到巴别塔! - Dan Korn
显示剩余6条评论

6
简短回答:由于strcasecmp()不在C标准库中,因此它是非标准的。 strcasecmp()在流行的标准中定义,例如4.4BSD、POSIX.1-2001。
无大小写函数的定义打开了挑剔细节的大门。这些细节通常涉及无大小写比较的正面或负面结果,而不仅仅是OP使用的0或非0。特别是:
在POSIX区域设置中,strcasecmp()和strncasecmp()的行为应该像字符串已经被转换为小写字母然后进行字节比较一样。其他区域设置的结果是未指定的。
这种情况的麻烦在于没有1对1映射的大写和小写字母。考虑一个具有E、e和é但没有É的本地化,但toupper('é') -> 'E'。然后,对于“好像字符串已被转换为小写”,'E'有2个选择。
作为一个可移植解决方案的候选人,考虑一个往返字母(先大写再小写)以应对非1对1映射的解决方案。
int SGA_stricmp(const char *a, const char *b) {
  int ca, cb;
  do {
     ca = * (unsigned char *)a;
     cb = * (unsigned char *)b;
     ca = tolower(toupper(ca));
     cb = tolower(toupper(cb));
     a++;
     b++;
   } while (ca == cb && ca != '\0');
   return ca - cb;
}

如果你不想往返传递数值,请使用以下方法:

     ca = tolower(ca);
     cb = tolower(cb);

详情:仅对在unsigned charEOF范围内的int定义了toupper()tolower()。使用* (unsigned char *)a作为*a可能具有负值。


1
“函数是非标准的”意味着,函数声明和契约未在C国际标准中指定。
“这使得代码不可移植”意味着,实现不需要实现strcasecmp(),因此您的代码不完全符合标准,并且不能保证被严格符合标准的编译器编译。 strcasecmp()本身是POSIX.1-20014.4BSD规范的一部分(link)。

很抱歉,我不理解这句话的意思:"This makes code non-portable means, that implementations aren't required to implement strcasecmp()..." 如果strcasecmp()是非标准的,为什么实现不需要呢?根据第一个答案,这似乎需要一些“特殊”的东西(如POSIX.1-2001或4.4BSD)才能使其工作。 - SuperGoA
1
他的意思是,如果该函数不是C标准的一部分,则符合标准的C编译器不需要实现它。因此,您的代码可能无法在世界上每个“标准”C编译器上编译。 - Dan Korn
1
@SuperGoA 国际C标准以单一文档形式呈现,详细描述了符合标准的C编译器必须/不得/可以/不可/应该/不应该做的一切。严格符合标准的C编译器只会实现标准中的内容。POSIX不是标准的一部分,而是一个广泛接受的扩展(规范)。在大多数Windows系统上,它根本没有被实现。 - набиячлэвэли

-1

另一种选择是使用标准的tolower()将输入规范化为小写。然后您可以使用标准的strcmp()。


1
请注意,当大写/小写字母没有一对一映射时,这可能与strcasecmp()在功能上不同。这取决于strcasecmp()的规范(它不是C标准)。大多数strcasecmp()确实使用此tolower()方法作为默认语言环境。 - chux - Reinstate Monica

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