web-dev-qa-db-fra.com

Rendre la carte :: trouver une opération insensible à la casse

La méthode map :: find prend-elle en charge la recherche non sensible à la casse?
J'ai une carte comme suit 

map<string,vector<string> > directory;  

et veulent que la recherche ci-dessous ignore le cas. 

directory.find(search_string);
44
Ankur

Ce n'est pas par défaut. Vous devrez fournir un comparateur personnalisé en tant que troisième argument. Après l'extrait vous aidera ...

  /************************************************************************/
  /* 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
    }
  };

Utilisez-le comme std::map< std::string, std::vector<std::string>, ci_less > myMap;

NOTE: std :: lexicographical_compare a quelques détails concrets. La comparaison de chaînes n'est pas toujours simple si l'on considère les paramètres régionaux. Voir this thread sur c.l.c ++ si cela vous intéresse.

UPDATE: avec C++ 11, std::binary_function est déconseillé et n'est pas nécessaire, car les types sont déduits automatiquement.

  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

Voici quelques autres alternatives, y compris une qui fonctionne beaucoup plus rapidement.

#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
Robert S. Barnes

Vous pouvez instancier std::map avec trois paramètres: type de clé, type de valeur et fonction de comparaison - un strict faible ordering (essentiellement une fonction ou un foncteur se comportant comme operator< en termes de transitivité et anti-réflexivité) de votre goût. Il suffit de définir le troisième paramètre pour faire "inférieur à" sans distinction de casse (par exemple, avec un < sur les chaînes minuscules comparées) et vous obtiendrez la "carte sans distinction de casse"!

6
Alex Martelli

J'utilise les éléments suivants:

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

Si vous ne souhaitez pas toucher au type de carte (pour conserver sa simplicité et son efficacité d'origine), utilisez une fonction de recherche plus lente et insensible à la casse (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: Peut-être que c'était l'idée de Roger Pate, mais je ne suis pas sûr, certains détails étaient un peu décalés (std :: search?, Comparateur de chaîne directe?)

4
Alink

Non, vous ne pouvez pas faire cela en utilisant find car dans ce cas, il y aura plusieurs correspondances. Par exemple, lors de l’insertion vous permet d’effectuer quelque chose comme map["A"] = 1 et map["a"] = 2 et maintenant, si vous souhaitez une map.find("a") insensible à la casse, quelle est la valeur de retour attendue? Le moyen le plus simple de résoudre ce problème consiste à insérer la chaîne dans la carte dans un seul cas (majuscule ou minuscule), puis à utiliser le même cas tout en effectuant la recherche.

3
Naveen

Testé:

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

L'élément Compare du modèle de carte utilise par défaut une classe de comparaison binaire "moins". Regardez l'implémentation:

http://www.cplusplus.com/reference/std/functional/less/

Vous pouvez probablement créer votre propre classe dérivée de binary_function (la classe parente à less) et faire la même comparaison sans distinction de casse.

1
user208608

Pour C++ 11 et au-delà:

#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

Implémentez la fonction std :: less et comparez en changeant les deux cas pour le même cas.

0
Vivek