Comparação de cadeias sem distinção entre maiúsculas e minúsculas em C ++ [fechado]

373

Qual é a melhor maneira de fazer comparação de strings sem distinção entre maiúsculas e minúsculas no C ++ sem transformar uma string em maiúsculas ou minúsculas?

Indique se os métodos são compatíveis com Unicode e como são portáteis.

Adão
fonte
@ [Adam] (# 11679): Embora essa variante seja boa em termos de usabilidade, é ruim em termos de desempenho porque cria cópias desnecessárias. Posso ignorar algo, mas acredito que a melhor maneira (não-Unicode) é usar std::stricmp. Caso contrário, leia o que Herb tem a dizer .
Konrad Rudolph
Em c, um geralmente foi forçado toupper a corda toda, então, comparar essa maneira - ou rolar seus próprios comparar: P
Michael Dorgan
uma pergunta mais tarde tem uma resposta simples: strcasecmp (pelo menos para BSD e POSIX compiladores) stackoverflow.com/questions/9182912/...
Moz
@ Mσᶎ esta pergunta também tem essa resposta, com a ressalva importante que strcasecmpnão faz parte do padrão e está ausente em pelo menos um compilador comum.
Mark Ransom

Respostas:

318

O Boost inclui um algoritmo útil para isso:

#include <boost/algorithm/string.hpp>
// Or, for fewer header dependencies:
//#include <boost/algorithm/string/predicate.hpp>

std::string str1 = "hello, world!";
std::string str2 = "HELLO, WORLD!";

if (boost::iequals(str1, str2))
{
    // Strings are identical
}
Roubar
fonte
14
Este UTF-8 é amigável? Eu acho que não.
vladr
18
Não, porque UTF-8 permite cordas idênticas a ser codificado com diferentes códigos binários, devido à acentos, colheitadeiras, questões bidi, etc.
vy32
10
@ vy32 Isso é absolutamente incorreto! As combinações UTF-8 são mutuamente exclusivas. Ele sempre deve usar a representação mais curta possível; caso contrário, é uma sequência UTF-8 mal formada ou um ponto de código que deve ser tratado com cuidado.
Wiz
48
@ Wiz, você está ignorando o problema da normalização de strings Unicode. ñ pode ser representado como uma combinação ˜ seguida por um n ou com um caractere ñ. Você precisa usar a normalização de seqüência de caracteres Unicode antes de realizar a comparação. Consulte o Relatório Técnico Unicode # 15, unicode.org/reports/tr15
vy32 11/11/11
12
@wonkorealtime: porque "ß" convertido para maiúsculas é "SS": fileformat.info/info/unicode/char/df/index.htm
Mooing Duck
118

Aproveite o padrão char_traits. Lembre-se de que a std::stringé de fato um typedef para std::basic_string<char>, ou mais explicitamente std::basic_string<char, std::char_traits<char> >,. O char_traitstipo descreve como os caracteres são comparados, como eles copiam, como são convertidos etc. Tudo o que você precisa fazer é digitar uma nova sequência de caracteres basic_stringe fornecer a ela seu próprio costume char_traitsque compara maiúsculas e minúsculas.

struct ci_char_traits : public char_traits<char> {
    static bool eq(char c1, char c2) { return toupper(c1) == toupper(c2); }
    static bool ne(char c1, char c2) { return toupper(c1) != toupper(c2); }
    static bool lt(char c1, char c2) { return toupper(c1) <  toupper(c2); }
    static int compare(const char* s1, const char* s2, size_t n) {
        while( n-- != 0 ) {
            if( toupper(*s1) < toupper(*s2) ) return -1;
            if( toupper(*s1) > toupper(*s2) ) return 1;
            ++s1; ++s2;
        }
        return 0;
    }
    static const char* find(const char* s, int n, char a) {
        while( n-- > 0 && toupper(*s) != toupper(a) ) {
            ++s;
        }
        return s;
    }
};

typedef std::basic_string<char, ci_char_traits> ci_string;

Os detalhes estão no Guru da Semana número 29 .

wilhelmtell
fonte
10
Até onde eu sei por experiência própria, isso torna seu novo tipo de string incompatível com std :: string.
Zan Lynx
8
Claro que sim - para seu próprio bem. Uma cadeia que não diferencia maiúsculas de minúsculas é outra coisa:, typedef std::basic_string<char, ci_char_traits<char> > istringnão typedef std::basic_string<char, std::char_traits<char> > string.
Andreas Spindler
232
"Tudo o que você precisa fazer ..."
Tim MB
3
Provavelmente @ Nathan usar um compilador que é capaz de realizar CSE básicas sobre o código ...
O paramagnética Croissant
17
Qualquer construção de linguagem que force tal insanidade nesse caso trivial deve e pode ser abandonada sem arrependimentos.
Erik Aronesty
86

O problema com o impulso é que você precisa se conectar e depender do impulso. Não é fácil em alguns casos (por exemplo, android).

E usar char_traits significa tudo suas comparações não maiúsculas de minúsculas, o que geralmente não é o que você deseja.

Isso deve ser suficiente. Deve ser razoavelmente eficiente. Não lida com unicode ou qualquer coisa.

bool iequals(const string& a, const string& b)
{
    unsigned int sz = a.size();
    if (b.size() != sz)
        return false;
    for (unsigned int i = 0; i < sz; ++i)
        if (tolower(a[i]) != tolower(b[i]))
            return false;
    return true;
}

Atualização: versão bônus do C ++ 14 ( #include <algorithm>):

bool iequals(const string& a, const string& b)
{
    return std::equal(a.begin(), a.end(),
                      b.begin(), b.end(),
                      [](char a, char b) {
                          return tolower(a) == tolower(b);
                      });
}
Timmmm
fonte
27
Na verdade, a biblioteca de cadeias de impulso é uma biblioteca apenas de cabeçalho, portanto, não há necessidade de vincular a nada. Além disso, você pode usar o utilitário 'bcp' do boost para copiar apenas os cabeçalhos de string na sua árvore de origem, para que você não precise exigir a biblioteca completa do boost.
Gretchen
Ah, eu não sabia sobre o bcp, parece realmente útil. Obrigado pela informação!
Timmmm 13/03/11
9
É bom saber uma versão simples e sem dependência de impulso.
Deqing 17/05
2
@Anna A biblioteca de textos de impulso precisa ser criada e vinculada. Ele usa o IBM ICU.
Behrouz.M
Também disponível com C ++ 11
marian
58

Se você estiver em um sistema POSIX, poderá usar o strcasecmp . Essa função não faz parte do padrão C, no entanto, nem está disponível no Windows. Isso fará uma comparação sem distinção entre maiúsculas e minúsculas em caracteres de 8 bits, desde que o código do idioma seja POSIX. Se o código do idioma não for POSIX, os resultados serão indefinidos (portanto, pode ser uma comparação localizada ou não). Um equivalente de caracteres largos não está disponível.

Caso contrário, um grande número de implementações históricas da biblioteca C possui as funções stricmp () e strnicmp (). O Visual C ++ no Windows renomeou todos esses itens, com um sublinhado como prefixo, porque eles não fazem parte do padrão ANSI; nesse sistema, eles são chamados _stricmp ou _strnicmp . Algumas bibliotecas também podem ter funções equivalentes a caracteres largos ou multibyte (normalmente denominadas, por exemplo, wcsicmp, mbcsicmp e assim por diante).

C e C ++ ignoram amplamente os problemas de internacionalização, portanto não há uma boa solução para esse problema, exceto o uso de uma biblioteca de terceiros. Confira IBM ICU (Componentes Internacionais para Unicode) se você precisar de uma biblioteca robusta para C / C ++. A UTI é para os sistemas Windows e Unix.

Derek Park
fonte
53

Você está falando de uma comparação sem distinção entre maiúsculas e minúsculas ou uma comparação Unicode normalizada completa?

Uma comparação idiota não encontrará cadeias que podem ser iguais, mas não são binárias iguais.

Exemplo:

U212B (ANGSTROM SIGN)
U0041 (LATIN CAPITAL LETTER A) + U030A (COMBINING RING ABOVE)
U00C5 (LATIN CAPITAL LETTER A WITH RING ABOVE).

São todos equivalentes, mas eles também têm diferentes representações binárias.

Dito isto, a Normalização Unicode deve ser uma leitura obrigatória, especialmente se você planeja dar suporte ao Hangul, Thaï e outros idiomas asiáticos.

Além disso, a IBM praticamente patenteou os algoritmos Unicode mais otimizados e os disponibilizou publicamente. Eles também mantêm uma implementação: IBM ICU

Coincoin
fonte
2
Convém editar esse link da UTI para site.icu-project.org
DevSolar
31

boost :: iequals não é compatível com utf-8 no caso de string. Você pode usar boost :: locale .

comparator<char,collator_base::secondary> cmpr;
cout << (cmpr(str1, str2) ? "str1 < str2" : "str1 >= str2") << endl;
  • Primário - ignore acentos e maiúsculas e minúsculas, comparando apenas as letras base. Por exemplo, "fachada" e "Fachada" são iguais.
  • Secundário - ignore maiúsculas e minúsculas, mas considere acentos. "fachada" e "fachada" são diferentes, mas "fachada" e "fachada" são iguais.
  • Terciário - considere maiúsculas e minúsculas: "Fachada" e "fachada" são diferentes. Ignore pontuação.
  • Quaternário - considere todos os casos, sotaques e pontuação. As palavras devem ser idênticas em termos de representação Unicode.
  • Idêntico - como quaternário, mas compare também os pontos de código.
Igor Milyakov
fonte
30

Meu primeiro pensamento para uma versão não-unicode foi fazer algo assim:


bool caseInsensitiveStringCompare(const string& str1, const string& str2) {
    if (str1.size() != str2.size()) {
        return false;
    }
    for (string::const_iterator c1 = str1.begin(), c2 = str2.begin(); c1 != str1.end(); ++c1, ++c2) {
        if (tolower(*c1) != tolower(*c2)) {
            return false;
        }
    }
    return true;
}
Shadow2531
fonte
20

Você pode usar strcasecmpno Unix ou stricmpno Windows.

Uma coisa que não foi mencionada até agora é que, se você estiver usando strings stl com esses métodos, é útil comparar primeiro o comprimento das duas strings, pois essas informações já estão disponíveis para você na classe de strings. Isso poderia impedir a comparação cara de uma string, se as duas strings que você está comparando não tiverem o mesmo comprimento em primeiro lugar.

bradtgmurray
fonte
Como determinar o comprimento de uma string consiste em iterar todos os caracteres da string e compará-la com 0, há realmente muita diferença entre isso e apenas comparar as strings imediatamente? Eu acho que você obtém melhor localidade de memória no caso em que as duas seqüências não coincidem, mas provavelmente quase o tempo de execução é 2x em caso de uma correspondência.
uliwitness
3
C ++ 11 especifica que a complexidade de std :: :: cadeia comprimento deve ser constante: cplusplus.com/reference/string/string/length
bradtgmurray
11
Isso é um fato pouco divertido, mas tem pouca influência aqui. strcasecmp () e stricmp () usam seqüências de caracteres C não decoradas, portanto não há std :: string envolvido.
uliwitness
3
Esses métodos retornarão -1 se você comparar "a" vs "ab". Os comprimentos são diferentes, mas "a" vem antes de "ab". Portanto, a simples comparação dos comprimentos não é viável se o chamador se importar com o pedido.
Nathan
14

Funções de seqüência de caracteres do Visual C ++ com suporte a unicode: http://msdn.microsoft.com/en-us/library/cc194799.aspx

o que você provavelmente está procurando é _wcsnicmp

Darren Kopp
fonte
7
Ironicamente, os "códigos de caracteres amplos" da Microsoft NÃO são limpos por unicode porque não tratam da normalização unicode.
vy32
13

Estou tentando reunir uma boa resposta de todas as postagens, então me ajude a editar isso:

Aqui está um método para fazer isso, embora ele transforme as seqüências de caracteres e não seja compatível com Unicode, ele deve ser portátil, o que é uma vantagem:

bool caseInsensitiveStringCompare( const std::string& str1, const std::string& str2 ) {
    std::string str1Cpy( str1 );
    std::string str2Cpy( str2 );
    std::transform( str1Cpy.begin(), str1Cpy.end(), str1Cpy.begin(), ::tolower );
    std::transform( str2Cpy.begin(), str2Cpy.end(), str2Cpy.begin(), ::tolower );
    return ( str1Cpy == str2Cpy );
}

Pelo que li, isso é mais portátil que o stricmp () porque o stricmp () não faz parte da biblioteca std, mas é implementado apenas pela maioria dos fornecedores de compiladores.

Para obter uma implementação realmente compatível com Unicode, parece que você deve sair da biblioteca std. Uma boa biblioteca de terceiros é o IBM ICU (International Components for Unicode)

O boost :: iequals também fornece um utilitário bastante bom para fazer esse tipo de comparação.

Adam
fonte
você pode dizer o que significa :: tolower, por que você pode usar tolower em vez de tolower () e o que é '::' antes? obrigado
VextoR
17
Essa não é uma solução muito eficiente - você faz cópias das duas seqüências e as transforma, mesmo que o primeiro caractere seja diferente.
Timmmm 13/03/11
2
Se você deseja fazer uma cópia de qualquer maneira, por que não passar por valor em vez de por referência?
Celticminstrel
Eu acho que é uma dica simples, sem impulso. :)
cmcromance
11
a questão pede explicitamente para não transformtoda a cadeia antes de comparação
Sandburg
12
str1.size() == str2.size() && std::equal(str1.begin(), str1.end(), str2.begin(), [](auto a, auto b){return std::tolower(a)==std::tolower(b);})

Você pode usar o código acima no C ++ 14 se não estiver em posição de usar o boost. Você tem que usar std::towlowerpara caracteres largos.

vinha
fonte
4
Eu acho que você precisa adicionar um str1.size() == str2.size() &&para a frente para que não saia dos limites quando str2 é um prefixo de str1.
ɲeuroburɳ
11

A biblioteca Boost.String possui muitos algoritmos para fazer comparações sem distinção entre maiúsculas e minúsculas e assim por diante.

Você pode implementar o seu próprio, mas por que se preocupar quando isso já foi feito?

Dean Harding
fonte
11
Não existe uma maneira embutida com std :: string?
WilliamKF
6
Não, não existe.
Dean Harding
3
"... por que se preocupar quando já está pronto?" - e se você não estiver usando o Boost? O OP não tinha a etiqueta com a pergunta.
JWW
11

FYI strcmp()e stricmp()são vulneráveis ​​ao estouro de buffer, pois apenas processam até atingir um terminador nulo. É mais seguro de usar _strncmp()e _strnicmp().

Cunha
fonte
6
É verdade, embora overREADing um buffer seja significativamente menos perigoso que overWRITEing a buffer.
23811 Adam Rosenfield
4
stricmp()e strnicmp()não fazem parte do padrão POSIX :-( No entanto, você pode encontrar strcasecmp(), strcasecmp_l(), strncasecmp()e strncasecmp_l()em POSIX cabeçalho strings.h:-) ver opengroup.org
olibre
2
@AdamRosenfield 'pior' depende do contexto. Em segurança, às vezes o ponto principal de uma substituição é conseguir sobrescrever.
precisa saber é o seguinte
10

Veja std::lexicographical_compare:

// lexicographical_compare example
#include <iostream>  // std::cout, std::boolalpha
#include <algorithm>  // std::lexicographical_compare
#include <cctype>  // std::tolower

// a case-insensitive comparison function:
bool mycomp (char c1, char c2) {
    return std::tolower(c1) < std::tolower(c2);
}

int main () {
    char foo[] = "Apple";
    char bar[] = "apartment";

    std::cout << std::boolalpha;

    std::cout << "Comparing foo and bar lexicographically (foo < bar):\n";

    std::cout << "Using default comparison (operator<): ";
    std::cout << std::lexicographical_compare(foo, foo + 5, bar, bar + 9);
    std::cout << '\n';

    std::cout << "Using mycomp as comparison object: ";
    std::cout << std::lexicographical_compare(foo, foo + 5, bar, bar + 9, mycomp);
    std::cout << '\n';

    return 0;
}

Demo

Brian Rodriguez
fonte
11
Este método é potencialmente inseguro e não portátil. std::tolowerfunciona apenas se o caractere for codificado em ASCII. Não existe tal garantia para std::string- portanto, pode ser um comportamento indefinido facilmente.
plasmacel
@plasmacel Em seguida, use uma função que funcione com outras codificações.
Brian Rodriguez
9

Para minhas necessidades básicas de comparação de cadeias sem distinção entre maiúsculas e minúsculas, prefiro não precisar usar uma biblioteca externa, nem quero uma classe de cadeia separada com características sem diferenciação de maiúsculas e minúsculas que sejam incompatíveis com todas as minhas outras strings.

Então, o que eu criei é o seguinte:

bool icasecmp(const string& l, const string& r)
{
    return l.size() == r.size()
        && equal(l.cbegin(), l.cend(), r.cbegin(),
            [](string::value_type l1, string::value_type r1)
                { return toupper(l1) == toupper(r1); });
}

bool icasecmp(const wstring& l, const wstring& r)
{
    return l.size() == r.size()
        && equal(l.cbegin(), l.cend(), r.cbegin(),
            [](wstring::value_type l1, wstring::value_type r1)
                { return towupper(l1) == towupper(r1); });
}

Uma função simples com uma sobrecarga para char e outra para whar_t. Como não usa nada de fora do padrão, deve funcionar bem em qualquer plataforma.

A comparação de igualdade não considerará problemas como codificação de comprimento variável e normalização Unicode, mas o basic_string não tem suporte para o que eu saiba, e normalmente não é um problema.

Nos casos em que é necessária uma manipulação lexicográfica mais sofisticada do texto, você simplesmente precisa usar uma biblioteca de terceiros como o Boost, o que é esperado.

Neutrino
fonte
2
Você provavelmente poderia fazer essa função se o transformasse em um modelo e usasse basic_string <T> em vez de versões separadas de string / wstring?
uliwitness
2
Como o modelo de função única invocaria o toupper ou o towupper sem recorrer ao uso de especialização ou macros, uma sobrecarga de função parece uma implementação mais simples e apropriada do que qualquer um.
Neutrino
9

Curto e agradável. Nenhuma outra dependência, além do std C lib estendido .

strcasecmp(str1.c_str(), str2.c_str()) == 0

retorna true se str1e str2é igual. strcasecmppode não existir, pode haver análogos stricmp, strcmpietc.

Código de exemplo:

#include <iostream>
#include <string>
#include <string.h> //For strcasecmp(). Also could be found in <mem.h>

using namespace std;

/// Simple wrapper
inline bool str_ignoreCase_cmp(std::string const& s1, std::string const& s2) {
    if(s1.length() != s2.length())
        return false;  // optimization since std::string holds length in variable.
    return strcasecmp(s1.c_str(), s2.c_str()) == 0;
}

/// Function object - comparator
struct StringCaseInsensetiveCompare {
    bool operator()(std::string const& s1, std::string const& s2) {
        if(s1.length() != s2.length())
            return false;  // optimization since std::string holds length in variable.
        return strcasecmp(s1.c_str(), s2.c_str()) == 0;
    }
    bool operator()(const char *s1, const char * s2){ 
        return strcasecmp(s1,s2)==0;
    }
};


/// Convert bool to string
inline char const* bool2str(bool b){ return b?"true":"false"; }

int main()
{
    cout<< bool2str(strcasecmp("asd","AsD")==0) <<endl;
    cout<< bool2str(strcasecmp(string{"aasd"}.c_str(),string{"AasD"}.c_str())==0) <<endl;
    StringCaseInsensetiveCompare cmp;
    cout<< bool2str(cmp("A","a")) <<endl;
    cout<< bool2str(cmp(string{"Aaaa"},string{"aaaA"})) <<endl;
    cout<< bool2str(str_ignoreCase_cmp(string{"Aaaa"},string{"aaaA"})) <<endl;
    return 0;
}

Resultado:

true
true
true
true
true
kyb
fonte
6
é estranho que C ++ std :: string tem método de comparação não ignorar caso ..
KYB
11
"strcasecmp não faz parte do padrão" - Mark Ransom 1 / Dez / 14 às 19:57
Liviu
sim, mas a maioria dos compiladores modernos possui esse ou seu análogo de outro nome. stricmp, strcmpi, strcasecmp, Etc. Obrigado. mensagem editada.
Kyb # 21/16
TODO: use em cout << boolalphavez do meu bool2strporque ele converte implicitamente bool em chars para fluxo.
KYB
Está em <strings.h> nas bibliotecas do gcc.
Coruja
7

Isso sem o uso do Boost pode ser feito com o ponteiro da string C c_str()e usando strcasecmp:

std::string str1 ="aBcD";
std::string str2 = "AbCd";;
if (strcasecmp(str1.c_str(), str2.c_str()) == 0)
{
    //case insensitive equal 
}
DavidS
fonte
6

Supondo que você esteja procurando um método e não uma função mágica que já exista, francamente não há maneira melhor. Todos nós poderíamos escrever trechos de código com truques inteligentes para conjuntos de caracteres limitados, mas no final do dia, em algum momento, você precisa converter os caracteres.

A melhor abordagem para essa conversão é fazer isso antes da comparação. Isso permite uma grande flexibilidade quando se trata de esquemas de codificação, dos quais o seu operador de comparação real deve desconhecer.

É claro que você pode "ocultar" essa conversão atrás de sua própria função ou classe de string, mas ainda precisará converter as strings antes da comparação.

Andrew Grant
fonte
6

Eu escrevi uma versão sem distinção entre maiúsculas e minúsculas de char_traits para uso com std :: basic_string, a fim de gerar uma std :: string que não faz distinção entre maiúsculas e minúsculas ao fazer comparações, pesquisas etc. usando as funções integradas std :: basic_string.

Então, em outras palavras, eu queria fazer algo assim.

std::string a = "Hello, World!";
std::string b = "hello, world!";

assert( a == b );

... que std :: string não pode manipular. Aqui está o uso dos meus novos char_traits:

std::istring a = "Hello, World!";
std::istring b = "hello, world!";

assert( a == b );

... e aqui está a implementação:

/*  ---

        Case-Insensitive char_traits for std::string's

        Use:

            To declare a std::string which preserves case but ignores case in comparisons & search,
            use the following syntax:

                std::basic_string<char, char_traits_nocase<char> > noCaseString;

            A typedef is declared below which simplifies this use for chars:

                typedef std::basic_string<char, char_traits_nocase<char> > istring;

    --- */

    template<class C>
    struct char_traits_nocase : public std::char_traits<C>
    {
        static bool eq( const C& c1, const C& c2 )
        { 
            return ::toupper(c1) == ::toupper(c2); 
        }

        static bool lt( const C& c1, const C& c2 )
        { 
            return ::toupper(c1) < ::toupper(c2);
        }

        static int compare( const C* s1, const C* s2, size_t N )
        {
            return _strnicmp(s1, s2, N);
        }

        static const char* find( const C* s, size_t N, const C& a )
        {
            for( size_t i=0 ; i<N ; ++i )
            {
                if( ::toupper(s[i]) == ::toupper(a) ) 
                    return s+i ;
            }
            return 0 ;
        }

        static bool eq_int_type( const int_type& c1, const int_type& c2 )
        { 
            return ::toupper(c1) == ::toupper(c2) ; 
        }       
    };

    template<>
    struct char_traits_nocase<wchar_t> : public std::char_traits<wchar_t>
    {
        static bool eq( const wchar_t& c1, const wchar_t& c2 )
        { 
            return ::towupper(c1) == ::towupper(c2); 
        }

        static bool lt( const wchar_t& c1, const wchar_t& c2 )
        { 
            return ::towupper(c1) < ::towupper(c2);
        }

        static int compare( const wchar_t* s1, const wchar_t* s2, size_t N )
        {
            return _wcsnicmp(s1, s2, N);
        }

        static const wchar_t* find( const wchar_t* s, size_t N, const wchar_t& a )
        {
            for( size_t i=0 ; i<N ; ++i )
            {
                if( ::towupper(s[i]) == ::towupper(a) ) 
                    return s+i ;
            }
            return 0 ;
        }

        static bool eq_int_type( const int_type& c1, const int_type& c2 )
        { 
            return ::towupper(c1) == ::towupper(c2) ; 
        }       
    };

    typedef std::basic_string<char, char_traits_nocase<char> > istring;
    typedef std::basic_string<wchar_t, char_traits_nocase<wchar_t> > iwstring;
John Dibling
fonte
2
Isso funciona para caracteres regulares, mas não funciona para todo o Unicode, pois a captitalização não é necessariamente bidirecional (há um bom exemplo em grego envolvendo sigma que não me lembro agora; algo como ele tem duas letras maiúsculas e uma minúscula , e você não pode obter uma comparação de qualquer forma adequada)
coppro
11
Essa é realmente a maneira errada de fazer isso. A distinção entre maiúsculas e minúsculas não deve ser uma propriedade das próprias strings. O que acontece quando o mesmo objeto de string precisa de comparações que diferenciam maiúsculas e minúsculas de maiúsculas e minúsculas?
Ferruccio
Se a distinção entre maiúsculas e minúsculas não for apropriada para "fazer parte" da cadeia, a função find () também não será. O que, para você, pode ser verdade, e tudo bem. Na IMO, o melhor de C ++ é que ele não impõe um paradigma específico ao programador. É o que você quer / precisa que seja.
John Dibling
Na verdade, acho que a maioria dos guru de C ++ (como os do comitê de padrões) concorda que foi um erro colocar find () em std :: basic_string <> junto com muitas outras coisas que poderiam igualmente ser colocadas em funções livres. Além disso, existem alguns problemas em colocá-lo no tipo.
Andreas Magnusson
Como outros já apontaram, há duas coisas principais erradas nessa solução (ironicamente, uma é a interface e a outra é a implementação ;-)).
Konrad Rudolph
4

