当前位置:  开发笔记 > 编程语言 > 正文

C++中不区分大小写的字符串比较

如何解决《C++中不区分大小写的字符串比较》经验,为你挑选了19个好方法。

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

请说明这些方法是否符合Unicode,以及它们的可移植性.



1> Rob..:

Boost包含一个方便的算法:

#include 
// Or, for fewer header dependencies:
//#include 

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

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


@Wiz,你忽略了Unicode字符串规范化的问题.ñ可以表示为组合〜后跟n或具有ñ字符.在执行比较之前,您需要使用Unicode字符串规范化.请查看Unicode技术报告#15,http://unicode.org/reports/tr15/
不,因为UTF-8允许使用不同的二进制代码对相同的字符串进行编码,这是由于重音,组合,比迪西问题等.
这是UTF-8友好吗?我想不是.
@wonkorealtime:因为"ß"转换为大写是"SS":http://www.fileformat.info/info/unicode/char/df/index.htm
@ vy32这绝对不对!UTF-8组合是互斥的.它必须始终使用尽可能短的表示,否则,它是一个格式错误的UTF-8序列或代码点,必须小心处理.
@ vy32(我从未跟回过那条评论),但这并不意味着它不是UTF-8友好的.比较应进行标准化,以完全分解或完全组合以消除此类等效问题.然而,没有什么能阻止iequals这样做.
在一般情况下,它不适用于Unicode."ß"和"SS"应该相等,但是Boost String Algorithms不能处理这个问题.
@dalle为什么"ß"和"SS"比较平等?在什么用途?大多数人不希望瑞士的"ß"

2> wilhelmtell..:

利用标准char_traits.回想一下,a std::string实际上是一个typedef std::basic_string,或更明确地说std::basic_string >.该char_traits类型描述了字符如何比较,如何复制,如何转换等等.您需要做的就是键入一个新的字符串basic_string,并为它提供自己的自定义char_traits,比较不区分大小写.

struct ci_char_traits : public char_traits {
    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 ci_string;

细节是关于第29周的大师.


"所有你需要做的......"
在这个微不足道的案件中强迫这种疯狂的任何语言结构应该而且可以毫无遗憾地被抛弃.
据我所知,这使得你的新字符串类型与std :: string不兼容.
当然它确实 - 为了它自己的利益.不区分大小写的字符串是其他内容:`typedef std :: basic_string > istring`,而不是`typedef std :: basic_string > string`.
@Nathan可能使用能够在代码上执行基本CSE的编译器...
我知道这是直接从GotW29复制的,我假设这个被广泛引用的东西是正确的,但是对于我(在Visual Studio 2005上),这里的查找功能不起作用.它导致basic_string :: find溢出缓冲区并崩溃.我不得不改变"回归"; "return(n> = 0?s:NULL);".
compare()方法为每个字符调用两次toupper().可能应该缓冲toupper()的结果以减少CPU影响.
@DaveKennedy我认为Erik建议放弃人类语言,因为*那些*是迫使这种精神错乱的语言结构。:-)

3> Timmmm..:

增强的问题在于你必须链接并依赖于提升.在某些情况下并不容易(例如android).

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

这应该足够了.它应该是合理有效的.虽然不处理unicode或其他任何东西.

bool iequals(const string& a, const string& b)
{
    unsigned int sz = a.size();
    if (b.size() != sz)
        return false;
    for (unsigned int i = 0; i < sz; ++i)
        if (tolower(a[i]) != tolower(b[i]))
            return false;
    return true;
}

更新:Bonus C++ 14 version(#include ):

bool iequals(const string& a, const string& b)
{
    return std::equal(a.begin(), a.end(),
                      b.begin(), b.end(),
                      [](char a, char b) {
                          return tolower(a) == tolower(b);
                      });
}


实际上,boost字符串库是一个只有标头的库,所以不需要链接到任何东西.此外,您可以使用boost的'bcp'实用程序将字符串标头复制到源树,因此您无需需要完整的boost库.
很高兴知道一个简单的非boost依赖版本.
@Anna需要构建boost文本库并链接.它使用IBM ICU.

4> Derek Park..:

如果您使用的是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系统.



5> Coincoin..:

你在谈论一个愚蠢的不区分大小写的比较或完全标准化的Unicode比较吗?

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

例:

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

都是等价的,但它们也有不同的二进制表示.

也就是说,Unicode规范化应该是强制性的读取,特别是如果您计划支持Hangul,Thaï和其他亚洲语言.

此外,IBM还获得了大多数优化的Unicode算法专利,并将其公之于众.他们还维护一个实现:IBM ICU



6> Igor Milyako..:

在字符串的情况下,boost :: iequals不兼容utf-8.您可以使用boost :: locale.

comparator cmpr;
cout << (cmpr(str1, str2) ? "str1 < str2" : "str1 >= str2") << endl;

主要 - 忽略重音和字符大小写,仅比较基本字母.例如"facade"和"Façade"是相同的.

次要 - 忽略字符大小写但考虑重音."立面"和"立面"不同,但"外墙"和"立面"是相同的.

大学 - 考虑案例和口音:"外墙"和"外观"是不同的.忽略标点符号.

第四纪 - 考虑所有案例,重音和标点符号.在Unicode表示方面,单词必须相同.

相同 - 作为四元组,但也比较代码点.



7> Shadow2531..:

我对非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(*c1) != tolower(*c2)) {
            return false;
        }
    }
    return true;
}



