map::find
方法是否支持不区分大小写的搜索?我的地图如下:
map<string, vector<string> > directory;
并希望以下搜索忽略大小写:
directory.find(search_string);
答案 0 :(得分:64)
默认情况下不会。您必须提供自定义比较器作为第三个参数。以下片段将帮助您...
/************************************************************************/
/* 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
}
};
像std::map< std::string, std::vector<std::string>, ci_less > myMap;
注意:std :: lexicographical_compare有一些细节。如果考虑区域设置,字符串比较并不总是很简单。如果感兴趣,请参阅c.l.c ++上的this主题。
更新:使用C ++ 11 std::binary_function
已弃用,因为类型是自动推断的,所以不需要。
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
}
};
答案 1 :(得分:23)
以下是其他一些替代方案,包括表现更快的方案。
#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;
}
答案 2 :(得分:6)
您可以使用三个参数来实例化std::map
:键的类型,值的类型和比较函数 - 严格弱排序< / em>(基本上,一个函数或仿函数在传递性和反反射性方面表现得像operator<
)。只需定义第三个参数来执行“不区分大小写的情况”(例如,通过它所比较的小写字符串上的<
)并且您将拥有所需的“不区分大小写的地图”!
答案 3 :(得分:5)
我使用以下内容:
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);
答案 4 :(得分:4)
如果您不想触摸地图类型(保持其原始的简单性和效率),但不介意使用较慢的不区分大小写的查找功能(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:也许这是Roger Pate的想法,但不确定,因为有些细节有点过时(std :: search ?,直接字符串比较器?)
答案 5 :(得分:3)
不,你不能使用find
这样做,因为在这种情况下会有多个匹配。例如,插入时允许您执行map["A"] = 1
和map["a"] = 2
之类的操作,现在如果您想要不区分大小写map.find("a")
,那么预期的返回值是多少?解决这个问题的最简单方法是在一个案例(大写或小写)中将字符串插入映射,然后在执行查找时使用相同的大小写。
答案 6 :(得分:1)
地图模板的Compare元素默认为二进制比较类“less”。看看实现:
http://www.cplusplus.com/reference/std/functional/less/
您可以创建自己的类,该类派生自binary_function(父类为less),并且在不区分大小写的情况下进行相同的比较。
答案 7 :(得分:1)
测试:
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
答案 8 :(得分:1)
对于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;
}
答案 9 :(得分:0)
实现std :: less函数并通过将两者都改为相同的情况进行比较。
答案 10 :(得分:0)
我想提出一个简短的解决方案,而不使用Boost或模板。由于C++11,您还可以提供lambda expression作为地图的自定义比较器。对于兼容POSIX的系统,解决方案如下所示:
auto comp = [](const std::string& s1, const std::string& s2) {
return strcasecmp(s1.c_str(), s2.c_str()) < 0;
};
std::map<std::string, std::vector<std::string>, decltype(comp)> directory(comp);
对于Window,strcasecmp()
不存在,但是您可以改用_stricmp()
:
auto comp = [](const std::string& s1, const std::string& s2) {
return _stricmp(s1.c_str(), s2.c_str()) < 0;
};
std::map<std::string, std::vector<std::string>, decltype(comp)> directory(comp);
注意:根据您的系统以及是否必须支持Unicode,您可能需要以其他方式比较字符串。 This Q&A开了个好头。