2009-11-26 13 views

Respuesta

59

No lo hace de forma predeterminada. Deberá proporcionar un comparador personalizado como tercer argumento. Siguiente fragmento le ayudará 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 
    } 
    }; 

Se usa como std::map< std::string, std::vector<std::string>, ci_less > myMap;

NOTA: std :: lexicographical_compare tiene algunos detalles esenciales. La comparación de cadenas no siempre es sencilla si se consideran configuraciones regionales. Ver this thread en c.l.C++ si está interesado.

ACTUALIZACIÓN: Con C++ 11 std::binary_function está en desuso y no es necesario ya que los tipos se deducen automáticamente.

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. Gracias por tu respuesta. Sin embargo, no estoy del todo seguro de cómo funciona esto realmente (siendo relativamente nuevo en STL). ¿Cómo es que la definición de un tercer parámetro, que es una función de comparación o un objeto de función que hace "insensible a mayúsculas y minúsculas" menor que, en realidad hace una comparación insensible a mayúsculas y minúsculas. ¿No deberíamos usar el operador == en su lugar? ¿Cómo funciona esto realmente? Estoy seguro de que estoy perdiendo algo. – Ankur

+1

@Ankur: std :: map generalmente se implementa como algún tipo de estructura de árbol. El método find() usa la función de comparación que se usa para ordenar (un elemento que no viene antes ni después se considera igual - a.k.una ordenación débil estricta) el árbol. Esto permite que la búsqueda se realice en tiempo O (logN) usando la estructura de árbol del mapa. El objeto predicado es muy parecido a una función de clasificación; su operador() toma un MyMap :: value_type & como referencia, y devuelve verdadero si el elemento coincide con su criterio de búsqueda. – Abhay

+0

@Ankur: Además, de acuerdo con la firma std :: map std :: map , 'Compare' es un 'Strict Weak Ordering' cuyo tipo de argumento es Key. Y el árbol está construido usando este orden. Para ver qué es exactamente el ordenamiento estricto-débil en términos matemáticos, lea este artículo simplificado de SGI @ http://www.sgi.com/tech/stl/StrictWeakOrdering.html – Abhay

2

No, no se puede hacer que el uso de find como en ese caso habrá varias coincidencias. Por ejemplo, al insertar le permite hacer algo como map["A"] = 1 y map["a"] = 2 y ahora, si desea un caso insensible map.find("a"), ¿cuál es el valor de retorno esperado? La forma más sencilla de resolver esto sería insertar la cadena en el mapa en un solo caso (ya sea en mayúscula o minúscula) y luego usar el mismo estuche al hacer el hallazgo.

+0

-1, engañosa. El valor esperado para un mapa insensible a mayúsculas y minúsculas sería simplemente 2 (último valor escrito para A "==" a "). Los mapas usan ordenamientos débiles estrictos, que (pueden) tener claves equivalentes. Cualquiera de estas claves se puede usar de forma intercambiable – MSalters

+0

puede ser engañoso ... lo que estaba tratando de mostrar es que si tienes un mapa sensible a mayúsculas y minúsculas no hay forma de tener una función find() que funcione de manera insensible a las mayúsculas. – Naveen

+0

Punto justo, pero ese punto hubiera sido mucho más claro, por ejemplo, si hubiera explicado que 'std :: map' solo admite un índice, que puede ser sensible a mayúsculas y minúsculas pero no a ambos. A partir de ahí, es un enlace fácil para' boost :: multi_index', que admite un segundo índice. – MSalters

1

El elemento Comparar de la plantilla de mapa tiene como valor predeterminado una clase de comparación binaria "menos". Mira a la puesta en práctica:

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

es probable que pueda crear su propia clase que deriva de binary_function (la clase padre a menos) y hacer lo mismo sin comparación entre mayúsculas y minúsculas.

6

Pueden crearse instancias std::map con tres parámetros: tipo de llaves, el tipo de valores, y la función de comparación - una estricta débil ordenar (esencialmente, una función o un funtor comportarse como operator< en términos de transitividad y anti-reflexividad) de su agrado. Simplemente defina el tercer parámetro para hacer "insensible a mayúsculas y minúsculas que no sea" (por ejemplo, mediante un < en las cadenas minúsculas que está comparando) y tendrá el "mapa insensible a mayúsculas y minúsculas" que desee.

0

Implemente la función std :: less y compare cambiando ambas al mismo caso.

4

En caso de que no quiere tocar el tipo de mapa (para mantener su simplicidad original y eficiencia), pero no les importa usar un caso-insensibles función más lenta 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)); 
} 

PD: Tal vez fue idea de Roger Pate, pero no estoy seguro, ya que algunos detalles estaban un poco desajustados (std :: search ?, ¿comparador de cadenas directo?)

5

que utilizar los siguientes:

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 Para la frialdad, pero guau, eso es feo. Hazlo como un functor como en mi ejemplo y escribe el mapa. –

22

Aquí están algunas otras alternativas, incluyendo uno que lleva a cabo mucho más rápido.

#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

El '? La parte 1: 0 en el primer método es tonta e innecesaria, pero por lo demás es una gran respuesta. – jcoffland

+0

FYI, strcasecmp() no está en o . Está en pero no creo que Windows lo tenga. – jcoffland

+1

seguramente en el momento en que dices 'es x <0', ahora tienes un booleano, entonces ¿por qué estás produciendo un entero que luego tendrá que convertirse a bool para que coincida con el argumento de retorno? –

1

probados:

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

Para C++ 11 y más allá:

#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; 
} 
Cuestiones relacionadas