C++ 中不区分大小写的字符串比较 [已关闭]

Case-insensitive string comparison in C++ [closed]

提问人:Adam 提问时间:8/15/2008 最后编辑:Toby SpeightAdam 更新时间:9/14/2023 访问量:629488

问:

5年前关闭。
这个问题的答案是社区的努力。编辑现有答案以改进此帖子。它目前不接受新的答案或交互。

在 C++ 中进行不区分大小写的字符串比较而不将字符串转换为全大写或全小写的最佳方法是什么?

请说明这些方法是否对Unicode友好,以及它们的可移植性如何。

C++ 字符串

评论

0赞 Michael Dorgan 5/22/2010
在 c 中,人们通常被迫将整个字符串放在上面,然后以这种方式进行比较 - 或者滚动你自己的比较:P
0赞 Konrad Rudolph 8/26/2008
@[Adam](#11679):虽然这个变体在可用性方面很好,但在性能方面却很糟糕,因为它会产生不必要的副本。我可能会忽略一些东西,但我相信最好的(非 Unicode)方法是使用 .否则,请阅读 Herb 所说的话。std::stricmp
0赞 Móż 11/6/2013
后面的问题有一个更简单的答案:strcasecmp(至少对于BSD和POSIX编译器)stackoverflow.com/questions/9182912/...
0赞 Mark Ransom 12/2/2014
@M σᶎ 这个问题也有这个答案,但重要的警告不是标准的一部分,并且至少在一个通用编译器中缺失。strcasecmp

答:

6赞 Andrew Grant 8/15/2008 #1

假设您正在寻找一种方法而不是已经存在的魔术函数,坦率地说,没有更好的方法。我们都可以为有限的字符集编写带有巧妙技巧的代码片段,但在一天结束时,您必须在某个时候转换字符。

此转换的最佳方法是在比较之前进行。这为您在编码方案方面提供了很大的灵活性,而实际的比较运算符应该对此一无所知。

当然,您可以将此转换“隐藏”在自己的字符串函数或类后面,但是在比较之前仍然需要转换字符串。

13赞 Darren Kopp 8/15/2008 #2

支持 unicode 的可视C++字符串函数:http://msdn.microsoft.com/en-us/library/cc194799.aspx

您可能正在寻找的是_wcsnicmp

评论

7赞 vy32 6/19/2011
具有讽刺意味的是,Microsoft的“宽字符代码”不是Unicode干净的,因为它们不处理Unicode规范化。
12赞 Wedge 8/15/2008 #3

仅供参考,并且容易受到缓冲区溢出的影响,因为它们只是处理,直到它们遇到 null 终止符。使用和_strnicmp()更安全。strcmp()stricmp()_strncmp()

评论

6赞 Adam Rosenfield 11/18/2008
没错,尽管过度读取缓冲区比过度写入缓冲区的危险性要小得多。
4赞 oHo 4/11/2013
stricmp()并且不是 POSIX 标准的一部分:-(但是,您可以在 POSIX 标头中找到 、 和 :-)查看 opengroup.orgstrnicmp()strcasecmp()strcasecmp_l()strncasecmp()strncasecmp_l()strings.h
4赞 karmakaze 3/22/2015
@AdamRosenfield“更糟”取决于上下文。在安全方面,有时覆盖的全部意义在于过度阅读。
4赞 Blair Conrad 8/15/2008 #4

我在使用 International Components for Unicode 库方面有很好的经验 - 它们非常强大,并且提供了转换、区域设置支持、日期和时间呈现、大小写映射(您似乎不想要)和排序规则的方法,其中包括不区分大小写和重音的比较(等等)。我只使用了 C++ 版本的库,但它们似乎也有 Java 版本。

存在执行@Coincoin所指的规范化比较的方法,甚至可以解释语言环境 - 例如(这是一个排序示例,不是严格意义上的相等),传统上在西班牙语(在西班牙)中,字母组合“ll”在“l”和“m”之间排序,因此“lz”<“ll”<“马”。

57赞 Coincoin 8/15/2008 #5

您说的是不区分大小写的哑比较还是完全规范化的 Unicode 比较?

愚蠢的比较不会找到可能相同但不二进制相等的字符串。

例:

U212B (ANGSTROM SIGN)
U0041 (LATIN CAPITAL LETTER A) + U030A (COMBINING RING ABOVE)
U00C5 (LATIN CAPITAL LETTER A WITH RING ABOVE).

都是等价的,但它们也具有不同的二进制表示形式。

也就是说,Unicode 规范化应该是一本必读的书,特别是如果您打算支持韩文、泰语和其他亚洲语言。

此外,IBM 几乎为最优化的 Unicode 算法申请了专利,并将它们公开发布。他们还维护一个实现:IBM ICU

评论

0赞 Aconcagua 7/7/2022
迟到的来者,我知道......“都是等价的”可能并不完全正确,尽管我不熟悉给定的情况——德语“Umlaut”虽然可以通过组合 、 或与 diaeresis 或直接通过字母 , 、 创建——但是两个点的距离(略微)不同(直接字符更窄)......aouäöü
16赞 7 revsAdam #6

我试图从所有帖子中拼凑出一个好的答案,所以帮我编辑这个:

这里有一种方法,虽然它确实转换了字符串,并且对Unicode不友好,但它应该是可移植的,这是一个优点:

bool caseInsensitiveStringCompare( const std::string& str1, const std::string& str2 ) {
    std::string str1Cpy( str1 );
    std::string str2Cpy( str2 );
    std::transform( str1Cpy.begin(), str1Cpy.end(), str1Cpy.begin(), ::tolower );
    std::transform( str2Cpy.begin(), str2Cpy.end(), str2Cpy.begin(), ::tolower );
    return ( str1Cpy == str2Cpy );
}

据我所知,它比 stricmp() 更具可移植性,因为 stricmp() 实际上不是 std 库的一部分,而仅由大多数编译器供应商实现。

要获得真正对Unicode友好的实现,似乎必须离开std库。一个很好的第三方库是 IBM ICU(Unicode 国际组件)

此外,boost::iequals 为进行此类比较提供了一个相当不错的实用程序。

评论

0赞 VextoR 3/11/2011
你能说说,是什么意思::tolower,为什么你可以使用tolower而不是toLower(),以及之前的'::'是什么?谢谢
20赞 Timmmm 3/14/2011
这不是一个非常有效的解决方案 - 即使第一个字符不同,您也可以复制两个字符串并转换所有字符串。
3赞 celticminstrel 6/21/2015
如果你无论如何都要做一个副本,为什么不按值而不是按引用传递呢?
1赞 Sandburg 6/6/2019
在比较之前,该问题明确要求不要整个字符串transform
1赞 Evg 9/26/2020
std::tolower不应直接调用,需要 to。charstatic_castunsigned char
65赞 Derek Park 8/15/2008 #7

如果你使用的是 POSIX 系统,则可以使用 strcasecmp。但是,此函数不是标准 C 的一部分,在 Windows 上也不可用。这将对 8 位字符执行不区分大小写的比较,只要区域设置为 POSIX。如果区域设置不是 POSIX,则结果是未定义的(因此它可能会执行本地化比较,也可能不执行)。宽字符等效项不可用。

如果做不到这一点,许多历史 C 库实现都具有函数 stricmp() 和 strnicmp()。Windows 上的 Visual C++ 通过在它们前面加上下划线来重命名所有这些,因为它们不是 ANSI 标准的一部分,因此在该系统上它们被称为 _stricmp 或 _strnicmp。一些库还可能具有宽字符或多字节等效函数(通常命名为 wcsicmp、mbcsicmp 等)。

C 和 C++ 在很大程度上都对国际化问题一无所知,因此除了使用第三方库之外,没有很好的解决方案来解决这个问题。如果您需要一个强大的 C/C++ 库,请查看 IBM ICU(Unicode 国际组件)。ICU 适用于 Windows 和 Unix 系统。

34赞 Shadow2531 8/26/2008 #8

对于非 unicode 版本,我的第一个想法是做这样的事情:

bool caseInsensitiveStringCompare(const string& str1, const string& str2) {
    if (str1.size() != str2.size()) {
        return false;
    }
    for (string::const_iterator c1 = str1.begin(), c2 = str2.begin(); c1 != str1.end(); ++c1, ++c2) {
        if (tolower(static_cast<unsigned char>(*c1)) != tolower(static_cast<unsigned char>(*c2))) {
            return false;
        }
    }
    return true;
}
6赞 John Dibling 11/18/2008 #9

我编写了一个不区分大小写的 char_traits 版本,用于 std::basic_string,以便在使用内置的 std::basic_string 成员函数进行比较、搜索等时生成一个不区分大小写的 std::string。

换句话说,我想做这样的事情。

std::string a = "Hello, World!";
std::string b = "hello, world!";

assert( a == b );

...std::string 无法处理。以下是我的新char_traits的用法:

std::istring a = "Hello, World!";
std::istring b = "hello, world!";

assert( a == b );

...下面是实现:

/*  ---

        Case-Insensitive char_traits for std::string's

        Use:

            To declare a std::string which preserves case but ignores case in comparisons & search,
            use the following syntax:

                std::basic_string<char, char_traits_nocase<char> > noCaseString;

            A typedef is declared below which simplifies this use for chars:

                typedef std::basic_string<char, char_traits_nocase<char> > istring;

    --- */

    template<class C>
    struct char_traits_nocase : public std::char_traits<C>
    {
        static bool eq( const C& c1, const C& c2 )
        { 
            return ::toupper(c1) == ::toupper(c2); 
        }

        static bool lt( const C& c1, const C& c2 )
        { 
            return ::toupper(c1) < ::toupper(c2);
        }

        static int compare( const C* s1, const C* s2, size_t N )
        {
            return _strnicmp(s1, s2, N);
        }

        static const char* find( const C* s, size_t N, const C& a )
        {
            for( size_t i=0 ; i<N ; ++i )
            {
                if( ::toupper(s[i]) == ::toupper(a) ) 
                    return s+i ;
            }
            return 0 ;
        }

        static bool eq_int_type( const int_type& c1, const int_type& c2 )
        { 
            return ::toupper(c1) == ::toupper(c2) ; 
        }       
    };

    template<>
    struct char_traits_nocase<wchar_t> : public std::char_traits<wchar_t>
    {
        static bool eq( const wchar_t& c1, const wchar_t& c2 )
        { 
            return ::towupper(c1) == ::towupper(c2); 
        }

        static bool lt( const wchar_t& c1, const wchar_t& c2 )
        { 
            return ::towupper(c1) < ::towupper(c2);
        }

        static int compare( const wchar_t* s1, const wchar_t* s2, size_t N )
        {
            return _wcsnicmp(s1, s2, N);
        }

        static const wchar_t* find( const wchar_t* s, size_t N, const wchar_t& a )
        {
            for( size_t i=0 ; i<N ; ++i )
            {
                if( ::towupper(s[i]) == ::towupper(a) ) 
                    return s+i ;
            }
            return 0 ;
        }

        static bool eq_int_type( const int_type& c1, const int_type& c2 )
        { 
            return ::towupper(c1) == ::towupper(c2) ; 
        }       
    };

    typedef std::basic_string<char, char_traits_nocase<char> > istring;
    typedef std::basic_string<wchar_t, char_traits_nocase<wchar_t> > iwstring;

评论

2赞 coppro 11/25/2008
这适用于常规字符,但不适用于所有 Unicode,因为强制化不一定是双向的(希腊语中有一个很好的例子涉及 sigma,我现在不记得了;像它有两个小写和一个大写,你无法以任何一种方式进行适当的比较)
1赞 Ferruccio 11/25/2008
这确实是错误的方法。区分大小写不应是字符串本身的属性。当同一字符串对象需要区分大小写和不区分大小写的比较时,会发生什么情况?
0赞 John Dibling 11/25/2008
如果区分大小写不适合作为字符串的“一部分”,那么 find() 函数也根本不适合。对你来说,这可能是真的,这很好。IMO C++ 最伟大的事情是它不会将特定的范式强加给程序员。这是你想要/需要的。
0赞 Andreas Magnusson 11/25/2008
实际上,我认为大多数C++大师(如标准委员会的成员)都同意将 find() 与许多其他同样可以放在自由函数中的东西一起放在 std::basic_string<> 中是一个错误。此外,将其放入类型中也存在一些问题。
0赞 Konrad Rudolph 11/25/2008
正如其他人所指出的,这个解决方案有两个主要问题(具有讽刺意味的是,一个是接口,另一个是实现;-))。
329赞 Rob 11/25/2008 #10

