webentwicklung-frage-antwort-db.com.de

Map :: Find Operation unabhängig von Groß- und Kleinschreibung

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);
44
Ankur

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
    }
  };
63
Abhay

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;
}
23

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"!

6
Alex Martelli

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);
5
Manuel

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).

4
Alink

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.

3
Naveen

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
1
tibor.sz

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.

1
user208608

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);

Code auf Ideone

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.

0
honk

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;
}
0
James

Implementieren Sie die Funktion std :: less und vergleichen Sie sie, indem Sie beide in denselben Fall ändern.

0
Vivek