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

请指出这些方法是否对unicode友好,以及它们的可移植性如何。


当前回答

我有使用Unicode库的International Components的丰富经验——它们非常强大,并提供了转换、区域支持、日期和时间呈现、大小写映射(您似乎不想要)和排序(其中包括大小写和重音不敏感比较)的方法。我只使用了c++版本的库,但它们似乎也有Java版本。

方法可以执行标准化的比较,如@Coincoin所述,甚至可以解释地区-例如(这是一个排序的例子,不是严格的相等),传统的西班牙语(在西班牙),字母组合“ll”在“l”和“m”之间排序,因此“lz”<“ll”<“ma”。

其他回答

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

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

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

利用标准char_traits。回想一下,std::string实际上是std::basic_string<char>的类型定义,或者更明确地说,std::basic_string<char, std::char_traits<char> >。char_traits类型描述了字符如何比较,如何复制,如何转换等。您所需要做的就是在basic_string上typedef一个新字符串,并为它提供您自己的自定义char_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期《本周大师》

只需使用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之前,所以苹果在球之前

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

boost的问题在于,你必须与boost相关联并依赖于boost。在某些情况下并不容易(例如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;
}

更新:c++ 14版本(#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);
                      });
}

c++ 20版本使用std::ranges:

#include <ranges>
#include <algorithm>
#include <string>

bool iequals(const std::string_view& lhs, const std::string_view& rhs) {
    auto to_lower{ std::ranges::views::transform(std::tolower) };
    return std::ranges::equal(lhs | to_lower, rhs | to_lower);
}