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

时间:2008-08-14 20:01:28

标签: c++ string

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

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

31 个答案:

答案 0 :(得分:306)

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
}

答案 1 :(得分:116)

答案 2 :(得分:65)

增强的问题在于你必须与升级相关联并依赖于升压。在某些情况下并不容易(例如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版本(#include <algorithm>):

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

答案 3 :(得分:53)

如果您使用的是POSIX系统,则可以使用strcasecmp。但是,此功能不是标准C的一部分,也不适用于Windows。这将对8位字符执行不区分大小写的比较,只要语言环境是POSIX即可。如果语言环境不是POSIX,则结果是未定义的(因此可能会进行本地化比较,也可能不会)。没有宽字符的等价物。

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

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

答案 4 :(得分:52)

您是在谈论一种不区分大小写的比较或完全规范化的Unicode比较吗?

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

示例:

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

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

尽管如此,Unicode Normalization应该是强制性阅读,特别是如果你计划支持Hangul,Thaï和其他亚洲语言。

此外,IBM为大多数优化的Unicode算法提供了专利,并将其公之于众。他们还维持一个实施:IBM ICU

答案 5 :(得分:31)

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

comparator<char,collator_base::secondary> cmpr;
cout << (cmpr(str1, str2) ? "str1 < str2" : "str1 >= str2") << endl;
  • 主要 - 忽略重音和字符大小写,仅比较基本字母。例如“facade”和“Façade”是相同的。
  • 辅助 - 忽略字符大小写但考虑重音。 “立面”和“立面”不同,但“外墙”和“立面”是相同的。
  • 第三级 - 考虑案例和口音:“外墙”和“立面”是不同的。忽略标点符号。
  • 第四纪 - 考虑所有案例,重音和标点符号。在Unicode表示方面,单词必须相同。
  • 相同 - 作为四元组,但也比较代码点。

答案 6 :(得分:28)

我对非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;
}

答案 7 :(得分:16)

您可以在Unix上使用strcasecmp,在Windows上使用stricmp

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

答案 8 :(得分:14)

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

您可能正在寻找的是_wcsnicmp

答案 9 :(得分:13)

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

这是一种方法,虽然它确实转换了字符串,并且不是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 (International Components for Unicode)

此外, boost :: iequals 为进行此类比较提供了相当不错的实用工具。

答案 10 :(得分:11)

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

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

答案 11 :(得分:10)

仅供参考,strcmp()stricmp()容易受到缓冲区溢出的影响,因为它们只会在它们遇到空终止符之前一直处理。使用_strncmp()_strnicmp()更安全。

答案 12 :(得分:9)

std::equal(str1.begin(), str1.end(), str2.begin(), [](auto a, auto b){return std::tolower(a)==std::tolower(b);})

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

答案 13 :(得分:9)

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

所以我想出的是:

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这样的第三方库。

答案 14 :(得分:7)

请参阅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 () {
    char foo[] = "Apple";
    char 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, foo + 5, bar, bar + 9);
    std::cout << '\n';

    std::cout << "Using mycomp as comparison object: ";
    std::cout << std::lexicographical_compare(foo, foo + 5, bar, bar + 9, mycomp);
    std::cout << '\n';

    return 0;
}

Demo

答案 15 :(得分:7)

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

strcasecmp(str1.c_str(), str2.c_str()) == 0
如果str1str2相等,则

返回 true strcasecmp可能不存在,可能有类似stricmpstrcmpi等。

示例代码:

#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

答案 16 :(得分:6)

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

此转换的最佳方法是在比较之前执行此操作。这使您在编码方案时具有很大的灵活性,您的实际比较运算符应该不知道。

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

答案 17 :(得分:6)

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

答案 18 :(得分:6)

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

std::string str1 ="aBcD";
std::string str2 = "AbCd";;
if (strcasecmp(str1.c_str(), str2.c_str()) == 0)
{
    //case insensitive equal 
}

答案 19 :(得分:4)

使用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之前,所以苹果来到球前

