Diferencia entre InvariantCulture y comparación de cadenas ordinales

548

Al comparar dos cadenas en c # para la igualdad, ¿cuál es la diferencia entre InvariantCulture y la comparación ordinal?

Kapil
fuente
¿Quizás siao2.com/2004/12/29/344136.aspx ? (googleado)
Cheese Daneish
2
Para aquellos que usan String1.Equals(String2, StringComparison.Ordinal), es mejor usar el String1 == String2que es intrínsecamente String1.Equals(String2)y es, por defecto, una comparación entre mayúsculas y minúsculas ordinales.
Ghasan
3
@Ghasan No estoy seguro de si eso hace =="mejor", pero es a) más corto, b) menos explícito sobre lo que hace exactamente yc) String1puede ser nulo sin la comparación arrojando a NullReferenceException.
Eugene Beresovsky
3
@Ghasan, las mejores prácticas oficiales de MSDN para usar cadenas en la página de .NET Framework ( msdn.microsoft.com/en-us/library/… ) recomienda el uso de sobrecargas que especifican explícitamente el StringComparisontipo. En el caso de la comparación de cadenas, significa String.Equals.
Ohad Schneider
3
Para evitar @EugeneBeresovsky NullReferenceExceptionpuede simplemente usar el método estático: String.Equals(string1, string2, StringComparison.Ordinal).
Ohad Schneider

Respuestas:

302

Cultura Invariante

Utiliza un conjunto "estándar" de ordenamiento de caracteres (a, b, c, ... etc.). Esto contrasta con algunas configuraciones regionales específicas, que pueden ordenar los caracteres en diferentes órdenes ('a-with-acute' puede estar antes o después de 'a', dependiendo de la configuración regional, y así sucesivamente).

Ordinal

Por otro lado, analiza únicamente los valores de los bytes sin procesar que representan el carácter.


Hay una gran muestra en http://msdn.microsoft.com/en-us/library/e6883c06.aspx que muestra los resultados de los distintos valores de StringComparison. Todo el camino al final, muestra (extracto):

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)

Puede ver que donde InvariantCulture rinde (U + 0069, U + 0049, U + 00131), Ordinal rinde (U + 0049, U + 0069, U + 00131).

JaredReisinger
fuente
25
La comparación ordinaria mira los puntos de código , no los bytes.
Joey el
144
Siento que es información útil, pero en realidad no responde la pregunta. Al determinar la igualdad de dos cadenas, ¿hay alguna razón para usar InvarintCulture en lugar de Ordinal? Parece que InvariantCulture se usaría para ordenar cadenas, y Ordinal debería usarse para la verificación de igualdad (no nos importa que acentuado a aparezca antes o después de a, simplemente es diferente). Sin embargo, yo mismo estoy un poco inseguro de este punto.
MPavlak
18
Consulte msdn.microsoft.com/en-us/library/ms230117%28v=vs.90%29.aspx y observe que se recomienda la normalización de cadenas y la comparación ordinal.
MPavlak
23
Ordinal es mucho más rápido
Darren
99
Hay buenos resultados de las pruebas de rendimiento publicadas C # String Comparision Tests que indican el rendimiento de cada método de comparación de cadenas y su tiempo.
Kumar C
262

Sí importa, por ejemplo: hay una cosa llamada expansión de personaje

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

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

Con InvariantCultureel carácter ß se expande a ss.

Ventsyslav Raikov
fuente
1
¿Esto también difiere de alguna manera entre Ordinaly InvariantCulture? De eso se trata la pregunta original.
Matthijs Wessels
3
Para aquellos que no saben ß, debe tenerse en cuenta que ßal menos en alemán equivale a un doble s, Fuente: en.wikipedia.org/wiki/%C3%9F
Peter
20
Eso no es del todo correcto @Peter, no puedes usarlo ße ssintercambiablemente en alemán (soy un hablante nativo). Hay casos en que ambos son legales (pero a menudo uno está desactualizado / no recomendado) y hay casos en los que solo se permite un formulario.
enzi
55
Este simple ejemplo demuestra claramente la diferencia entre las 2 comparaciones. Creo que estoy entendiendo esto ahora.
BrianLegg
44
Tuve que probarlo: ideone.com/j8Dv ¡ Qué genial! Una pequeña lección de alemán también. Preguntándome cuál es la diferencia entre ß y ss ahora ...
Mzn
111

