Diferença entre InvariantCulture e comparação de cadeias ordinais

548

Ao comparar duas seqüências de caracteres em c # para igualdade, qual é a diferença entre comparação entre InvariantCulture e Ordinal?

Kapil
fonte
Talvez siao2.com/2004/12/29/344136.aspx ? (pesquisado)
Cheese Daneish
2
Para aqueles que usam String1.Equals(String2, StringComparison.Ordinal), é melhor usar o String1 == String2que é intrinsecamente String1.Equals(String2)e, por padrão, é uma comparação ordinal que diferencia maiúsculas de minúsculas.
Ghasan #
3
@ Ghasan Não tenho certeza se isso =="melhora", mas é a) mais curto, b) menos explícito sobre o que exatamente ele faz e c) String1pode ser nulo sem a comparação de a NullReferenceException.
Eugene Beresovsky
3
@Gasan as práticas recomendadas oficiais do MSDN para usar seqüências de caracteres na página .NET Framework ( msdn.microsoft.com/en-us/library/… ) recomenda o uso de sobrecargas que especificam explicitamente o StringComparisontipo. No caso de comparação de strings, isso significa String.Equals.
Ohad Schneider
3
@EugeneBeresovsky Para evitar NullReferenceExceptionque você pode simplesmente usar o método estático: String.Equals(string1, string2, StringComparison.Ordinal).
Ohad Schneider

Respostas:

302

InvariantCulture

Usa um conjunto "padrão" de ordenação de caracteres (a, b, c, ... etc.). Isso contrasta com algumas localidades específicas, que podem classificar caracteres em diferentes ordens ('a-with-aguda' pode ser antes ou depois de 'a', dependendo da localidade e assim por diante).

Ordinal

Por outro lado, analisa puramente os valores dos bytes brutos que representam o caractere.


Há um ótimo exemplo em http://msdn.microsoft.com/en-us/library/e6883c06.aspx que mostra os resultados dos vários valores de StringComparison. No final, ele mostra (trecho):

StringComparison.InvariantCulture:
LATIN SMALL LETTER I (U+0069) is less than LATIN SMALL LETTER DOTLESS I (U+0131)
LATIN SMALL LETTER I (U+0069) is less than LATIN CAPITAL LETTER I (U+0049)
LATIN SMALL LETTER DOTLESS I (U+0131) is greater than LATIN CAPITAL LETTER I (U+0049)

StringComparison.Ordinal:
LATIN SMALL LETTER I (U+0069) is less than LATIN SMALL LETTER DOTLESS I (U+0131)
LATIN SMALL LETTER I (U+0069) is greater than LATIN CAPITAL LETTER I (U+0049)
LATIN SMALL LETTER DOTLESS I (U+0131) is greater than LATIN CAPITAL LETTER I (U+0049)

Você pode ver que, onde InvariantCulture gera (U + 0069, U + 0049, U + 00131), Ordinal produz (U + 0049, U + 0069, U + 00131).

JaredReisinger
fonte
25
A comparação ordinal examina os pontos de código , não os bytes.
Joey
144
Eu sinto que é uma informação útil, mas na verdade não responde à pergunta. Ao determinar a Igualdade de duas seqüências, existe algum motivo para usar o InvarintCulture em vez do Ordinal? Parece que InvariantCulture seria usado para classificar seqüências de caracteres, e Ordinal deveria ser usado para verificação de igualdade (não nos importamos que o sotaque - a venha antes ou depois de a, é simplesmente diferente). No entanto, eu mesmo estou um pouco inseguro sobre esse ponto.
precisa saber é o seguinte
18
Consulte msdn.microsoft.com/en-us/library/ms230117%28v=vs.90%29.aspx e observe que a normalização de seqüência de caracteres e a comparação ordinal são recomendadas.
precisa saber é o seguinte
23
Ordinal é muito mais rápido
Darren
9
Há bons resultados de testes de desempenho publicados Testes de comparação de strings em C # que informam o desempenho de cada método de comparação de strings e o tempo deles.
Kumar C
262

Importa, por exemplo - existe uma coisa chamada expansão de personagem

var s1 = "Strasse";
var s2 = "Straße";

