C中不区分大小写的字符串comp


74

我有两个char*要比较的邮政编码,忽略大小写。有功能可以做到这一点吗?

还是我必须遍历每个使用tolower函数,然后进行比较?

任何想法,此函数将如何对字符串中的数字做出反应

谢谢


我想我写的是一种不好的方式,邮政编码不是一种类型,而只是char *所拥有的真实世界的价值。
bond425 2011年

3
您在什么平台上?许多平台具有特定于平台的功能来执行此操作。
2011年

如果您将数字与字母进行比较,那么无论大小写,您都知道字符串并不等效。
亚历克斯·雷诺兹

我假设您只是说ASCII字符串比较?跨多个语言环境在全世界不是通用的吗?
Doug T.

比较可能导致比较一个数字和一个字母,我需要测试两个邮政编码是否相等,一个大于或小于。大于,小于部分令人困惑,我不确定这将如何解决
bond425 2011年

Answers:


66

C标准中没有执行此操作的功能。符合POSIX的Unix系统必须包含strcasecmp在头文件中strings.h; 微软系统有stricmp。为了便于移植,请编写您自己的:

但是请注意,这些解决方案均不能与UTF-8字符串一起使用,而只能与ASCII一起使用。


3
这个实现是不正确的。当b是a的子字符串时,它将错误地返回0。例如,它将为strcicmp(“ another”,“ an”)返回0,但应返回1
RobertoP'5

26
这是个坏建议。没有理由“编写自己的”标准C文本函数来处理简单的名称差异。执行#ifdef _WINDOWS ... #define strcasecmp stricmp ... #endif并将其放在适当的标头中。上面的注释中,作者必须修复该函数才能正常工作,这就是为什么如果有更简单的解决方案可以重写标准C函数的原因。
B. Nadolson

4
_stricmp和strcasecmp在-std = c ++ 11中都不可用。它们在语言环境方面也有不同的语义。
minexew

2
这将打破要命的时候a或者bNULL
YoTengoUnLCD

5
@YoTengoUnLCD Re:当a或b为NULL时,无法正常工作。与null一起打破a和/或b按照NULL惯例(通常被认为是空指针)并不指向字符串。可以添加一个不错的支票,但是返回什么呢?应该cmp("", NULL)返回0,INT_MIN吗?对此尚无共识。注意:C允许UB使用strcmp(NULL, "abc");
chux-恢复莫妮卡

37

看看到strcasecmp()strings.h


4
我认为您的意思int strcasecmp(const char *s1, const char *s2);是strings.h
Brigham,

2
此功能是非标准的;微软称之为stricmp。@entropo:strings.h是与1980年代Unix系统兼容的标头。
Fred Foo

1
@entropo:抱歉,POSIX确实定义了strings.h。它还定义strcasecmp了要在该标头中声明的。但是,ISO C没有它。
Fred Foo

5
请参阅:字符串h和字符串h之间的差异。一些C标准库已将所有不推荐使用的功能合并到中string.h。参见例如Glibc
entropo 2011年

1
@Mihran:这与编译器无关。这是图书馆的问题。
弗雷德·富

7

我发现内置的这样的方法名为from,其中包含标准标头中的其他字符串函数。

相关签名如下:

我还在xnu内核(osfmk / device / subrs.c)中找到了它的同义词,并在以下代码中实现了它,因此与原始strcmp函数相比,您不会期望数字上的行为发生任何变化。


提到安全strncasecmp()功能的荣誉!
Mike C.

strcasecmp()并且strncasecmp()不是标准C库的一部分,而是* nix中的常见补充。
chux-恢复莫妮卡

5

我会用stricmp()。它不区分大小写地比较两个字符串。

请注意,在某些情况下,将字符串转换为小写可能会更快。


4

比较不区分大小写时要注意的其他陷阱:


比较小写还是大写?(足够常见的问题)