Eu tive uma boa experiência usando os componentes internacionais para bibliotecas Unicode - eles são extremamente poderosos e fornecem métodos para conversão, suporte de localidade, renderização de data e hora, mapeamento de caso (que você não parece querer) e agrupamento , que inclui comparação sem distinção entre maiúsculas e minúsculas (e mais). Eu usei apenas a versão C ++ das bibliotecas, mas elas parecem ter uma versão Java também.

Existem métodos para executar comparações normalizadas, como referido por @Coincoin, e podem até explicar a localidade - por exemplo (e este é um exemplo de classificação, não estritamente igualitário), tradicionalmente em espanhol (na Espanha), a combinação de letras "ll" classifica entre "l" e "m", então "lz" <"ll" <"ma".

Blair Conrad
fonte
4

Basta usar strcmp()para comparação entre maiúsculas e minúsculas e / strcmpi()ou sem stricmp()distinção entre maiúsculas e minúsculas. Que estão ambos no arquivo de cabeçalho<string.h>

formato:

int strcmp(const char*,const char*);    //for case sensitive
int strcmpi(const char*,const char*);   //for case insensitive

Uso:

string a="apple",b="ApPlE",c="ball";
if(strcmpi(a.c_str(),b.c_str())==0)      //(if it is a match it will return 0)
    cout<<a<<" and "<<b<<" are the same"<<"\n";
