在C ++中进行不区分大小写的字符串比较而不将字符串转换为全部大写或全部小写的最佳方法是什么?
请指出这些方法是否支持Unicode,以及它们的可移植性。
strcasecmp
不是该标准的一部分,并且至少在一个常用的编译器中缺少。
在C ++中进行不区分大小写的字符串比较而不将字符串转换为全部大写或全部小写的最佳方法是什么?
请指出这些方法是否支持Unicode,以及它们的可移植性。
strcasecmp
不是该标准的一部分,并且至少在一个常用的编译器中缺少。
Answers:
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
}
利用标准char_traits
。回想一下,a std::string
实际上是的类型定义std::basic_string<char>
,或更明确地说是std::basic_string<char, std::char_traits<char> >
。该char_traits
类型描述字符如何进行比较,如何复制,如何进行转换等。您需要做的就是在上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”。
typedef std::basic_string<char, ci_char_traits<char> > istring
,不是typedef std::basic_string<char, std::char_traits<char> > string
。
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;
}
更新: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);
});
}
如果您使用的是POSIX系统,则可以使用strcasecmp。但是,此功能不是标准C的一部分,在Windows上也不可用。只要语言环境为POSIX,这将对8位字符执行不区分大小写的比较。如果语言环境不是POSIX,则结果是不确定的(因此它可能会进行本地化比较,也可能不会)。不能使用宽字符等效项。
失败的是,许多历史悠久的C库实现都具有函数stricmp()和strnicmp()。Windows上的Visual C ++将所有这些都重命名为下划线,因为它们不是ANSI标准的一部分,因此在该系统上,它们被称为_stricmp或_strnicmp。一些库可能还具有宽字符或多字节等效功能(通常命名为例如wcsicmp,mbcsicmp等)。
C和C ++都对国际化问题一无所知,因此,除了使用第三方库以外,没有很好的解决方案。如果需要用于C / C ++的强大库,请签出IBM ICU(Unicode国际组件)。ICU适用于Windows和Unix系统。
您是在说不区分大小写的哑比较还是完全规范化的Unicode比较?
愚蠢的比较不会找到可能相同但不等于二进制的字符串。
例:
U212B (ANGSTROM SIGN)
U0041 (LATIN CAPITAL LETTER A) + U030A (COMBINING RING ABOVE)
U00C5 (LATIN CAPITAL LETTER A WITH RING ABOVE).
都是等效的,但它们也具有不同的二进制表示形式。
就是说,Unicode规范化应该是强制性阅读,尤其是如果您计划支持韩文,Thaï和其他亚洲语言。
此外,IBM几乎为最优化的Unicode算法申请了专利,并使其公开可用。他们还维护一个实现:IBM ICU
对于字符串,boost :: iequals不兼容utf-8。您可以使用boost :: locale。
comparator<char,collator_base::secondary> cmpr;
cout << (cmpr(str1, str2) ? "str1 < str2" : "str1 >= str2") << endl;
对于非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;
}
您可以strcasecmp
在Unix或stricmp
Windows上使用。
到目前为止尚未提及的一件事是,如果您在这些方法中使用stl字符串,则首先比较两个字符串的长度很有用,因为在字符串类中已经可以使用此信息。如果您要比较的两个字符串最初的长度甚至不相同,则可以避免进行昂贵的字符串比较。
支持Unicode的Visual C ++字符串函数:http : //msdn.microsoft.com/zh-cn/library/cc194799.aspx
您可能正在寻找的是 _wcsnicmp
我正在尝试从所有帖子中收集一个好的答案,所以请帮助我编辑一下:
这是一种执行此操作的方法,尽管它确实可以转换字符串,并且不支持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提供了做这种比较的一个相当不错的实用性。
transform
在比较之前不是整个字符串
该Boost.String图书馆有很多算法做案例insenstive比较等。
您可以实现自己的实现,但是为什么要花些时间呢?
仅供参考,strcmp()
并且stricmp()
容易受到缓冲区溢出的影响,因为它们只会处理直到遇到空终止符为止。使用_strncmp()
和更安全_strnicmp()
。
stricmp()
而strnicmp()
不是POSIX标准的一部分:-(但是你可以找到strcasecmp()
,strcasecmp_l()
,strncasecmp()
并strncasecmp_l()
在POSIX头strings.h
:-)看看opengroup.org
见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;
}
std::tolower
仅当字符是ASCII编码时才有效。没有这样的保证std::string
-因此它很容易成为未定义的行为。
对于我的基本不区分大小写的字符串比较需求,我不想不必使用外部库,也不希望有一个不区分大小写特征的单独的字符串类,该类与我的所有其他字符串都不兼容。
所以我想出的是:
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这类第三方库即可。
简短又好。除了扩展的 std C lib ,没有其他依赖项。
strcasecmp(str1.c_str(), str2.c_str()) == 0
如果和相等,则返回true。
可能不存在,可能有类似物,等等。str1
str2
strcasecmp
stricmp
strcmpi
示例代码:
#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
stricmp
,strcmpi
,strcasecmp
,等谢谢。讯息已编辑。
cout << boolalpha
而不是my,bool2str
因为它隐式将bool转换为chars以进行流传输。
我写了一个不区分大小写的char_traits版本,用于std :: basic_string,以便生成一个std :: string,该字符串在使用内置std :: basic_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;
仅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相同
a先于b,所以苹果先于b
聚会晚了,但这是使用的变体,std::locale
因此可以正确处理土耳其语:
auto tolower = std::bind1st(
std::mem_fun(
&std::ctype<char>::tolower),
&std::use_facet<std::ctype<char> >(
std::locale()));
给您一个函子,该函子使用活动的语言环境将字符转换为小写,然后可以使用via std::transform
生成小写的字符串:
std::string left = "fOo";
transform(left.begin(), left.end(), left.begin(), tolower);
这也适用于wchar_t
基于字符串的字符串。
截至2013年初,由IBM维护的ICU项目是一个很好的答案。
ICU是“紧密跟踪行业标准的完整,可移植的Unicode库”。对于字符串比较的特定问题,Collation对象可以满足您的要求。
Mozilla项目于2012年中采用了ICU在Firefox中进行国际化;您可以在此处跟踪工程讨论,包括构建系统和数据文件大小的问题:
看起来上面的解决方案没有使用compare方法,而是再次实现了total,所以这是我的解决方案,希望它对您有用(工作正常)。
#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;
}
如果您不想使用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;
}
如果您必须更经常地将源字符串与其他字符串进行比较,一个不错的解决方案是使用正则表达式。
std::wstring first = L"Test";
std::wstring second = L"TEST";
std::wregex pattern(first, std::wregex::icase);
bool isEqual = std::regex_match(second, pattern);
error: conversion from 'const char [5]' to non-scalar type 'std::wstring {aka std::basic_string<wchar_t>}' requested
比较C ++中两个字符串的简单方法(已针对Windows进行了测试)是使用_stricmp
// Case insensitive (could use equivalent _stricmp)
result = _stricmp( string1, string2 );
如果要与std :: string一起使用,请参见以下示例:
std::string s1 = string("Hello");
if ( _stricmp(s1.c_str(), "HELLO") == 0)
std::cout << "The string are equals.";
有关更多信息,请参见:https : //msdn.microsoft.com/it-it/library/e0z9k731.aspx
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;
}
}
}
这可能会变得更加高效,但是这是一个体积庞大的版本,其所有内容都未显示。
并不是所有的便携式设备都可以,但是可以与我的计算机上的所有设备很好地工作(不知道,我是图片而不是文字)
比较仅由小写和大写字母组成的字符串的一种简单方法是进行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
}
}
std::stricmp
。否则,请阅读赫伯必须说的话。