以下两个都将通过strcicmpL("A", "a")和返回0 strcicmpU("A", "a")
然而,strcicmpL("A", "_")并且通常在大写和小写字母之间strcicmpU("A", "_")可以返回不同的签名结果'_'

与一起使用时,这会影响排序顺序qsort(..., ..., ..., strcicmp)。非标准库C函数(例如常用的函数) stricmp()strcasecmp()易于定义的函数,倾向于通过小写字母进行比较。但是存在差异。


char可以为负值。(不罕见)

touppper(int)并且tolower(int)unsigned char值和负数指定EOF。此外,strcmp()返回结果就像每个char都转换为一样unsigned char,无论char是带符号的还是无符号的


语言环境(较不常见)

尽管使用ASCII码(0-127)的字符集无处不在,但其余的代码往往会遇到区域特定的问题。因此,strcasecmp("\xE4", "a")可能在一个系统上返回0,而在另一个系统上返回非零。


Unicode(未来之路)

如果解决方案需要处理多个ASCII字符,请考虑使用unicode_strcicmp()。由于C lib不提供此类功能,因此建议使用一些备用库中的预编码功能。编写自己的书 unicode_strcicmp()是一项艰巨的任务。


所有字母都将一低一高映射吗?(学究的)

[AZ]与[az]一对一映射,但各种语言环境将各种小写字母映射到一个大写字母,反之亦然。此外,某些大写字母可能缺少小写字母,反之亦然。

这使代码必须同时隐藏tolower()tolower()

同样,如果代码对tolower(toupper(*a))vs ,则排序时可能会出现不同的结果toupper(tolower(*a))


可移植性

@B。Nadolson建议避免自己滚动strcicmp(),这是合理的,除非代码需要高度等效的可移植功能。

下面是一种方法,其执行速度甚至比某些系统提供的功能还快。它使用2个不同的表对每个循环执行单个比较,而不是两个'\0'。您的结果可能会有所不同。


2

我不太喜欢此处最受欢迎的答案(部分原因是,这似乎是不正确的,因为它应该continue读取两个字符串中的空终止符(但不是一次同时读取两个字符串),并且不这样做),所以我写了自己的。

这是的直接替代品strncmp(),并且已通过许多测试用例进行了测试,如下所示。