if(strcmpi(a.c_str(),b.c_str()<0)
    cout<<a[0]<<" comes before ball "<<b[0]<<", so "<<a<<" comes before "<<b;

Resultado

Apple e ApPlE são os mesmos

a vem antes de b, então a maçã vem antes da bola

reubenjohn
fonte
2
Voto negativo, porque essa dificilmente é uma maneira C ++ de fazer as coisas.
Thomas Daugaard 30/07
Esta é a convenção c ++ na minha universidade, mas eu vou manter isso em mente ao postar aqui
reubenjohn
4
stricmp é uma extensão da Microsoft AFAIK. O BSD parece ter strcasecmp ().
uliwitness
3

Tarde para a festa, mas aqui está uma variante que usa std::localee, portanto, manipula corretamente o turco:

auto tolower = std::bind1st(
    std::mem_fun(
        &std::ctype<char>::tolower),
    &std::use_facet<std::ctype<char> >(
        std::locale()));

fornece um functor que usa o código do idioma ativo para converter caracteres em minúsculas, que você pode usar via std::transformpara gerar cadeias de letras minúsculas:

std::string left = "fOo";
transform(left.begin(), left.end(), left.begin(), tolower);

Isso também funciona para wchar_tseqüências de caracteres baseadas.

Simon Richter
fonte
2

Apenas uma observação sobre o método que você finalmente escolher, se esse método incluir o uso de strcmpalgumas respostas sugerem:

strcmpnão funciona com dados Unicode em geral. Em geral, ele nem funciona com codificações Unicode baseadas em bytes, como o utf-8, pois strcmpapenas faz comparações de bytes por byte e os pontos de código Unicode codificados no utf-8 podem levar mais de 1 byte. O único caso Unicode específico que strcmplida corretamente é quando uma string codificada com uma codificação baseada em bytes contém apenas pontos de código abaixo de U + 00FF - a comparação de bytes por byte é suficiente.

Johann Gerell
fonte
2

Desde o início de 2013, o projeto de UTI, mantido pela IBM, é uma resposta muito boa para isso.

http://site.icu-project.org/

A UTI é uma "biblioteca Unicode completa e portátil que acompanha de perto os padrões do setor". Para o problema específico da comparação de cadeias, o objeto Collation faz o que você deseja.

O Projeto Mozilla adotou a UTI para internacionalização no Firefox em meados de 2012; você pode acompanhar a discussão de engenharia, incluindo problemas de sistemas de construção e tamanho do arquivo de dados, aqui:

michaelhanson
fonte
2

Parece que as soluções acima não estão usando o método compare e a implementação total novamente, então aqui está a minha solução e espero que funcione para você (está funcionando bem).

#include<iostream>
#include<cstring>
#include<cmath>
using namespace std;
string tolow(string a)
{
    for(unsigned int i=0;i<a.length();i++)
    {
        a[i]=tolower(a[i]);
    }
    return a;
}
int main()
{
    string str1,str2;
    cin>>str1>>str2;
    int temp=tolow(str1).compare(tolow(str2));
    if(temp>0)
        cout<<1;
    else if(temp==0)
        cout<<0;
    else
        cout<<-1;
}
Jagadeesh Pulamarasetti
fonte
1

Se você não deseja usar a biblioteca Boost , aqui está a solução usando apenas o cabeçalho io padrão C ++.

#include <iostream>

struct iequal
{
    bool operator()(int c1, int c2) const
    {
        // case insensitive comparison of two characters.
        return std::toupper(c1) == std::toupper(c2);
    }
};

bool iequals(const std::string& str1, const std::string& str2)
{
    // use std::equal() to compare range of characters using the functor above.
    return std::equal(str1.begin(), str1.end(), str2.begin(), iequal());
}

int main(void)
{
    std::string str_1 = "HELLO";
    std::string str_2 = "hello";

    if(iequals(str_1,str_2))
    {
        std::cout<<"String are equal"<<std::endl;   
    }

    else
    {
        std::cout<<"String are not equal"<<std::endl;
    }


    return 0;
}
HaSeeB MiR
fonte
Acredito que std :: toupper esteja em #include <cctype>, talvez seja necessário incluí-lo.
David Ledger
Se você usar uma versão global como esta :: toupper, talvez não seja necessário incluir <ctype> porque existem duas versões c version e c ++ version com localidade, eu acho. Tão melhor usar a versão global ":: toupper ()"
HaSeeB MiR
esta solução falha quando uma das cordas está vazia: "" - ele retorna true, nesse caso, quando deve retornar falso
ekkis
0

Se você precisar comparar uma string de origem com mais frequência com outras strings, uma solução elegante é usar regex.

std::wstring first = L"Test";
std::wstring second = L"TEST";

std::wregex pattern(first, std::wregex::icase);
bool isEqual = std::regex_match(second, pattern);
smibe
fonte
Tentei este erro de compilação: error: conversion from 'const char [5]' to non-scalar type 'std::wstring {aka std::basic_string<wchar_t>}' requested
Deqing 15/05
péssima ideia. É a pior solução.
Behrouz.M
Esta não é uma boa solução, mas mesmo se você queria usá-lo, você precisa de um L na frente de seus constantes WideString, por exemplo L "TEST"
celticminstrel
Seria bom se alguém pudesse explicar por que é a pior solução. Devido a problemas de desempenho? Criar a regex é caro, mas depois a comparação deve ser muito rápida.
smibe
é utilizável e portátil, o principal problema é que primeiro não pode conter nenhum caractere usado pelo regex. Não pode ser usado como uma comparação geral de cadeias por causa disso. Também será mais lento, há uma bandeira para fazê-lo funcionar da maneira que o smibe diz, mas ainda não pode ser usado como uma função geral.
Ben Ben
0

Uma maneira simples de comparar duas strings em c ++ (testado para windows) é usando _stricmp

// Case insensitive (could use equivalent _stricmp)  
result = _stricmp( string1, string2 );  

Se você deseja usar com std :: string, um exemplo:

std::string s1 = string("Hello");
if ( _stricmp(s1.c_str(), "HELLO") == 0)
   std::cout << "The string are equals.";

Para mais informações aqui: https://msdn.microsoft.com/it-it/library/e0z9k731.aspx

DAme
fonte
Vale a pena ler stackoverflow.com/a/12414441/95309 além desta resposta, pois é a) uma função C eb) supostamente não portável.
Claus Jørgensen
que #include precisamos para fazer isso funcionar?
ekkis
11
@ekkis para usar _stricmp, você deve incluir <string.h> como pode ler aqui: docs.microsoft.com/en-us/cpp/c-runtime-library/reference/…
DAme
-1
bool insensitive_c_compare(char A, char B){
  static char mid_c = ('Z' + 'a') / 2 + 'Z';
  static char up2lo = 'A' - 'a'; /// the offset between upper and lowers

  if ('a' >= A and A >= 'z' or 'A' >= A and 'Z' >= A)
      if ('a' >= B and B >= 'z' or 'A' >= B and 'Z' >= B)
      /// check that the character is infact a letter
      /// (trying to turn a 3 into an E would not be pretty!)
      {
        if (A > mid_c and B > mid_c or A < mid_c and B < mid_c)
        {
          return A == B;
        }
        else
        {
          if (A > mid_c)
            A = A - 'a' + 'A'; 
          if (B > mid_c)/// convert all uppercase letters to a lowercase ones
            B = B - 'a' + 'A';
          /// this could be changed to B = B + up2lo;
          return A == B;
        }
      }
}