Boost 为此提供了一个方便的算法:

#include <boost/algorithm/string.hpp>
// Or, for fewer header dependencies:
//#include <boost/algorithm/string/predicate.hpp>

std::string str1 = "hello, world!";
std::string str2 = "HELLO, WORLD!";

if (boost::iequals(str1, str2))
{
    // Strings are identical
}

评论

16赞 vladr 10/30/2010
这个 UTF-8 友好吗?我认为不是。
20赞 vy32 6/19/2011
不可以,因为 UTF-8 允许使用不同的二进制代码对相同的字符串进行编码,这是由于重音、组合、双向问题等。
12赞 Wiz 11/11/2011
@vy32 这是绝对不正确的!UTF-8 组合是互斥的。它必须始终使用尽可能短的表示形式,如果不这样做,则它是格式错误的 UTF-8 序列或代码点,必须小心处理。
57赞 vy32 11/11/2011
@Wiz,您忽略了 Unicode 字符串规范化的问题。ñ 可以表示为组合 ̃ 后跟 n,也可以表示为 ñ 字符。在执行比较之前,您需要使用 Unicode 字符串规范化。请查看 Unicode 技术报告 #15,unicode.org/reports/tr15
14赞 Mooing Duck 5/30/2014
@wonkorealtime:因为转换为大写的“ß”是“SS”:fileformat.info/info/unicode/char/df/index.htm
2赞 Johann Gerell 11/25/2008 #11