s1.Equals(s2, StringComparison.Ordinal);           //false
s1.Equals(s2, StringComparison.InvariantCulture);  //true

Com InvariantCultureo caractere ß é expandido para ss.

Ventsyslav Raikov
fonte
1
Isso também difere de alguma forma entre Ordinale InvariantCulture? É disso que trata a pergunta original.
Matthijs Wessels
3
Para aqueles que não sabem ß, deve-se notar que ßpelo menos em alemão é igual a um s duplo, Fonte: en.wikipedia.org/wiki/%C3%9F
Peter
20
Isso não é muito correto, Peter, você não pode usar ße ssalternadamente em alemão (eu sou um falante nativo). Existem casos em que ambos são legais (mas geralmente um está desatualizado / não é recomendado) e há casos em que apenas um formulário é permitido.
enzi 22/03
5
Este exemplo simples demonstra claramente a diferença entre as 2 comparações. Eu acho que estou entendendo isso agora.
21917 BrianLegg
4
Tinha de experimentá-lo: ideone.com/j8DvDo tão legal! Uma pequena lição de alemão também. Quer saber o que é a diferença entre ß e ss agora ...
MZN
111

Apontando para as práticas recomendadas para usar seqüências de caracteres no .NET Framework :

  • Use StringComparison.Ordinalou StringComparison.OrdinalIgnoreCasepara comparações como seu padrão seguro para correspondência de sequência independente de cultura.
  • Use comparações com StringComparison.Ordinalou StringComparison.OrdinalIgnoreCasepara obter melhor desempenho.
  • Use os valores não-linguísticos StringComparison.Ordinalou em StringComparison.OrdinalIgnoreCasevez das operações de cadeia de caracteres com base em CultureInfo.InvariantCulturequando a comparação for linguisticamente irrelevante (simbólica, por exemplo).

E finalmente:

  • Não use operações de string baseadas StringComparison.InvariantCulturena maioria dos casos . Uma das poucas exceções é quando você persiste em dados lingüisticamente significativos, mas culturalmente agnósticos.
Dariusz
fonte
56

Outra diferença útil (em inglês, onde os acentos são incomuns) é que uma comparação da InvariantCulture compara as seqüências inteiras primeiro sem distinção entre maiúsculas e minúsculas e, em seguida, se necessário (e solicitado) distingue por maiúsculas e minúsculas depois de comparar primeiro apenas nas letras distintas. (Você também pode fazer uma comparação sem distinção entre maiúsculas e minúsculas, é claro, o que não fará distinção entre maiúsculas e minúsculas.) Corrigido:As letras acentuadas são consideradas outro sabor das mesmas letras e a cadeia de caracteres é comparada primeiro, ignorando os acentos e depois contabilizando-os se todas as letras gerais corresponderem (da mesma forma que em maiúsculas e minúsculas, exceto que, no final, não são ignoradas em uma comparação que não diferencia maiúsculas de minúsculas). Isso agrupa versões acentuadas da mesma palavra, próximas umas das outras, em vez de se separarem completamente na primeira diferença de sotaque. Essa é a ordem de classificação que você normalmente encontraria em um dicionário, com palavras em maiúsculas aparecendo ao lado de seus equivalentes em minúsculas e letras acentuadas próximas à letra não acentuada correspondente.

Uma comparação ordinal compara estritamente os valores numéricos dos caracteres, parando na primeira diferença. Isso classifica as letras maiúsculas completamente separadas das letras minúsculas (e as letras acentuadas provavelmente se separam daquelas), para que as palavras maiúsculas não se aproximem dos equivalentes em minúsculas.

InvariantCulture também considera maiúsculas maiúsculas e minúsculas, enquanto Ordinal considera maiúsculas minúsculas (uma recuperação de ASCII dos velhos tempos antes dos computadores terem letras minúsculas, as letras maiúsculas eram alocadas primeiro e, portanto, tinham valores inferiores às letras minúsculas adicionado mais tarde).

Por exemplo, por Ordinal: "0" < "9" < "A" < "Ab" < "Z" < "a" < "aB" < "ab" < "z" < "Á" < "Áb" < "á" < "áb"

