问答中心分类: C++C ++中不区分大小写的字符串比较
0
Adam 提问 1小时 前

关闭.这个问题是基于意见的.它目前不接受答案。

关闭在4年前.

这个问题的答案是社区努力.编辑现有答案以改进这篇文章。它目前不接受新的答案或交互。

在 C++ 中进行不区分大小写的字符串比较而不将字符串转换为全部大写或全部小写的最佳方法是什么?
请说明这些方法是否对 Unicode 友好以及它们的可移植性。

Konrad Rudolph 回复 1小时 前

@[Adam](#11679):虽然这个变体在可用性方面很好,但在性能方面很差,因为它会创建不必要的副本。我可能会忽略一些东西,但我相信最好的(非 Unicode)方法是使用std::stricmp.否则,读什么药草不得不说.

Konrad Rudolph 回复 1小时 前

在 c 中,通常被迫将整个字符串放在上面,然后以这种方式进行比较 – 或者滚动您自己的比较:P

Konrad Rudolph 回复 1小时 前

后面的问题有一个更简单的答案:strcasecmp(至少对于 BSD 和 POSIX 编译器)stackoverflow.com/questions/9182912/…

Konrad Rudolph 回复 1小时 前

@Mσᶎ 这个问题也有这个答案,重要的警告是strcasecmp不是标准的一部分,并且至少在一个通用编译器中缺失。

28 Answers
0
wilhelmtell 回答 1小时 前

充分利用标准char_traits.回想一下std::string实际上是一个 typedefstd::basic_string<char>,或更明确地说,std::basic_string<char, std::char_traits<char> >.这char_traitstype 描述了字符如何比较,它们如何复制,它们如何转换等。您需要做的就是 typedef 一个新字符串basic_string,并为其提供您自己的自定义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 期.

Zan Lynx 回复 1小时 前

据我自己的实验所知,这会使您的新字符串类型与 std::string 不兼容。

Andreas Spindler 回复 1小时 前

当然可以——为了它自己的利益。不区分大小写的字符串是另一回事:typedef std::basic_string<char, ci_char_traits<char> > istring, 不是typedef std::basic_string<char, std::char_traits<char> > string.

njplumridge 回复 1小时 前

我知道这是直接从 GotW29 复制的,我认为这个被广泛引用的东西是正确的,但对我来说(在 Visual Studio 2005 上)这里的查找功能不起作用。它会导致 basic_string::find 溢出缓冲区并崩溃。我不得不改变“return s;”到“返回(n &gt;= 0 ? s : NULL);”。

Tim MB 回复 1小时 前

“你需要做的一切……”

Nathan 回复 1小时 前

compare() 方法对每个字符调用 toupper() 两次。可能应该缓冲 toupper() 的结果以减少 CPU 影响。

The Paramagnetic Croissant 回复 1小时 前

@Nathan 可能使用能够对代码执行基本 CSE 的编译器…

Erik Aronesty 回复 1小时 前

在这种微不足道的情况下,任何导致这种疯狂的语言结构都应该并且可以毫无遗憾地被抛弃。

Big McLargeHuge 回复 1小时 前

@ErikAronesty,您会推荐…?

srm 回复 1小时 前

@DaveKennedy 我认为 Erik 建议放弃人类语言,因为那些是迫使这种精神错乱的语言结构。 🙂

Pharap 回复 1小时 前

我想指出第二个参数find应该std::size_t, 不是int.不幸的是,我无法编辑,因为问题已被锁定。也可以实现findcompare按照eq,ltne.

celticminstrel 回复 1小时 前

尽管这确实使不区分大小写的字符串与std::string,使用范围构造函数在两者之间进行转换很简单。

celticminstrel 回复 1小时 前

补充跟进:乍一看,这种方法似乎与std::unordered_map. (或者至少,MSVC 标准库中字符串哈希的实现似乎没有将 char 特征用于任何事情。)因此,如果将其与std::unordered_map, 专业化std::hash可能也需要。

Evg 回复 1小时 前

std::toupper应该不是被召唤char直接,一个static_castunsigned char是需要的。

dodjango 回复 1小时 前

使用 C++17,我们可以使用 string_view 来使用 ci_char_traits

dodjango 回复 1小时 前

std::string s1{ "Ignore my CASE" }; std::string s2{ "ignore my case" }; std::basic_string_view<std::string::value_type, ci_char_traits> ci_view{ s1.c_str() }; std::cout << std::boolalpha << "\"" << s1 << "\" equals \"" << s2 << "\": " << (s1.compare(s2) == 0) << std::endl; std::cout << "\"" << s1 << "\" equals \"" << s2 << "\" (ignore casing): " << (ci_view.compare(s2.c_str()) == 0) << std::endl;

0
Timmmm 回答 1小时 前

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

更新:使用 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);
}
Gretchen 回复 1小时 前

