Поддерживает ли метод map::find
поиск без учета регистра? У меня есть следующая карта:
map<string, vector<string> > directory;
и хотите, чтобы поиск ниже игнорировал регистр:
directory.find(search_string);
Поддерживает ли метод map::find
поиск без учета регистра? У меня есть следующая карта:
map<string, vector<string> > directory;
и хотите, чтобы поиск ниже игнорировал регистр:
directory.find(search_string);
По умолчанию это не так. Вам нужно будет указать пользовательский компаратор в качестве третьего аргумента. Следующий фрагмент поможет вам...
/************************************************************************/
/* 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++, если интересно.
ОБНОВЛЕНИЕ: в 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
}
};
const char&
существует? Когда это вообще срабатывает? Это деталь реализации STL?
- person Victor Sergienko; 22.06.2017
std::binary_function
устарел в C++11 и удален в C++17.
- person Andreas H.; 28.07.2017
Вот некоторые другие альтернативы, в том числе одна, которая работает значительно быстрее.
#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;
}
is x < 0
, у вас теперь есть логическое значение, так почему же вы тогда создаете целое число, которое затем должно быть преобразовано в логическое значение, чтобы соответствовать возвращаемому аргументу?
- person Peter Nimmo; 22.10.2013
bool
. Я отредактирую это.
- person Robert S. Barnes; 22.10.2013
Вы можете создать экземпляр std::map
с тремя параметрами: тип ключей, тип значений и функция сравнения -- строгий слабый порядок (по сути, функцию или функтор, ведущий себя как operator<
с точки зрения транзитивности и антирефлексивности) по вашему вкусу. Просто определите третий параметр, чтобы сделать «без учета регистра меньше чем» (например, с помощью <
в строчных строках, которые он сравнивает), и вы получите желаемую «карту без учета регистра»!
Я использую следующее:
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);
Для С++ 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;
}
Нет, вы не можете сделать это, используя find
, так как в этом случае будет несколько совпадений. Например, при вставке let вы сделали что-то вроде map["A"] = 1
и map["a"] = 2
, и теперь, если вы хотите, чтобы регистр не учитывал map.find("a")
, каково ожидаемое возвращаемое значение? Самый простой способ решить эту проблему - вставить строку в карту только в одном регистре (либо в верхнем, либо в нижнем регистре), а затем использовать тот же регистр при поиске.
std::map
поддерживает только один индекс, который может быть либо чувствительным к регистру, либо нечувствительным к регистру, но не обоим. Оттуда это простая ссылка на boost::multi_index
, которая поддерживает второй индекс.
- person MSalters; 26.11.2009
Если вы не хотите трогать тип карты (чтобы сохранить ее первоначальную простоту и эффективность), но не возражаете против использования более медленной функции поиска без учета регистра (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: Возможно, это была идея Роджера Пейта, но не уверен, так как некоторые детали были немного неверны (std::search?, прямой компаратор строк?)
Я хотел бы представить краткое решение без использования Boost или шаблонов. Поскольку C++11, вы также можете указать лямбда-выражение в качестве пользовательского компаратора для вашей карты. Для 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);
Для Windows 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 или нет, вам может понадобиться сравнивать строки по-разному. Вопросы и ответы дают хорошее начало.
Элемент Compare шаблона карты по умолчанию использует класс бинарного сравнения "less". Посмотрите на реализацию:
http://www.cplusplus.com/reference/std/functional/less/< /а>
Вероятно, вы можете создать свой собственный класс, производный от binary_function (от родительского класса к less), и выполнить такое же сравнение без учета регистра.
Протестировано:
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
Реализуйте функцию std::less и сравните, изменив оба на один и тот же регистр.