只是关于您最终选择的任何方法的说明,如果该方法恰好包括一些答案建议的使用:strcmp

strcmp通常不适用于 Unicode 数据。通常,它甚至不适用于基于字节的 Unicode 编码,例如 utf-8,因为只能进行逐字节比较,而以 utf-8 编码的 Unicode 码位可以占用超过 1 个字节。正确处理的唯一特定 Unicode 大小写是,当使用基于字节的编码编码的字符串仅包含低于 U+00FF 的码位时,则逐字节比较就足够了。strcmpstrcmp

28赞 bradtgmurray 12/2/2008 #12

您可以在 Unix 或 Windows 上使用。strcasecmpstricmp

到目前为止没有提到的一件事是,如果您在这些方法中使用 stl 字符串,则首先比较两个字符串的长度很有用,因为此信息已在字符串类中可用。如果您要比较的两个字符串一开始的长度甚至不同,这可能会阻止进行成本高昂的字符串比较。

评论

0赞 uliwitness 1/22/2014
由于确定字符串的长度包括遍历字符串中的每个字符并将其与 0 进行比较,因此这与立即比较字符串之间真的有那么大区别吗?我想在两个字符串不匹配的情况下,您可以获得更好的内存局部性,但在匹配的情况下,运行时间可能接近 2 倍。
6赞 bradtgmurray 2/5/2014
C++11 指定 std::string::length 的复杂度必须为常量:cplusplus.com/reference/string/string/length
2赞 uliwitness 2/6/2014
这是一个有趣的小事实,但在这里几乎没有关系。strcasecmp() 和 stricmp() 都采用未修饰的 C 字符串,因此不涉及 std::string。
4赞 Nathan 2/13/2014
如果比较“a”与“ab”,这些方法将返回 -1。长度不同,但“a”在“ab”之前。因此,如果调用方关心排序,则简单地比较长度是不可行的。
11赞 Dean Harding 5/22/2010 #13

