2009-11-26 9 views

risposta

59

Non per impostazione predefinita. Dovrai fornire un comparatore personalizzato come terzo argomento. Seguente frammento vi aiuterà a ...

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

Usalo come std::map< std::string, std::vector<std::string>, ci_less > myMap;

NOTA: std :: lexicographical_compare ha alcuni dettagli essenziali. Il confronto tra stringhe non è sempre semplice se si considerano locali. Vedere il thread this su c.l.C++ se interessato.

UPDATE: Con C++ 11 std::binary_function è obsoleto e non è necessario in quanto i tipi vengono dedotti automaticamente.

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

@Abhay. Grazie per la tua risposta. Tuttavia, non sono del tutto sicuro di come funzioni effettivamente (essendo relativamente nuovo a STL). Come si definisce un terzo parametro, che essendo una funzione di confronto o un oggetto funzione che fa "maiuscole e minuscole" meno di, in realtà fa un confronto insensibile al maiuscolo/minuscolo. Non dovremmo usare invece l'operatore ==. Come funziona davvero? Sono sicuro che sto prendendo in giro qualcosa. – Ankur

+1

@Ankur: std :: map viene solitamente implementato come una sorta di struttura ad albero. Il metodo find() usa la funzione di confronto che viene utilizzata per l'ordinamento (un elemento che non viene né prima né dopo è considerato uguale - a.k.a rigoroso ordinamento debole) dell'albero. Ciò consente di eseguire la ricerca in tempo O (logN) utilizzando la struttura ad albero della mappa. L'oggetto predicato è molto simile a una funzione di ordinamento; il suo operatore() accetta MyMap :: value_type & come riferimento, e restituisce true se l'elemento corrisponde al criterio di ricerca. – Abhay

+0

@Ankur: Inoltre, come per std :: firma della mappa std :: map , 'Compare' è un 'Ordinamento debole rigoroso' il cui tipo di argomento è Key. E l'albero è costruito usando questo ordine. Per vedere cosa è esattamente l'ordinamento rigoroso-debole in termini matematici, leggi questa semplice scrittura SGI @ http://www.sgi.com/tech/stl/StrictWeakOrdering.html – Abhay

2

No, non si può fare che usare find come in quel caso non ci sarà più corrispondenze. Ad esempio, durante l'inserimento ti è stato fatto qualcosa come map["A"] = 1 e map["a"] = 2 e ora se vuoi una maiuscole/minuscole map.find("a") qual è il valore di ritorno previsto? Il modo più semplice per risolverlo è inserire la stringa in mappa in un solo caso (maiuscolo o minuscolo) e quindi utilizzare lo stesso caso mentre si esegue il rilevamento.

+0

-1, fuorvianti. Il valore atteso per una mappa senza distinzione tra maiuscole e minuscole sarebbe semplicemente 2 (l'ultimo valore scritto per A "==" a "). Le mappe utilizzano rigorosi ordini deboli, che possono (possono) avere chiavi equivalenti.Qualsiasi chiave di questo tipo può essere utilizzata in modo intercambiabile – MSalters

+0

potrebbe essere fuorviante ... quello che stavo cercando di mostrare è che se hai una mappa sensibile al caso non c'è modo di avere una funzione find() che funzioni in modo insensibile. – Naveen

+0

Fair point, ma quel punto sarebbe stato molto Ad esempio se hai spiegato che 'std :: map' supporta un singolo indice, che può essere sia case-sensitive di case-insensitive ma non entrambi. Da lì è un link facile a' boost :: multi_index', che Supporta un secondo indice: – MSalters

1

L'elemento Confronta del modello di mappa assume come valore predefinito una classe di confronto binario "inferiore". Guardate l'attuazione:

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

È possibile probabile creare la propria classe che deriva da binary_function (classe genitore a meno) e fare lo stesso confronto, senza maiuscole e minuscole.

6

È possibile istanziare std::map con tre parametri: tipo di chiavi, tipo di valori, e funzione di confronto - un rigoroso debole ordinare (essenzialmente, una funzione o funtore comportarsi come operator< in termini di transitività e anti-riflessività) di tuo gradimento. Basta definire il terzo parametro per fare "case-insensitive less-than" (ad es. Con un < sulle stringhe in minuscolo che sta confrontando) e avrai la "mappa insensibile alle maiuscole e minuscole" che desideri!

0

Implementare std :: meno funzione e confrontare cambiando entrambi nello stesso caso.

4

Nel caso in cui non si vuole toccare il tipo di mappa (per mantenere la sua semplicità originale ed efficienza), ma non mente utilizzando una funzione più lento case-insensitive find (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: Forse era un'idea di Roger Pate, ma non è sicuro, dato che alcuni dettagli erano un po 'spenti (std :: search ?, comparatore diretto di stringhe?)

5

Io uso il seguente:

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

+1 Per il fresco, ma wow, è brutto. Rendilo un funtore come nel mio esempio e digita la mappa. –

22

Qui ci sono alcune altre alternative, tra cui uno che esegue molto più velocemente.

#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

Il '? 1: 0 'parte nel primo metodo è sciocco e inutile ma per il resto ottima risposta. – jcoffland

+0

FYI, strcasecmp() non è in o . È in ma non penso che Windows ce l'abbia. – jcoffland

+1

sicuramente nel momento in cui dici 'è x <0', ora hai un valore booleano, quindi perché stai producendo un numero intero che dovrà poi essere convertito in bool in modo che corrisponda all'argomento return? –

1

testati:

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 
0

Per C++ 11 e oltre:

#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; 
} 
Problemi correlati