C++ 如何使 map::find 操作不区分大小写?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1801892/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How can I make the map::find operation case insensitive?
提问by Ankur
Does the map::find
method support case insensitive search? I have a map as follows:
该map::find
方法是否支持不区分大小写的搜索?我有一张地图如下:
map<string, vector<string> > directory;
and want the below search to ignore case:
并希望以下搜索忽略大小写:
directory.find(search_string);
回答by Abhay
It does not by default. You will have to provide a custom comparator as a third argument. Following snippet will help you...
默认情况下不会。您必须提供一个自定义比较器作为第三个参数。以下代码段将帮助您...
/************************************************************************/
/* Comparator for case-insensitive comparison in STL assos. containers */
/************************************************************************/
struct ci_less : std::binary_function<std::string, std::string, bool>
{
// case-independent (ci) compare_less binary function
struct nocase_compare : public std::binary_function<unsigned char,unsigned char,bool>
{
bool operator() (const unsigned char& c1, const unsigned char& c2) const {
return tolower (c1) < tolower (c2);
}
};
bool operator() (const std::string & s1, const std::string & s2) const {
return std::lexicographical_compare
(s1.begin (), s1.end (), // source range
s2.begin (), s2.end (), // dest range
nocase_compare ()); // comparison
}
};
Use it like std::map< std::string, std::vector<std::string>, ci_less > myMap;
使用它就像 std::map< std::string, std::vector<std::string>, ci_less > myMap;
NOTE: std::lexicographical_compare has some nitty-gritty details. String comparison isn't always straightforward if you consider locales. See thisthread on c.l.c++ if interested.
注意: std::lexicographical_compare 有一些细节。如果您考虑语言环境,字符串比较并不总是那么简单。如果有兴趣,请参阅clc++ 上的此线程。
UPDATE: With C++11 std::binary_function
is deprecated and is unnecessary as the types are deduced automatically.
更新:std::binary_function
不推荐使用 C++11并且不需要,因为类型是自动推导的。
struct ci_less
{
// case-independent (ci) compare_less binary function
struct nocase_compare
{
bool operator() (const unsigned char& c1, const unsigned char& c2) const {
return tolower (c1) < tolower (c2);
}
};
bool operator() (const std::string & s1, const std::string & s2) const {
return std::lexicographical_compare
(s1.begin (), s1.end (), // source range
s2.begin (), s2.end (), // dest range
nocase_compare ()); // comparison
}
};
回答by Robert S. Barnes
Here are some other alternatives, including one which performs significantly faster.
以下是其他一些替代方案,其中包括一种性能明显更快的替代方案。
#include <map>
#include <string>
#include <cstring>
#include <iostream>
#include <boost/algorithm/string.hpp>
using std::string;
using std::map;
using std::cout;
using std::endl;
using namespace boost::algorithm;
// recommended in Meyers, Effective STL when internationalization and embedded
// NULLs aren't an issue. Much faster than the STL or Boost lex versions.
struct ciLessLibC : public std::binary_function<string, string, bool> {
bool operator()(const string &lhs, const string &rhs) const {
return strcasecmp(lhs.c_str(), rhs.c_str()) < 0 ;
}
};
// Modification of Manuel's answer
struct ciLessBoost : std::binary_function<std::string, std::string, bool>
{
bool operator() (const std::string & s1, const std::string & s2) const {
return lexicographical_compare(s1, s2, is_iless());
}
};
typedef map< string, int, ciLessLibC> mapLibc_t;
typedef map< string, int, ciLessBoost> mapBoost_t;
int main(void) {
mapBoost_t cisMap; // change to test other comparitor
cisMap["foo"] = 1;
cisMap["FOO"] = 2;
cisMap["bar"] = 3;
cisMap["BAR"] = 4;
cisMap["baz"] = 5;
cisMap["BAZ"] = 6;
cout << "foo == " << cisMap["foo"] << endl;
cout << "bar == " << cisMap["bar"] << endl;
cout << "baz == " << cisMap["baz"] << endl;
return 0;
}
回答by Manuel
I use the following:
我使用以下内容:
bool str_iless(std::string const & a,
std::string const & b)
{
return boost::algorithm::lexicographical_compare(a, b,
boost::is_iless());
}
std::map<std::string, std::string,
boost::function<bool(std::string const &,
std::string const &)>
> case_insensitive_map(&str_iless);
回答by Alex Martelli
You can instantiate std::map
with threeparameters: type of keys, type of values, and comparison function-- a strict weak ordering(essentially, a function or functor behaving like operator<
in terms of transitivity and anti-reflexivity) of your liking. Just define the third parameter to do "case-insensitive less-than" (e.g. by a <
on the lowercased strings it's comparing) and you'll have the "case-insensitive map" you desire!
您可以std::map
使用三个参数实例化:键类型、值类型和比较函数——您喜欢的严格弱排序(本质上,函数或函子的行为类似于operator<
传递性和反自反性)。只需定义第三个参数来执行“不区分大小写的小于”(例如,通过<
它比较的小写字符串上的a ),您将拥有您想要的“不区分大小写的映射”!
回答by Alink
In case you don't want to touch the map type (to keep it's original simplicity and efficiency), but don't mind using a slower case-insensitive find function (O(N)):
如果您不想触及地图类型(以保持其原始的简单性和效率),但不介意使用较慢的不区分大小写的查找函数 (O(N)):
string to_lower(string s) {
transform(s.begin(), s.end(), s.begin(), (int(*)(int)) tolower );
return s;
}
typedef map<string, int> map_type;
struct key_lcase_equal {
string lcs;
key_lcase_equal(const string& s) : lcs(to_lower(s)) {}
bool operator()(const map_type::value_type& p) const {
return to_lower(p.first) == lcs;
}
};
map_type::iterator find_ignore_case(map_type& m, const string& s) {
return find_if(m.begin(), m.end(), key_lcase_equal(s));
}
PS: Maybe it was Roger Pate's idea, but not sure, since some details were a bit off (std::search?, direct string comparator?)
PS:也许这是 Roger Pate 的想法,但不确定,因为有些细节有点偏离(std::search?,直接字符串比较器?)
回答by Naveen
No, you can not do that using find
as in that case there will be multiple matches. For example, while inserting lets you have done something like map["A"] = 1
and map["a"] = 2
and now if you want a case insensitive map.find("a")
what is the expected return value? The simplest way to solve this would be insert the string into map in only one case (either upper or lower case) and then using the same case while doing the find.
不,您不能这样做,find
因为在这种情况下会有多个匹配项。例如,在插入时,您已经完成了类似的操作map["A"] = 1
,map["a"] = 2
现在如果您想要不区分大小写,map.find("a")
那么预期的返回值是多少?解决此问题的最简单方法是将字符串仅以一种情况(大写或小写)插入到 map 中,然后在进行查找时使用相同的情况。
回答by James
For C++11 and beyond:
对于 C++11 及更高版本:
#include <strings.h>
#include <map>
#include <string>
namespace detail
{
struct CaseInsensitiveComparator
{
bool operator()(const std::string& a, const std::string& b) const noexcept
{
return ::strcasecmp(a.c_str(), b.c_str()) < 0;
}
};
} // namespace detail
template <typename T>
using CaseInsensitiveMap = std::map<std::string, T, detail::CaseInsensitiveComparator>;
int main(int argc, char* argv[])
{
CaseInsensitiveMap<int> m;
m["one"] = 1;
std::cout << m.at("ONE") << "\n";
return 0;
}
回答by James
The Compare element of the map template defaults to a binary comparison class "less". Look at the implementation:
地图模板的 Compare 元素默认为二进制比较类“less”。看实现:
http://www.cplusplus.com/reference/std/functional/less/
http://www.cplusplus.com/reference/std/functional/less/
You can likely create your own class that derives from binary_function (the parent class to less) and do the same comparison without case sensitivity.
您可能会创建自己的类,该类派生自 binary_function(父类到less),并在不区分大小写的情况下进行相同的比较。
回答by sz9
Tested:
测试:
template<typename T>
struct ci_less:std::binary_function<T,T,bool>
{ bool operator() (const T& s1,const T& s2) const { return boost::ilexicographical_compare(s1,s2); }};
...
map<string,int,ci_less<string>> x=boost::assign::map_list_of
("One",1)
("Two",2)
("Three",3);
cout << x["one"] << x["TWO"] <<x["thrEE"] << endl;
//Output: 123
回答by Vivek
Implement std::less function and compare by changing both to same case.
实现 std::less 函数并通过将两者更改为相同大小写来进行比较。