Boost.String 库有很多算法用于进行不区分大小写的比较等。

你可以实现你自己的,但是当它已经完成时,为什么还要麻烦呢?

评论

1赞 WilliamKF 5/22/2010
std::string没有内置的方法吗?
6赞 Dean Harding 5/22/2010
不,没有。
3赞 jww 2/8/2016
“......既然已经完成了,为什么还要打扰呢?- 如果您不使用 Boost 怎么办?OP 没有带有问题的标签。
123赞 wilhelmtell 5/22/2010 #14

利用标准 .回想一下,a 实际上是 的 typedef,或者更明确地说,是 。该类型描述了角色如何比较、如何复制、如何投射等。您需要做的就是 typedef 一个新字符串,并为其提供您自己的自定义,这些自定义值不区分大小写。char_traitsstd::stringstd::basic_string<char>std::basic_string<char, std::char_traits<char> >char_traitsbasic_stringchar_traits

struct ci_char_traits : public char_traits<char> {
    static bool eq(char c1, char c2) { return toupper(c1) == toupper(c2); }
    static bool ne(char c1, char c2) { return toupper(c1) != toupper(c2); }
    static bool lt(char c1, char c2) { return toupper(c1) <  toupper(c2); }
    static int compare(const char* s1, const char* s2, size_t n) {
        while( n-- != 0 ) {
            if( toupper(*s1) < toupper(*s2) ) return -1;
            if( toupper(*s1) > toupper(*s2) ) return 1;
            ++s1; ++s2;
        }
        return 0;
    }
    static const char* find(const char* s, int n, char a) {
        while( n-- > 0 && toupper(*s) != toupper(a) ) {
            ++s;
        }
        return s;
    }
};

typedef std::basic_string<char, ci_char_traits> ci_string;

详情见《本周大师》第29期

评论

15赞 Zan Lynx 9/27/2012
据我从自己的实验中了解到,这使得您的新字符串类型与 std::string 不兼容。
11赞 Andreas Spindler 10/9/2012
当然可以 - 为了它自己的利益。不区分大小写的字符串是另一回事:,而不是 .typedef std::basic_string<char, ci_char_traits<char> > istringtypedef std::basic_string<char, std::char_traits<char> > string
295赞 Tim MB 4/19/2013
“你需要做的一切......”
35赞 Erik Aronesty 11/14/2014
在这个微不足道的案例中,任何强迫这种精神错乱的语言结构都应该而且可以毫无遗憾地被放弃。
5赞 srm 3/22/2018
@DaveKennedy我认为埃里克建议放弃人类语言,因为这些语言结构迫使这种疯狂。:-)
134赞 Timmmm 11/8/2010 #15

boost 的问题在于您必须链接并依赖 boost。在某些情况下并不容易(例如android)。

使用 char_traits 意味着您的所有比较都不区分大小写,这通常不是您想要的。