答案 20 :(得分:4)

我使用International Components for Unicode libraries有很好的经验 - 它们非常强大,并提供转换方法,区域设置支持,日期和时间渲染,案例映射(您似乎不需要),和collation,包括不区分大小写和重音比较(以及更多)。我只使用了C ++版本的库,但它们似乎也有Java版本。

存在执行@ Coincoin所引用的规范化比较的方法,甚至可以考虑区域设置 - 例如(这是一个排序示例,而不是严格相等),传统上用西班牙语(在西班牙),字母组合“ll “在”l“和”m“之间排序,所以”lz“&lt; “ll”&lt; “MA”。

答案 21 :(得分:2)

晚会,但这是一个使用std::locale的变体,因此正确处理土耳其语:

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

为您提供了一个使用活动区域设置将字符转换为小写的仿函数,然后您可以通过std::transform使用它来生成小写字符串:

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

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

答案 22 :(得分:2)

请注意您最终选择的方法,如果该方法恰好包括使用某些答案提示的strcmp

strcmp通常不适用于Unicode数据。一般来说,它甚至不能用于基于字节的Unicode编码,例如utf-8,因为strcmp只进行每字节字节比较,而用utf-8编码的Unicode代码点可能需要1个以上字节。唯一特定的Unicode情况strcmp正确处理的是当使用基于字节的编码编码的字符串仅包含低于U + 00FF的代码点时 - 那么每字节的字节比较就足够了。

答案 23 :(得分:2)

截至2013年初,由IBM维护的ICU项目对此非常好。

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

ICU是一个“完整,可移植的Unicode库,可以跟踪行业标准。”对于字符串比较的特定问题,Collat​​ion对象可以执行您想要的操作。

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

答案 24 :(得分:2)

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

#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;
}

答案 25 :(得分:1)

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

#include <iostream>

struct iequal
{
    bool operator()(int c1, int c2) const
    {
        // case insensitive comparison of two characters.
        return std::toupper(c1) == std::toupper(c2);
    }
};

bool iequals(const std::string& str1, const std::string& str2)
{
    // use std::equal() to compare range of characters using the functor above.
    return std::equal(str1.begin(), str1.end(), str2.begin(), iequal());
}

int main(void)
{
    std::string str_1 = "HELLO";
    std::string str_2 = "hello";

    if(iequals(str_1,str_2))
    {
        std::cout<<"String are equal"<<std::endl;   
    }

    else
    {
        std::cout<<"String are not equal"<<std::endl;
    }


    return 0;
}

答案 26 :(得分:0)

如果你有一个字符串向量,例如:

std::sort(std::begin(myvector), std::end(myvector), [](std::string const &a, std::string const &b)
{
    return std::lexicographical_compare(std::begin(a), std::end(a), std::begin(b), std::end(b), [](std::string::value_type a, std::string::value_type b)
    {
        return std::tolower(a) < std::tolower(b); //case-insensitive
    });
});

http://ideone.com/N6sq6X

答案 27 :(得分:0)

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

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

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

答案 28 :(得分:0)

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

    private ArrayList prepareData(){

    ArrayList android_version = new ArrayList<>();
    for(int i=0;i<android_version_names.length;i++){
        AndroidVersion androidVersion = new AndroidVersion();
        androidVersion.setAndroid_version_name(android_version_names[i]);
        androidVersion.setAndroid_image_url(android_image_urls[i]);
        android_version.add(androidVersion);
    }
    return android_version;
}

如果您希望使用std :: string,请举例:

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

有关详情,请访问:https://msdn.microsoft.com/it-it/library/e0z9k731.aspx

答案 29 :(得分:-1)

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;
        }
      }
}

这可能会变得更有效率,但这是一个庞大的版本,所有的位都没有。

不是那么便携,但适用于我电脑上的任何东西(不知道,我的照片不是文字)

答案 30 :(得分:-3)

比较只有小写和大写字符不同的字符串的简单方法是进行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
    }
}