Señalando las mejores prácticas para usar cadenas en .NET Framework :

  • Use StringComparison.Ordinalo StringComparison.OrdinalIgnoreCasepara comparaciones como su valor predeterminado seguro para la coincidencia de cadenas independiente de la cultura.
  • Use comparaciones con StringComparison.Ordinalo StringComparison.OrdinalIgnoreCasepara un mejor rendimiento.
  • Utilice los valores no lingüísticos StringComparison.Ordinalo en StringComparison.OrdinalIgnoreCaselugar de las operaciones de cadena en función de CultureInfo.InvariantCulturecuándo la comparación es lingüísticamente irrelevante (simbólica, por ejemplo).

Y finalmente:

  • No utilice operaciones de cadena basadas StringComparison.InvariantCultureen la mayoría de los casos . Una de las pocas excepciones es cuando persiste datos lingüísticamente significativos pero culturalmente agnósticos.
Dariusz
fuente
56

Otra diferencia útil (en inglés, donde los acentos son poco comunes) es que una comparación de InvariantCulture compara las cadenas completas por mayúsculas y minúsculas primero, y luego si es necesario (y solicitado) distingue por mayúsculas y minúsculas después de primero comparar solo las letras distintas. (También puede hacer una comparación entre mayúsculas y minúsculas, por supuesto, que no distinguirá entre mayúsculas y minúsculas). Corregido:Las letras acentuadas se consideran otro sabor de las mismas letras y la cadena se compara primero ignorando los acentos y luego contabilizándolos si todas las letras generales coinciden (al igual que con mayúsculas y minúsculas, excepto que finalmente no se ignoran en una comparación que no distingue entre mayúsculas y minúsculas). Esto agrupa versiones acentuadas de la misma palabra, por lo demás, cercanas entre sí en lugar de estar completamente separadas en la primera diferencia de acento. Este es el orden de clasificación que normalmente encontraría en un diccionario, con palabras en mayúscula que aparecen justo al lado de sus equivalentes en minúsculas, y las letras acentuadas están cerca de la letra sin acento correspondiente.

Una comparación ordinal se compara estrictamente con los valores de caracteres numéricos, deteniéndose en la primera diferencia. Esto clasifica las letras en mayúscula completamente separadas de las letras minúsculas (y las letras acentuadas presumiblemente separadas de esas), por lo que las palabras en mayúscula no se clasificarían cerca de sus equivalentes en minúsculas.

InvariantCulture también considera que las mayúsculas son más grandes que las minúsculas, mientras que Ordinal considera que las mayúsculas son menos que minúsculas (un remanente de ASCII de los viejos tiempos antes de que las computadoras tuvieran letras minúsculas, las letras mayúsculas se asignaron primero y por lo tanto tenían valores más bajos que las minúsculas añadido más tarde).

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

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

Rob Parker
fuente
Eché otro vistazo a esto y noté una inconsistencia entre el ejemplo de InvariantCulture y mi explicación sobre el manejo de caracteres acentuados. El ejemplo parece ser correcto, por lo que he corregido la explicación para que sea coherente. La comparación InvariantCulture no se detiene en el primer acento diferente y parece considerar solo una diferencia de acento en la misma letra si el resto de las cadenas coinciden además de los acentos y las mayúsculas y minúsculas. Luego se considera una diferencia de acento antes de una diferencia de caso anterior, entonces "Aaba" <"aába".
Rob Parker
31

Aunque la pregunta es sobre igualdad , para una referencia visual rápida, aquí el orden de algunas cadenas ordenadas usando un par de culturas que ilustran algunas de las idiosincrasias existentes.

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 ß     