这应该足够了。它应该是相当有效的。不处理 unicode 或任何东西。

#include <cctype>    // std::tolower
#include <algorithm> // std::equal

bool ichar_equals(char a, char b)
{
    return std::tolower(static_cast<unsigned char>(a)) ==
           std::tolower(static_cast<unsigned char>(b));
}

bool iequals(const std::string& a, const std::string& b)
{
    return a.size() == b.size() &&
           std::equal(a.begin(), a.end(), b.begin(), ichar_equals);
}
C++14 版本
#include <cctype>    // std::tolower
#include <algorithm> // std::equal

bool iequals(const std::string& a, const std::string& b)
{
    return std::equal(a.begin(), a.end(), b.begin(), b.end(), ichar_equals);
}
C++20 版本使用std::ranges
#include <cctype>      // std::tolower
#include <algorithm>   // std::equal
#include <string_view> // std::string_view

bool iequals(std::string_view lhs, std::string_view rhs)
{
    return std::ranges::equal(lhs, rhs, ichar_equals);
}

评论

33赞 Gretchen 3/10/2011
实际上,boost 字符串库是一个仅标头库,因此无需链接到任何内容。此外,您可以使用 boost 的“bcp”实用程序仅将字符串标头复制到源代码树中,因此您不需要完整的 boost 库。
2赞 Behrouz.M 6/1/2015
@Anna 需要构建和链接 boost 的文本库。它使用 IBM ICU。
6赞 Evg 9/26/2020
std::tolower不应直接调用,需要 to。charstatic_castunsigned char
1赞 Jonatan Lindén 9/16/2021
在 C++14 版本中,只需将 lambda 函数的参数列表更改为 ,就不需要了。[](unsigned char a, unsigned char b)static_cast
2赞 Ben Cottrell 1/2/2022
@Timmmm我冒昧地在这个答案中添加了一个 C++20 版本,因为我相信这是最合适的,并且与此线程中的其他答案相比,我觉得与您的其他解决方案最相似。
34赞 Igor Milyakov 4/26/2012 #16

Boost::Iequals 在字符串的情况下与 UTF-8 不兼容。 您可以使用 boost::locale

comparator<char,collator_base::secondary> cmpr;
cout << (cmpr(str1, str2) ? "str1 < str2" : "str1 >= str2") << endl;
  • 主要 -- 忽略重音和字符大小写,仅比较基本字母。例如,“facade”和“Facade”是相同的。
  • 次要 -- 忽略字符大小写,但考虑重音。“门面”和“门面”是不同的,但“门面”和“门面”是相同的。
  • 第三级 -- 考虑大小写和重音:“Façade”和“façade”是不同的。忽略标点符号。
  • 四元 -- 考虑所有大小写、重音符号和标点符号。这些单词在 Unicode 表示形式方面必须相同。
  • 相同 -- 作为四元,但也比较代码点。
1赞 michaelhanson 4/2/2013 #17

截至 2013 年初,由 IBM 维护的 ICU 项目是一个很好的答案。

http://site.icu-project.org/

ICU 是一个“完整、可移植的 Unicode 库,密切跟踪行业标准”。对于字符串比较的特定问题,Collation 对象可以执行所需的操作。

Mozilla 项目在 2012 年年中采用了 ICU 在 Firefox 中实现国际化;您可以在此处跟踪工程讨论,包括构建系统和数据文件大小的问题:

9赞 Neutrino 6/27/2013 #18

对于我的基本不区分大小写的字符串比较需求,我宁愿不必使用外部库,也不想要一个具有不区分大小写特征的单独字符串类,该类与我的所有其他字符串不兼容。

所以我想出的是这样的:

bool icasecmp(const string& l, const string& r)
{
    return l.size() == r.size()
        && equal(l.cbegin(), l.cend(), r.cbegin(),
            [](string::value_type l1, string::value_type r1)
                { return toupper(l1) == toupper(r1); });
}

bool icasecmp(const wstring& l, const wstring& r)
{
    return l.size() == r.size()
        && equal(l.cbegin(), l.cend(), r.cbegin(),
            [](wstring::value_type l1, wstring::value_type r1)
                { return towupper(l1) == towupper(r1); });
}

一个简单的函数,一个重载用于 char,另一个重载用于 whar_t。不使用任何非标准的东西,所以在任何平台上都应该没问题。

相等性比较不会考虑可变长度编码和 Unicode 规范化等问题,但无论如何,basic_string对此没有任何支持,而且通常不是问题。

如果需要对文本进行更复杂的词典操作,那么您只需使用像 Boost 这样的第三方库,这是意料之中的。

评论