E por InvariantCulture: "0" < "9" < "a" < "A" < "á" < "Á" < "ab" < "aB" < "Ab" < "áb" < "Áb" < "z" < "Z"

Rob Parker
fonte
Dei uma outra olhada nisso e notei uma inconsistência entre o exemplo InvariantCulture e minha explicação sobre o tratamento de caracteres acentuados. O exemplo parece estar correto, então corrigi a explicação para ser consistente. A comparação InvariantCulture não para no primeiro sotaque diferente e parece considerar apenas uma diferença de sotaque na mesma letra se o restante das cadeias corresponder além de sotaques e maiúsculas e minúsculas. Uma diferença de acento é então considerada antes de uma diferença de caso anterior, então "Aaba" <"aába".
Rob Parker
31

Embora a questão seja sobre igualdade , para referência visual rápida, aqui está a ordem de algumas sequências ordenadas usando algumas culturas que ilustram algumas das idiossincrasias por aí.

Ordinal          0 9 A Ab a aB aa ab ss Ä Äb ß ä äb      
IgnoreCase       0 9 a A aa ab Ab aB ss ä Ä äb Äb ß      
--------------------------------------------------------------------
InvariantCulture 0 9 a A  ä Ä aa ab aB Ab äb Äb ss ß     
IgnoreCase       0 9 A a  Ä ä aa Ab aB ab Äb äb ß ss     
--------------------------------------------------------------------
da-DK            0 9 a A  ab aB Ab ss ß ä Ä äb Äb aa     
IgnoreCase       0 9 A a  Ab aB ab ß ss Ä ä Äb äb aa     
--------------------------------------------------------------------
de-DE            0 9 a A  ä Ä aa ab aB Ab äb Äb ß ss     
IgnoreCase       0 9 A a  Ä ä aa Ab aB ab Äb äb ss ß     
--------------------------------------------------------------------
en-US            0 9 a A  ä Ä aa ab aB Ab äb Äb ß ss     
IgnoreCase       0 9 A a  Ä ä aa Ab aB ab Äb äb ss ß     
--------------------------------------------------------------------
ja-JP            0 9 a A  ä Ä aa ab aB Ab äb Äb ß ss     
IgnoreCase       0 9 A a  Ä ä aa Ab aB ab Äb äb ss ß     

Observações:

  • de-DE, ja-JPe en-USclassifique da mesma maneira
  • Invariantapenas classifica sse ßdiferentemente das três culturas acima
  • da-DK classifica de maneira bem diferente
  • a IgnoreCasebandeira é importante para todas as culturas incluídas na amostra

O código usado para gerar a tabela acima:

var l = new List<string>
    { "0", "9", "A", "Ab", "a", "aB", "aa", "ab", "ss", "ß",
      "Ä", "Äb", "ä", "äb", "あ", "ぁ", "ア", "ァ", "A", "亜" };