Observaciones:

  • de-DE, ja-JPy en-USordenar de la misma manera
  • Invariantsolo clasifica ssy de manera ßdiferente a las tres culturas anteriores
  • da-DK se clasifica de manera bastante diferente
  • la IgnoreCasebandera es importante para todas las culturas muestreadas

El código utilizado para generar la tabla anterior:

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
fuente
1
Hmmm - OK, es bueno que hayas hecho esta investigación y publicado tus hallazgos, aunque no estoy exactamente seguro de cuál es tu punto. De todos modos, es posible que el danés no sea una de las "culturas más importantes" (aunque 5 millones de daneses son bastante aficionados a su cultura), pero si agrega "aa" como una cadena de prueba adicional y "da-DK" como una cultura de prueba adicional, verá algunos resultados interesantes.
RenniePet
1
@ RenniePet Gracias por eso. Agregué danés, ya que se clasifica de manera bastante diferente a las otras 3 culturas utilizadas. (Como los emoticones que indican ironía no parecen ser tan bien entendidos en la web de lectura en inglés como habría supuesto, eliminé el comentario de las "culturas más importantes". Después de todo, el BCL no presenta uno CultureComparerque podamos usar para verificar. Para esta tabla, la Danishcultura (información) resultó ser muy importante.)
Eugene Beresovsky
1
Gracias. Me di cuenta de que su comentario sobre las "culturas más importantes" estaba destinado a ser tomado con un grano de sal, es solo que me he vuelto demasiado viejo para usar emoticones. Me imagino que enviar mensajes de texto se ha vuelto tan común que usar emoticones es como explicar tus bromas después de contarlas, independientemente de si alguien se ríe o no. Por cierto, las otras culturas escandinavas (finlandesas, noruegas y suecas) son las mismas que las danesas, excepto por el manejo muy especial de "aa", lo que demuestra que el danés es la cultura superior, por supuesto.
RenniePet
1
Para lo que vale, el danés clasifica ä y aa de manera diferente debido a la ubicación de las letras especiales æ (ae), ø (oe, ö) y å (aa, ä) al final del alfabeto en el orden escrito.
Alrek
5

Aquí hay un ejemplo donde la comparación de igualdad de cadenas usando InvariantCultureIgnoreCase y OrdinalIgnoreCase no dará los mismos 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);

Si ejecuta esto, equals1 será falso y equals2 será verdadero.

Dwedit
fuente
Solo para agregar otro ejemplo similar, pero con literales de cadena, si a="\x00e9"(e agudo) y b="\x0065\x0301"(e combinado con un acento agudo), StringComparer.Ordinal.Equals(a, b)devolverá falso mientras StringComparer.InvariantCulture.Equals(a, b)que devolverá verdadero.
George Helyar
2

No es necesario usar extractos de caracteres unicode para mostrar la diferencia. Aquí hay un ejemplo simple que descubrí hoy que es sorprendente, que consiste solo en caracteres ASCII.

De acuerdo con la tabla ASCII, 0(0x48) es menor que _(0x95) en comparación ordinalmente. InvariantCulture diría lo contrario (código de PowerShell a continuación):

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

Siempre trate de usar InvariantCulture en esos métodos de cadena que lo aceptan como sobrecarga. Al usar InvariantCulture estás en un lado seguro. Muchos programadores .NET pueden no usar esta funcionalidad, pero si su software será utilizado por diferentes culturas, InvariantCulture es una característica extremadamente útil.

Jorge
fuente
3
Si su software no será utilizado por diferentes culturas, es mucho más lento que Ordinal.
Kyle
44
Pensé en votar abajo porque ciertamente no pensaste en tu respuesta casual. Aunque en su interior hay un grano de verdad. SI su aplicación se extiende en masa entre varias culturas ... Eso ciertamente no garantiza sus palabras iniciales de "Siempre trate de usar la Cultura Invariante", ¿verdad? Me sorprende que no haya regresado a lo largo de los años para editar esta locura después de recibir un voto negativo, y tal vez más experiencia.
Suamere