实际上,boost 字符串库是一个只有头文件的库,所以不需要链接到任何东西。此外,您可以使用 boost 的 'bcp' 实用程序将字符串标题复制到源代码树中,因此您不需要完整的 boost 库。

Timmmm 回复 1小时 前

啊我不知道bcp,它看起来真的很有用。谢谢(你的)信息!

Deqing 回复 1小时 前

很高兴知道一个简单且非增强依赖的版本。

Behrouz.M 回复 1小时 前

@Anna 需要构建和链接 boost 的文本库。它使用 IBM ICU。

martian 回复 1小时 前

也可用于 C++11

Timmmm 回复 1小时 前

std::equal在 C++11 中不可用。

Evg 回复 1小时 前

std::tolower应该不是被召唤char直接,一个static_castunsigned char是需要的。

Timmmm 回复 1小时 前

@Evg 这太疯狂了。幸运的是,这似乎是“技术上未定义的行为,但无论如何每个人都在做理智的事情”之一,所以事实上它可能没问题。我检查了 GCC、Clang 和 ICC-O3他们都做着理智的事情。我想如果你做了一个编译器,它就不能编译很多现有的代码。

Jonatan Lindén 回复 1小时 前

在 C++14 版本中,只需将 lambda 函数的参数列表更改为[](unsigned char a, unsigned char b), 不static_cast有必要的。

Ben Cottrell 回复 1小时 前

@Timmmm 我冒昧地在这个答案中添加了 C++20 版本,因为我相信这里是最合适的,并且与这个线程中的其他答案相比,我觉得最类似于你的其他解决方案。

Aconcagua 回复 1小时 前

(后期)旁注:有相当多的语言对大写字母有不同的小写字母表示(例如希腊语Σ映射到ς在词尾和到σ别处)。虽然反过来也存在,(是在土耳其语中吗?)这种情况很少见,因此获得正确比较的机会更大toupper– 当然,如果您恰好对其中一种反例语言进行编码,则无济于事;)

Timmmm 回复 1小时 前

有趣,但这仅适用于 Unicode(在这种情况下,可能有官方算法)。此代码只是 ASCII。

0
Derek Park 回答 1小时 前

如果您在 POSIX 系统上,则可以使用strcasecmp.但是,此函数不是标准 C 的一部分,在 Windows 上也不可用。只要语言环境是 POSIX,这将对 8 位字符执行不区分大小写的比较。如果语言环境不是 POSIX,则结果未定义(因此它可能会进行本地化比较,也可能不会)。没有等效的宽字符。
如果做不到这一点,大量历史 C 库实现都具有函数 stricmp() 和 strnicmp()。 Windows 上的 Visual C++ 通过在它们前面加上下划线来重命名所有这些,因为它们不是 ANSI 标准的一部分,所以在那个系统上它们被称为_stricmp 或 _strnicmp.一些库还可能具有宽字符或多字节等效函数(通常命名为例如 wcsicmp、mbcsicmp 等)。
C 和 C++ 在很大程度上都对国际化问题一无所知,所以除了使用第三方库外,没有很好的解决方案。查看IBM ICU(Unicode 国际组件)如果您需要一个强大的 C/C++ 库。 ICU 适用于 Windows 和 Unix 系统。

0
Coincoin 回答 1小时 前

您是在谈论不区分大小写的愚蠢比较还是完全规范化的 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 重症监护室

Aconcagua 回复 1小时 前

迟到的评论,我知道…'都是等价的'可能不完全正确,尽管我不熟悉给定的情况——不过,德语的变音符号可以通过组合来创建a,o或者u分音符或直接通过字母ä,ö,ü然而两个点的距离(稍微)不同(直接字符更窄)……

0
Igor Milyakov 回答 1小时 前

boost::iequals 在字符串的情况下不兼容 utf-8。您可以使用提升::语言环境.

comparator<char,collator_base::secondary> cmpr;
cout << (cmpr(str1, str2) ? "str1 < str2" : "str1 >= str2") << endl;
  • 主要的——忽略重音和字符大小写,只比较基本字母。例如,“facade”和“Façade”是相同的。
  • 次要的——忽略字符大小写但考虑重音。 “立面”和“立面”是不同的,但“立面”和“立面”是相同的。
  • 第三——考虑大小写和重音:“Façade”和“façade”是不同的。忽略标点符号。
  • Quaternary – 考虑所有大小写、重音符号和标点符号。就 Unicode 表示而言,这些词必须相同。
  • 相同 – 作为四元,但也比较代码点。