Unterstützt die map :: find-Methode die Suche ohne Berücksichtigung der Groß- und Kleinschreibung?
Ich habe eine Karte wie folgt
map<string,vector<string> > directory;
und möchten, dass die unten stehende Suche den Fall ignoriert.
directory.find(search_string);
Standardmäßig nicht. Als drittes Argument müssen Sie einen benutzerdefinierten Vergleicher angeben. Der folgende Ausschnitt hilft Ihnen dabei ...
/************************************************************************/
/* 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
}
};
Verwenden Sie es wie std::map< std::string, std::vector<std::string>, ci_less > myMap;
NOTE: std :: lexicographical_compare hat einige winzige Details. Der Zeichenfolgenvergleich ist nicht immer einfach, wenn Sie die Ländereinstellungen berücksichtigen. Siehe this thread auf c.l.c ++ bei Interesse.
UPDATE: Mit C++ 11 ist std::binary_function
veraltet und nicht erforderlich, da die Typen automatisch abgeleitet werden.
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
}
};
Hier sind einige andere Alternativen, einschließlich einer, die wesentlich schneller arbeitet.
#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;
}
Sie können std::map
mit drei Parametern instanziieren: Schlüsseltyp, Wertetyp und Vergleichsfunktion - eine strikte schwache Reihenfolge (im Wesentlichen eine Funktion oder eine Funktion, die sich wie operator<
in Bezug auf Transitivität und Antireflexivität). Definieren Sie einfach den dritten Parameter für "case-insensitive less-than" (z. B. durch einen <
in den unterstrichenen Strings, die verglichen werden), und Sie haben die gewünschte "case-insensitive Karte"!
Ich benutze folgendes:
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);
Für den Fall, dass Sie den Kartentyp nicht berühren möchten (um die ursprüngliche Einfachheit und Effizienz zu erhalten), sollten Sie jedoch eine langsamere Suchfunktion (O (N)) verwenden, bei der die Groß-/Kleinschreibung nicht berücksichtigt wird:
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: Vielleicht war es die Idee von Roger Pate, aber nicht sicher, da einige Details etwas abschreckend waren (std :: search?, Direkter Zeichenfolgenvergleicher).
Nein, Sie können dies nicht mit find
tun, da in diesem Fall mehrere Übereinstimmungen auftreten. Beim Einfügen können Sie beispielsweise etwas wie map["A"] = 1
und map["a"] = 2
ausgeführt haben. Wenn Sie nun eine Groß-/Kleinschreibung ohne Berücksichtigung von map.find("a")
wünschen, wie lautet der erwartete Rückgabewert? Der einfachste Weg, dieses Problem zu lösen, besteht darin, den String in nur einem Fall (entweder in Groß- oder Kleinschreibung) in die Map einzufügen und dann den gleichen Fall zu verwenden, während die Suche ausgeführt wird.
Geprüft:
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
Das Compare-Element der Kartenvorlage verwendet standardmäßig eine binäre Vergleichsklasse "less". Schauen Sie sich die Implementierung an:
http://www.cplusplus.com/reference/std/functional/less/
Sie können wahrscheinlich eine eigene Klasse erstellen, die von binary_function (der übergeordneten Klasse in less) abgeleitet ist, und den gleichen Vergleich ohne Berücksichtigung der Groß- und Kleinschreibung durchführen.
Ich möchte eine kurze Lösung vorstellen, ohne Boost oder Vorlagen zu verwenden. Seit C++ 11 können Sie auch Lambda-Ausdruck als benutzerdefinierten Komparator für Ihre Karte angeben. Für ein POSIX-kompatibles System könnte die Lösung folgendermaßen aussehen:
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);
Für Window existiert strcasecmp()
nicht, aber Sie können stattdessen _stricmp()
verwenden:
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);
Hinweis: Abhängig von Ihrem System und davon, ob Sie Unicode unterstützen müssen oder nicht, müssen Sie möglicherweise Zeichenfolgen auf andere Weise vergleichen. Diese Q & A gibt einen guten Start.
Für C++ 11 und höher:
#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;
}
Implementieren Sie die Funktion std :: less und vergleichen Sie sie, indem Sie beide in denselben Fall ändern.