在c++中进行不区分大小写字符串比较而不将字符串转换为全大写或全小写的最佳方法是什么?
请指出这些方法是否对unicode友好,以及它们的可移植性如何。
在c++中进行不区分大小写字符串比较而不将字符串转换为全大写或全小写的最佳方法是什么?
请指出这些方法是否对unicode友好,以及它们的可移植性如何。
当前回答
Boost::iequals在string的情况下不兼容utf-8。 你可以使用boost::locale。
comparator<char,collator_base::secondary> cmpr;
cout << (cmpr(str1, str2) ? "str1 < str2" : "str1 >= str2") << endl;
Primary -- ignore accents and character case, comparing base letters only. For example "facade" and "Façade" are the same. Secondary -- ignore character case but consider accents. "facade" and "façade" are different but "Façade" and "façade" are the same. Tertiary -- consider both case and accents: "Façade" and "façade" are different. Ignore punctuation. Quaternary -- consider all case, accents, and punctuation. The words must be identical in terms of Unicode representation. Identical -- as quaternary, but compare code points as well.
其他回答
可以在Unix上使用strcasecmp,在Windows上使用stricmp。
到目前为止还没有提到的一件事是,如果您使用这些方法使用stl字符串,首先比较两个字符串的长度是有用的,因为这个信息已经在string类中提供给您了。如果您正在比较的两个字符串的长度一开始就不相同,这可以防止进行代价高昂的字符串比较。
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);
}
对于非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;
}
截至2013年初,由IBM维护的ICU项目是一个很好的答案。
http://site.icu-project.org/
ICU是一个“完整的、可移植的Unicode库,密切跟踪行业标准”。对于字符串比较的特定问题,Collation对象将执行您想要的操作。
2012年年中,Mozilla项目在Firefox中采用了ICU进行国际化;您可以在这里跟踪工程讨论,包括构建系统和数据文件大小的问题:
https://groups.google.com/forum/#!topic/mozilla.dev.platform/sVVpS2sKODw https://bugzilla.mozilla.org/show_bug.cgi?id=724529(跟踪) https://bugzilla.mozilla.org/show_bug.cgi?id=724531(构建系统)
如果是在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系统。