Diferencia entre InvariantCulture y Ordinal string comparison

Al comparar dos cadenas en c # para la igualdad, ¿cuál es la diferencia entre InvariantCulture y Ordinal comparison?

InvariantCulture

Utiliza un conjunto “estándar” de ordenamientos de caracteres (a, b, c, … etc.). Esto está en contraste con algunas configuraciones regionales específicas, que pueden ordenar caracteres en diferentes órdenes (‘a-con-agudo’ puede ser antes o después de ‘a’, dependiendo de la configuración regional, y así sucesivamente).

Ordinal

Por otro lado, mira puramente los valores de los bytes crudos que representan el personaje.


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

 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), los rendimientos ordinales (U + 0049, U + 0069, U + 00131).

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

  var s1 = "Strasse"; var s2 = "Straße"; s1.Equals(s2, StringComparison.Ordinal); //false s1.Equals(s2, StringComparison.InvariantCulture); //true 

Con InvariantCulture el carácter ß se expande a ss.

Apuntando a las mejores prácticas para usar cadenas en .NET Framework :

  • Utilice StringComparison.Ordinal o StringComparison.OrdinalIgnoreCase para las comparaciones como su valor predeterminado seguro para la coincidencia de cadena cultural-agnóstica.
  • Use comparaciones con StringComparison.Ordinal o StringComparison.OrdinalIgnoreCase para un mejor rendimiento.
  • Utilice los valores no lingüísticos StringComparison.Ordinal o StringComparison.OrdinalIgnoreCase lugar de operaciones de cadena basadas en CultureInfo.InvariantCulture cuando la comparación sea lingüísticamente irrelevante (simbólica, por ejemplo).

Y finalmente:

  • No use operaciones de cadena basadas en StringComparison.InvariantCulture en la mayoría de los casos . Una de las pocas excepciones es cuando persisten datos lingüísticamente significativos pero culturalmente agnósticos.

Otra diferencia importante (en inglés, donde los acentos son poco comunes) es que una comparación InvariantCulture compara las cadenas completas primero por mayúsculas y luego, si es necesario (y solicitado), se distingue por mayúsculas después de comparar primero solo en las letras distintas. (También puede hacer una comparación insensible a mayúsculas y minúsculas, por supuesto, que no distinguirá por caso). Corregida: 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 el caso diferente, excepto que finalmente no se ignora en una comparación insensible a mayúsculas y minúsculas). Esto agrupa las versiones acentuadas de la palabra que de otro modo sería la misma cerca una de la otra, 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 las palabras en mayúscula que aparecen junto a sus equivalentes en minúscula, y las letras acentuadas cerca de la letra correspondiente sin acentuar.

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

InvariantCulture también considera que las mayúsculas son mayores que minúsculas, mientras que Ordinal considera mayúsculas 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 agregado 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"

Invariant es un tipo de comparación lingüísticamente apropiado.
Ordinal es un tipo binario de comparación. (Más rápido)
Ver http://www.siao2.com/2004/12/29/344136.aspx

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

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

Observaciones:

  • de-DE , ja-JP y en-US clasifican de la misma manera
  • Invariant solo clasifica ss y ß diferente a las tres culturas anteriores
  • da-DK clasifica bastante diferente
  • la bandera IgnoreCase importante para todas las culturas incluidas en la muestra

El código utilizado para generar la tabla anterior:

 var l = new List { "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)); } 

Aquí hay un ejemplo donde la comparación de igualdad de cadenas utilizando 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, e igual2 será verdadero.

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

De acuerdo con la tabla ASCII, 0 (0x48) es menor que _ (0x95) cuando se compara 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 

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