strncmp()除以下内容外,其他均相同:

  1. 不区分大小写。
  2. 如果任一字符串为空ptr,则该行为不是未定义的(定义明确)。strncmp()如果其中一个字符串为空ptr,则Regular具有未定义的行为(请参阅:https//en.cppreference.com/w/cpp/string/byte/strncmp)。
  3. INT_MIN如果任一输入字符串是NULLptr,它将作为特殊的前哨错误值返回。

限制:请注意,此代码仅适用于原始的7位ASCII字符集(十进制值0到127,包括十进制),不适用于Unicode字符,例如Unicode字符编码UTF-8(最流行),UTF-16,和UTF-32

这仅是代码(无注释):

完整评论的版本:

测试代码:

从我的eRCaGuy_hello_world资源库中下载完整的示例代码以及单元测试:“ strncmpci.c”

(这只是一个片段)

样本输出:

参考文献:

  1. 这个问题和其他答案在这里起到了启发作用,并给出了一些见解(C语言中的不区分大小写的字符串comp
  2. http://www.cplusplus.com/reference/cstring/strncmp/
  3. https://zh.wikipedia.org/wiki/ASCII
  4. https://en.cppreference.com/w/c/language/operator_precedence

有待进一步研究的课题

  1. (注意:这是C ++,而不是C)Unicode字符的小写
  2. OnlineGDB上的tolower_tests.c:https://onlinegdb.com/HyZieXcew

去做:

  1. 制作此代码的版本,该版本也可用于Unicode的UTF-8实现(字符编码)!

in part because it isn't correct since ...您的代码也不正确。毫无意义地使用tolower,这将是功能最慢的部分。如果您确实希望您的函数能够识别语言环境并处理非ASCII字符,则必须先将字符转换为未签名。否则,您的代码将导致UB
Pavel P

@PavelP,我真的不遵守您的意思。为什么使用毫无意义tolower()时,那才是我们获得不区分大小写的效果的方式,这就是这个问题的重点吗?另外,您链接到它的C ++参考而不是它的C参考。那不会改变一切吗?我从没说过我的函数可以识别语言环境或可以处理非ASCII字符,但是我真的不明白强制转换unsigned char首先可以解决任何问题。可以将所有字符强制转换为未签名。我不明白你的评论。
加布里埃尔·斯台普斯

我已经更新了答案,以指定仅适用于ASCII字符。另外,如果您写一个答案来澄清您的意思,那将会有所帮助。最后,我没有C ++或C ++的非ASCII语言环境(我的意思是没有非"C"语言环境,根据setlocale()程序启动时的默认情况)。我要求您在回答任何可能要澄清这些要点和事情的事情上要足够彻底。
加布里埃尔·斯台普斯 Gabriel Staples)

仅对于ascii,我永远不会使用std :: tolower,最好手动进行操作: static int tolower(char c){ return (c >= 'A' && c <= 'Z') ? (c | ' ') : c; }。std :: tolower非常慢,因为它是区域设置感知的。
Pavel P

1
@GaspardP您好,感谢您指出这种情况。我已经修复了我的代码。解决方法很简单。我初始化ret_code为,0而不是初始化为INT_MIN(或-9999在您测试的代码中那样),然后INT_MIN仅在输入字符串之一为NULLptr时将其设置为。现在,它可以完美运行了。问题很简单,因为forn为0,没有输入任何块(或都if没有输入while),因此它只是返回了我初始化ret_code的对象。不管怎么说,这是现在固定的,和我已经清理了我的单元测试一吨,并在你提到的增加的测试。希望您现在投票。
加布里埃尔·斯台普斯

1

正如其他人所述,没有可移植功能可在所有系统上运行。您可以使用简单的方法来部分规避此问题ifdef


0

如果库中没有任何内容,您可以从中获得一个想法,如何实现有效的想法: 这里

它对所有256个字符使用一个表。

  • 在该表中,所有字符(字母除外)均使用其ascii码。
  • 大写字母代码-小写符号的表列表代码。

那么我们只需要遍历一个字符串并比较给定字符的表单元格:


0

参考


1
这个OR想法有点漂亮,但是逻辑上有缺陷。例如,ignoreCaseComp("`", "@", 1)也许更重要的是,ignoreCaseComp("\0", " ", 1)(即,除位5以外的所有其他位(十进制32)都相同时)都求和0(匹配)。
user966939

0

简单的解决方案:


-1

祝好运

Edit-lowerCaseWord函数使用一个char *变量,并返回此char *的小写字母值。例如,对于char *的值,“ AbCdE”将返回“ abcde”。

基本上,要做的就是在将两个char *变量转换为小写字母之后,对它们使用strcmp函数。

例如,如果我们为“ AbCdE”和“ ABCDE”的值调用strcmpInsensitive函数,它将首先以小写形式返回两个值(“ abcde”),然后对它们执行strcmp函数。


某些解释可能会走很长的路
davejal

降低两个输入字符串似乎完全没有效率,而函数“可能”会在第一个字符比较之后立即返回。例如“ ABcDe”和“ BcdEF”,可以非常快速地返回,而无需降低或升高每个字符串的第一个字符以外的任何内容。
TS

4
更不用说两次内存泄漏了。
Ruud van Gaal

您不要对小写的字符串进行空终止,因此后续字符串strcmp()可能会使程序崩溃。
sth

您还计算strlen(a)总计strlen(a)+1次。加上循环本身,您遍历了strlen(a)+2次。
Stefan Vorkoetter,
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.