8> bradtgmurray..:

您可以strcasecmp在Unix或stricmpWindows上使用.

到目前为止还没有提到的一件事是,如果你使用这些方法的stl字符串,首先比较两个字符串的长度是有用的,因为这个信息已经在字符串类中可用了.如果您比较的两个字符串首先不是相同的长度,这可能会阻止进行昂贵的字符串比较.


C++ 11指定std :: string :: length的复杂性必须是常量:http://www.cplusplus.com/reference/string/string/length/
如果比较"a"与"ab",这些方法将返回-1.长度不同,但"a"出现在"ab"之前.因此,如果呼叫者关心订购,那么简单地比较长度是不可行的.

9> Darren Kopp..:

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

你可能正在寻找的是 _wcsnicmp


具有讽刺意味的是,微软的"宽字符代码"并非unicode干净,因为它们不处理unicode规范化.

10> Adam..:

我正在努力拼凑所有帖子的好答案,所以帮我编辑一下:

这是一个这样做的方法,虽然它确实转换了字符串,并且不是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为进行这种比较提供了相当好的实用工具.


这不是一个非常有效的解决方案 - 即使第一个字符不同,您也可以复制两个字符串并转换所有字符串.
如果你打算复制,为什么不通过价值而不是参考?

11> Wedge..:

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


没错,虽然过度缓冲缓冲区的危险性远远低于过度缓冲区.
`stricmp()`和`strnicmp()`不是POSIX标准的一部分:-(但是你可以在POSIX中找到`strcasecmp()`,`strcasecmp_l()`,`strncasecmp()`和`strncasecmp_l()` header`strings.h` :-)见[opengroup.org](http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/strings.h.html#tag_13_52)
@AdamRosenfield'更糟'取决于背景.在安全性方面,有时覆盖的重点是要重读.

12> Dean Harding..:

该Boost.String图书馆有很多算法做案例insenstive比较等.

你可以实现自己的,但为什么它已经完成了呢?


不,没有.
*"......当它已经完成时,为什么还要烦恼?"* - 如果你没有使用Boost怎么办?OP没有问题的标签.

13> vine'th..:
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宽字符.


我认为你需要在前面添加一个`str1.size()== str2.size()&&`,以便当str2是str1的前缀时不会超出界限.

14> Neutrino..:

对于我的基本不区分大小写的字符串比较需求,我不想使用外部库,也不希望单独的字符串类具有与我的所有其他字符串不兼容的不区分大小写的特征.

所以我想出的是:

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等第三方库即可.


如果你把它作为一个模板并使用basic_string 而不是单独的字符串/ wstring版本,你可能会创建一个函数吗?
单个函数模板将如何调用toupper或towupper而不求助于使用特殊化或宏,函数重载似乎比任何一种都更简单,更合适。

15> Brian Rodrig..:

std::lexicographical_compare:

// lexicographical_compare example
#include   // std::cout, std::boolalpha
#include   // std::lexicographical_compare
#include   // std::tolower

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

演示



16> kyb..:

简短又好看.没有其他依赖,而不是扩展 std C lib.

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

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

示例代码:

#include 
#include 
#include  //For strcasecmp(). Also could be found in 

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) <

输出:

true
true
true
true
true


奇怪的是,C++ std :: string没有ignore-case比较方法.

17> Andrew Grant..:

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

这种转换的最佳方法是在比较之前这样做.这使您在编码方案时具有很大的灵活性,您的实际比较运算符应该是无知的.

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



18> John Dibling..:

我编写了一个不区分大小写的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 > noCaseString;

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

                typedef std::basic_string > istring;

    --- */

    template
    struct char_traits_nocase : public std::char_traits
    {
        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
    struct char_traits_nocase : public std::char_traits
    {
        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 > istring;
    typedef std::basic_string > iwstring;



19> DavidS..:

这样做不使用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 
}

推荐阅读
可爱的天使keven_464
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有