2赞 uliwitness 1/22/2014
如果您将其设为模板并使用 basic_string<T> 而不是单独的 string/wstring 版本,您可能会将其制作成一个函数?
2赞 Neutrino 6/28/2015
单个函数模板如何在不使用专用化或宏的情况下调用 toupper 或 towupper,函数重载似乎比任何一个都更简单、更合适的实现。
4赞 reubenjohn 7/26/2013 #19

仅用于区分大小写和/或不区分大小写的比较。它们都在头文件中strcmp()strcmpi()stricmp()<string.h>

格式:

int strcmp(const char*,const char*);    //for case sensitive
int strcmpi(const char*,const char*);   //for case insensitive

用法:

string a="apple",b="ApPlE",c="ball";
if(strcmpi(a.c_str(),b.c_str())==0)      //(if it is a match it will return 0)
    cout<<a<<" and "<<b<<" are the same"<<"\n";
if(strcmpi(a.c_str(),b.c_str()<0)
    cout<<a[0]<<" comes before ball "<<b[0]<<", so "<<a<<" comes before "<<b;

输出

apple 和 ApPlE 是一样的

A 在 B 之前,所以 Apple 在 Ball 之前

评论

2赞 Thomas Daugaard 7/30/2013
投反对票,因为这几乎不是一种 C++ 做事方式。
0赞 reubenjohn 8/14/2013
这是我大学的 c++ 约定,但在这里发帖时我会牢记这一点
4赞 uliwitness 1/22/2014
stricmp是Microsoft扩展AFAIK。BSD 似乎有 strcasecmp() 代替。
-2赞 user4578093 3/5/2015 #20
bool insensitive_c_compare(char A, char B){
  static char mid_c = ('Z' + 'a') / 2 + 'Z';
  static char up2lo = 'A' - 'a'; /// the offset between upper and lowers

  if ('a' >= A and A >= 'z' or 'A' >= A and 'Z' >= A)
      if ('a' >= B and B >= 'z' or 'A' >= B and 'Z' >= B)
      /// check that the character is infact a letter
      /// (trying to turn a 3 into an E would not be pretty!)
      {
        if (A > mid_c and B > mid_c or A < mid_c and B < mid_c)
        {
          return A == B;
        }
        else
        {
          if (A > mid_c)
            A = A - 'a' + 'A'; 
          if (B > mid_c)/// convert all uppercase letters to a lowercase ones
            B = B - 'a' + 'A';
          /// this could be changed to B = B + up2lo;
          return A == B;
        }
      }
}

这可能会更有效率,但这是一个笨重的版本,所有部分都裸露在外。

不是那么便携,但适用于我电脑上的任何内容(不知道,我是图片而不是文字)

评论

0赞 Behrouz.M 6/1/2015
这不是Unicode支持,这是问题所问的。
0赞 Robert Andrzejuk 4/29/2018
这不支持非英语字符集。
1赞 smibe 3/6/2015 #21

如果您必须更频繁地将源字符串与其他字符串进行比较,一个优雅的解决方案是使用正则表达式。

std::wstring first = L"Test";
std::wstring second = L"TEST";

std::wregex pattern(first, std::wregex::icase);
bool isEqual = std::regex_match(second, pattern);

评论

0赞 Deqing 5/15/2015
试过这个,但编译错误:error: conversion from 'const char [5]' to non-scalar type 'std::wstring {aka std::basic_string<wchar_t>}' requested
0赞 Behrouz.M 6/1/2015
坏主意。这是最糟糕的解决方案。
0赞 celticminstrel 6/21/2015
这不是一个好的解决方案,但即使你想使用它,你也需要在宽字符串常量前面加上一个 L,例如 L“TEST”
0赞 smibe 9/30/2015
如果有人能解释为什么这是最糟糕的解决方案,那就太好了。因为性能问题?创建正则表达式的成本很高,但之后的比较应该非常快。
0赞 Ben 8/17/2016
它是可用和可移植的,主要问题是 First 不能包含正则表达式使用的任何字符。因此,它不能用作一般的字符串比较。它也会变慢,有一个标志可以使它按照 smibe 所说的方式工作,但仍然不能用作一般功能。
-4赞 Craig Stoddard 5/12/2015 #22

比较仅因小写和大写字符而不同的字符串的一种简单方法是进行 ascii 比较。ascii 表中的所有大写字母和小写字母相差 32 位,使用此信息,我们得到以下内容......

    for( int i = 0; i < string2.length(); i++)
    {
       if (string1[i] == string2[i] || int(string1[i]) == int(string2[j])+32 ||int(string1[i]) == int(string2[i])-32) 
    {
      count++;
      continue;
    }
    else 
    {
      break;
    }
    if(count == string2.length())
    {
      //then we have a match
    }
}

评论

3赞 celticminstrel 6/21/2015
据此,“++j”等于“KKJ”,“1234”等于“QRST”。我怀疑这是任何人都想要的。
16赞 Brian Rodriguez 9/17/2015 #23

请参阅 std::lexicographical_compare:

// lexicographical_compare example
#include <iostream>     // std::cout, std::boolalpha
#include <algorithm>    // std::lexicographical_compare
#include <cctype>       // std::tolower

// a case-insensitive comparison function:
bool mycomp(char c1, char c2) {
    return std::tolower(c1) < std::tolower(c2);
}

int main() {
    std::string foo = "Apple";
    std::string bar = "apartment";
    
    std::cout << std::boolalpha;
    
    std::cout << "Comparing foo and bar lexicographically (foo<bar):\n";
    
    std::cout << "Using default comparison (operator<): ";
    std::cout << std::lexicographical_compare(foo.begin(), foo.end(), bar.begin(), bar.end());
    std::cout << '\n';
    
    std::cout << "Using custom comparison (mycomp): ";
    std::cout << std::lexicographical_compare(foo.begin(), foo.end(), bar.begin(), bar.end(), mycomp);
    std::cout << '\n';
    
    return 0;
}

演示

评论

1赞 plasmacel 3/27/2018
这种方法可能不安全且不可移植。 仅当字符为 ASCII 编码时才有效。没有这样的保证 - 所以它很容易被定义为未定义的行为。std::tolowerstd::string
1赞 Brian Rodriguez 4/6/2018
@plasmacel 然后使用适用于其他编码的函数。
0赞 Robin Davies 3/12/2023
std::lexicographical_compare看起来很有希望,直到我们到了 mycomp。:-(
0赞 Brian Rodriguez 6/7/2023
此算法适用于任何元素类型。如果需要 unicode 支持,请提供 unicode 感知字符串和 unicode 感知迭代器/比较。换句话说,问题不在于 .std::stringstd::lexicographical_compare
6赞 Simon Richter 9/29/2015 #24

迟到了,但这里有一个使用 的变体,因此可以正确处理土耳其语:std::locale

auto tolower = std::bind1st(
    std::mem_fun(
        &std::ctype<char>::tolower),
    &std::use_facet<std::ctype<char> >(
        std::locale()));

为您提供一个函子,该函子使用活动区域设置将字符转换为小写,然后您可以使用该 via 生成小写字符串:std::transform

std::string left = "fOo";
transform(left.begin(), left.end(), left.begin(), tolower);

这也适用于基于字符串的字符串。wchar_t

评论

0赞 nimish 3/25/2023
绝对比使用下/上的所有其他人都好,而不考虑 i18n 问题
9赞 DavidS 1/12/2016 #25

在不使用 Boost 的情况下执行此操作可以通过获取 C 字符串指针并使用:c_str()strcasecmp

std::string str1 ="aBcD";
std::string str2 = "AbCd";;
if (strcasecmp(str1.c_str(), str2.c_str()) == 0)
{
    //case insensitive equal 
}
14赞 kyb 9/30/2016 #26

短小精悍。除了扩展的 std C 库外,没有其他依赖项。

strcasecmp(str1.c_str(), str2.c_str()) == 0

如果 和 相等,则返回 true。 可能不存在,可能有类似物等。str1str2strcasecmpstricmpstrcmpi

示例代码:

#include <iostream>
#include <string>
#include <string.h> //For strcasecmp(). Also could be found in <mem.h>

using namespace std;

/// Simple wrapper
inline bool str_ignoreCase_cmp(std::string const& s1, std::string const& s2) {
    if(s1.length() != s2.length())
        return false;  // optimization since std::string holds length in variable.
    return strcasecmp(s1.c_str(), s2.c_str()) == 0;
}

/// Function object - comparator
struct StringCaseInsensetiveCompare {
    bool operator()(std::string const& s1, std::string const& s2) {
        if(s1.length() != s2.length())
            return false;  // optimization since std::string holds length in variable.
        return strcasecmp(s1.c_str(), s2.c_str()) == 0;
    }
    bool operator()(const char *s1, const char * s2){ 
        return strcasecmp(s1,s2)==0;
    }
};


/// Convert bool to string
inline char const* bool2str(bool b){ return b?"true":"false"; }

int main()
{
    cout<< bool2str(strcasecmp("asd","AsD")==0) <<endl;
    cout<< bool2str(strcasecmp(string{"aasd"}.c_str(),string{"AasD"}.c_str())==0) <<endl;
    StringCaseInsensetiveCompare cmp;
    cout<< bool2str(cmp("A","a")) <<endl;
    cout<< bool2str(cmp(string{"Aaaa"},string{"aaaA"})) <<endl;
    cout<< bool2str(str_ignoreCase_cmp(string{"Aaaa"},string{"aaaA"})) <<endl;
    return 0;
}

输出:

true
true
true
true
true

评论

11赞 kyb 9/30/2016
奇怪的是,C++ std::string 没有忽略大小写比较方法。
1赞 Liviu 10/21/2016
“strcasecmp 不是标准的一部分” - Mark Ransom Dec 1 '14 at 19:57
1赞 kyb 10/22/2016
是的,但大多数现代编译器都有它或它的另一个名称的类似物。、 、 等 谢谢。消息已编辑。stricmpstrcmpistrcasecmp
0赞 kyb 6/1/2017
TODO:使用而不是我的,因为它将 bool 隐式转换为流的字符。cout << boolalphabool2str
0赞 Owl 8/8/2017
它位于 gcc 库的 <strings.h> 中。
16赞 vine'th 4/5/2017 #27
str1.size() == str2.size() && std::equal(str1.begin(), str1.end(), str2.begin(), [](auto a, auto b){return std::tolower(a)==std::tolower(b);})

如果您无法使用 boost,则可以在 C++14 中使用上述代码。您必须用于宽字符。std::towlower

评论

4赞 ɲeuroburɳ 8/1/2017
我认为您需要在前面添加 a,以便在 str2 是 str1 的前缀时不会越界。str1.size() == str2.size() &&
2赞 Jagadeesh Pulamarasetti 8/27/2017 #28

看起来上面的解决方案没有使用比较方法并再次实现总计,所以这是我的解决方案,希望它对你有用(它工作正常)。

#include<iostream>
#include<cstring>
#include<cmath>
using namespace std;
string tolow(string a)
{
    for(unsigned int i=0;i<a.length();i++)
    {
        a[i]=tolower(a[i]);
    }
    return a;
}
int main()
{
    string str1,str2;
    cin>>str1>>str2;
    int temp=tolow(str1).compare(tolow(str2));
    if(temp>0)
        cout<<1;
    else if(temp==0)
        cout<<0;
    else
        cout<<-1;
}
3赞 DAme 4/18/2018 #29

在 c++ 中比较两个字符串(针对 Windows 测试)的一种简单方法是使用 _stricmp

// Case insensitive (could use equivalent _stricmp)  
result = _stricmp( string1, string2 );  

如果您希望与 std::string 一起使用,请举个例子:

std::string s1 = string("Hello");
if ( _stricmp(s1.c_str(), "HELLO") == 0)
   std::cout << "The string are equals.";

欲了解更多信息,请访问:https://msdn.microsoft.com/it-it/library/e0z9k731.aspx

评论

1赞 Claus Jørgensen 8/23/2018
除了这个答案之外,还值得一读 stackoverflow.com/a/12414441/95309,因为它是 a) 一个 C 函数,b) 据说是不可移植的。
0赞 ekkis 6/23/2019
我们需要什么 #include 才能做到这一点?
1赞 DAme 7/1/2019
@ekkis使用_stricmp您必须包含 <string.h>,您可以在此处阅读: learn.microsoft.com/en-us/cpp/c-runtime-library/reference/...
1赞 AdrianTut 3/25/2022
不错的尝试微软!
0赞 HeavenHM 4/27/2018 #30