isso provavelmente poderia se tornar muito mais eficiente, mas aqui está uma versão volumosa com todos os seus bits vazios.

não é tão portátil, mas funciona bem com o que estiver no meu computador (não faço ideia, sou de figuras, não de palavras)

user4578093
fonte
Este não é o suporte Unicode, que é o que a pergunta foi feita.
Behrouz.M
Isso não suporta conjuntos de caracteres diferentes do inglês.
Robert Andrzejuk 29/04
-3

Uma maneira fácil de comparar seqüências de caracteres que são apenas diferentes por caracteres minúsculos e maiúsculos é fazer uma comparação ascii. Todas as letras maiúsculas e minúsculas diferem 32 bits na tabela ascii, usando essas informações, temos as seguintes ...

    for( int i = 0; i < string2.length(); i++)
    {
       if (string1[i] == string2[i] || int(string1[i]) == int(string2[j])+32 ||int(string1[i]) == int(string2[i])-32) 
    {
      count++;
      continue;
    }
    else 
    {
      break;
    }
    if(count == string2.length())
    {
      //then we have a match
    }
}
Craig Stoddard
fonte
3
De acordo com isso, "++ j" será encontrado igual a "KKJ" e "1234" será igual a "QRST". Duvido que seja algo que alguém queira.
Celticminstrel