foreach (var comparer in new[]
{
    StringComparer.Ordinal,
    StringComparer.OrdinalIgnoreCase,
    StringComparer.InvariantCulture,
    StringComparer.InvariantCultureIgnoreCase,
    StringComparer.Create(new CultureInfo("da-DK"), false),
    StringComparer.Create(new CultureInfo("da-DK"), true),
    StringComparer.Create(new CultureInfo("de-DE"), false),
    StringComparer.Create(new CultureInfo("de-DE"), true),
    StringComparer.Create(new CultureInfo("en-US"), false),
    StringComparer.Create(new CultureInfo("en-US"), true),
    StringComparer.Create(new CultureInfo("ja-JP"), false),
    StringComparer.Create(new CultureInfo("ja-JP"), true),
})
{
    l.Sort(comparer);
    Console.WriteLine(string.Join(" ", l));
}
Eugene Beresovsky
fonte
1
Hmmm - OK, é bom que você tenha feito essa pesquisa e postado suas descobertas, embora eu não tenha exatamente certeza de qual é o seu ponto. De qualquer forma, o dinamarquês pode não ser uma das "culturas mais importantes" (embora 5 milhões de dinamarqueses gostem bastante de sua cultura), mas se você colocar "aa" como uma sequência de teste adicional e "da-DK" como uma cultura de teste adicional, você verá alguns resultados interessantes.
RenniePet
1
@RenniePet Obrigado por isso. Eu adicionei dinamarquês, pois é bem diferente do que as outras 3 culturas usadas. (Como os emoticons que indicam ironia não parecem ser tão bem compreendidos na Web de leitura da língua inglesa quanto eu supunha, removi o comentário das "culturas mais importantes". Afinal, o BCL não possui um recurso CultureComparerque poderíamos usar Para esta tabela, a Danishcultura (informações) acabou sendo muito importante.)
Eugene Beresovsky
1
Obrigado. Percebi que seu comentário sobre "culturas mais importantes" deveria ser tomado com um pouco de sal - é que fiquei muito velho para usar emoticons. Eu acho que as mensagens de texto se tornaram tão comuns que usar emoticons é como explicar suas piadas depois que você as conta, independentemente de alguém rir ou não. Aliás, as outras culturas escandinavas (finlandesa, norueguesa e sueca) são as mesmas do dinamarquês, exceto pelo tratamento muito especial de "aa" - que prova que o dinamarquês é a cultura superior, é claro.
RenniePet
1
Pelo que vale, o dinamarquês classifica ä e aa de maneira diferente devido à localização das letras especiais æ (ae), ø (oe, ö) e å (aa, ä) no final do alfabeto na ordem escrita.
Alrekr
26

Invariante é um tipo de comparação linguisticamente apropriado.
Ordinal é um tipo binário de comparação. (mais rápido)
Consulte http://www.siao2.com/2004/12/29/344136.aspx

DanH
fonte
5

Aqui está um exemplo em que a comparação de igualdade de string usando InvariantCultureIgnoreCase e OrdinalIgnoreCase não fornecerá os mesmos resultados:

string str = "\xC4"; //A with umlaut, Ä
string A = str.Normalize(NormalizationForm.FormC);
//Length is 1, this will contain the single A with umlaut character (Ä)
string B = str.Normalize(NormalizationForm.FormD);
//Length is 2, this will contain an uppercase A followed by an umlaut combining character
bool equals1 = A.Equals(B, StringComparison.OrdinalIgnoreCase);
bool equals2 = A.Equals(B, StringComparison.InvariantCultureIgnoreCase);

Se você executar isso, igual a 1 será falso e igual a 2 será verdadeiro.

Dwedit
fonte
Apenas para adicionar outro exemplo semelhante, mas com literais de seqüência de caracteres, se a="\x00e9"(e aguda) e b="\x0065\x0301"(e combinadas com um acento agudo), StringComparer.Ordinal.Equals(a, b)retornará falso enquanto StringComparer.InvariantCulture.Equals(a, b)retornará verdadeiro.
George Helyar
2

Não é necessário usar exemplos sofisticados de caracteres unicode para mostrar a diferença. Aqui está um exemplo simples que descobri hoje que é surpreendente, consistindo apenas em caracteres ASCII.

De acordo com a tabela ASCII, 0(0x48) é menor que _(0x95) quando comparado ordinalmente. InvariantCulture diria o contrário (código do PowerShell abaixo):

PS> [System.StringComparer]::Ordinal.Compare("_", "0")
47
PS> [System.StringComparer]::InvariantCulture.Compare("_", "0")
-1
KFL
fonte
-7

Sempre tente usar InvariantCulture nos métodos de string que o aceitam como sobrecarga. Usando InvariantCulture, você está em um lado seguro. Muitos programadores .NET podem não usar essa funcionalidade, mas se o seu software for usado por diferentes culturas, o InvariantCulture é um recurso extremamente útil.

George
fonte
3
Se o seu software não for usado por culturas diferentes, é muito mais lento que o Ordinal.
Kyle
4
Eu considerei voto negativo, porque você certamente não pensou em sua resposta aleatória. Embora dentro dele haja um grão de verdade. SE o seu aplicativo está espalhado em massa entre várias culturas ... Isso certamente não garante suas palavras iniciais de "Sempre tente usar InvariantCulture", não é? Estou surpreso que você não tenha voltado ao longo dos anos para editar essa loucura depois de receber um voto negativo e talvez mais experiência.
Suamere