如果您不想使用 Boost 库,那么这里是仅使用 C++ 标准 io 标头的解决方案。

#include <iostream>
#include <cctype>
#include <algorithm>
#include <stdexcept>
#include <cassert>

struct iequal
{
    bool operator()(int c1, int c2) const
    {
        return std::toupper(c1) == std::toupper(c2);
    }
};

bool iequals(const std::string& str1, const std::string& str2)
{
    if (str1.empty() || str2.empty())
    {
        return str1.empty() && str2.empty();
    }
    return std::equal(str1.begin(), str1.end(), str2.begin(), iequal());
}

void runTests()
{
    assert(iequals("HELLO", "hello") == true);
    assert(iequals("HELLO", "") == false);
    assert(iequals("", "hello") == false);
    assert(iequals("", "") == true);
    std::cout << "All tests passed!" << std::endl;
}

int main(void)
{
    try
    {
        runTests();
    }
    catch (const std::exception& e)
    {
        std::cerr << "Exception: " << e.what() << std::endl;
    }
    return 0;
}

评论

0赞 David Ledger 12/2/2018
我相信 std::toupper 在 #include < cctype> 中,您可能需要包含它。
0赞 HeavenHM 12/2/2018
如果你将使用这样的全局版本::toupper,那么你可能不需要包含<ctype>因为有两个版本,我猜有两个版本的c版本和c++版本。所以最好使用全局版本“::toupper()”
0赞 ekkis 6/23/2019
当其中一个字符串为空时,此解决方案将失败: “” -- 在这种情况下,它应该返回 false 时返回 true
0赞 HeavenHM 9/14/2023
@ekkis 现在我已经用单元测试更新了它,现在你可以检查