Mayúsculas vs minúsculas

85

Al hacer comparaciones que no distinguen entre mayúsculas y minúsculas, ¿es más eficiente convertir la cadena a mayúsculas o minúsculas? ¿Incluso importa?

En esta publicación de SO se sugiere que C # es más eficiente con ToUpper porque "Microsoft lo optimizó de esa manera". Pero también he leído este argumento de que la conversión de ToLower frente a ToUpper depende de lo que contengan más cadenas de caracteres, y que normalmente las cadenas contienen más caracteres en minúsculas, lo que hace que ToLower sea más eficiente.

En particular, me gustaría saber:

  • ¿Hay alguna forma de optimizar ToUpper o ToLower de modo que uno sea más rápido que el otro?
  • ¿Es más rápido hacer una comparación sin distinción entre mayúsculas y minúsculas y por qué?
  • ¿Existen entornos de programación (por ejemplo, C, C #, Python, lo que sea) donde un caso es claramente mejor que el otro y por qué?
Parappa
fuente

Respuestas:

90

La conversión a mayúsculas o minúsculas para realizar comparaciones que no distinguen entre mayúsculas y minúsculas es incorrecta debido a las características "interesantes" de algunas culturas, especialmente Turquía. En su lugar, utilice StringComparer con las opciones adecuadas.

MSDN tiene excelentes pautas sobre el manejo de cadenas. También es posible que desee verificar que su código pase la prueba de Turquía .

EDITAR: Tenga en cuenta el comentario de Neil sobre las comparaciones ordinales que no distinguen entre mayúsculas y minúsculas. Todo este reino es bastante turbio :(

Jon Skeet
fuente
15
Sí, StringComparer es genial, pero la pregunta no fue respondida ... En situaciones en las que no puede usar StringComparer como una declaración swtich contra una cadena; ¿Debo ToUpper o ToLower en el interruptor?
joshperry
7
Utilice un StringComparer y "if" / "else" en lugar de utilizar ToUpper o ToLower.
Jon Skeet
5
John, sé que convertir a minúsculas es incorrecto, pero no había escuchado que convertir a mayúsculas es incorrecto. ¿Puede ofrecer un ejemplo o una referencia? El artículo de MSDN al que vinculó dice lo siguiente: "Las comparaciones realizadas con OrdinalIgnoreCase son conductualmente la composición de dos llamadas: llamar a ToUpperInvariant en ambos argumentos de cadena y realizar una comparación ordinal". En la sección titulada "Operaciones de cadenas ordinales", se repite esto en el código.
Neil
2
@Neil: Interesante, no había visto esa parte. Para una comparación ordinal que no distingue entre mayúsculas y minúsculas, supongo que es bastante justo. Tiene que elegir algo , después de todo. Para las comparaciones que no distinguen entre mayúsculas y minúsculas y que son culturalmente sensibles, creo que todavía habría lugar para algún comportamiento extraño. Señalará su comentario en la respuesta ...
Jon Skeet
4
@Triynko: Creo que es importante concentrarse principalmente en la corrección, con el punto de que obtener la respuesta incorrecta rápidamente no suele ser mejor (y a veces es peor) que obtener la respuesta incorrecta lentamente.
Jon Skeet
25

De Microsoft en MSDN:

Prácticas recomendadas para el uso de cadenas en .NET Framework

Recomendaciones para el uso de cadenas

¿Por qué? De Microsoft :

Normalizar cadenas a mayúsculas

Hay un pequeño grupo de caracteres que cuando se convierten a minúsculas no pueden realizar un viaje de ida y vuelta.

¿Cuál es el ejemplo de un personaje que no puede hacer un viaje de ida y vuelta?

  • Inicio : Símbolo griego Rho (U + 03f1) ϱ
  • Mayúsculas: mayúscula griega Rho (U + 03a1) Ρ
  • Minúscula: Rho griega pequeña (U + 03c1) ρ

ϱ, Ρ , ρ

.NET Fiddle

Original: ϱ
ToUpper: Ρ
ToLower: ρ

Por eso, si desea hacer comparaciones que no distingan entre mayúsculas y minúsculas, convierta las cadenas a mayúsculas y no a minúsculas.

Entonces, si tiene que elegir uno, elija Mayúsculas .

Ian Boyd
fuente
y cual es la razon
bjan
@bjan La razón es que es malo no hacerlo.
Ian Boyd
1
¿Qué grupo de personajes? ¿Qué significa hacer un viaje de ida y vuelta?
johv
1
@johv Desde el enlace: "Hacer un viaje de ida y vuelta significa convertir los caracteres de una localidad a otra que represente los datos de caracteres de forma diferente, y luego recuperar con precisión los caracteres originales de los caracteres convertidos". ¿Qué grupo de personajes? No lo sé, pero voy a adivinar la minúscula ien turco, cuando se convierte İ, en lugar de la Ique estás acostumbrado. Además, estamos acostumbrados a Iconvertir en mayúsculas i, pero en Turquía se convierte en ı.
Ian Boyd
3
De vuelta a la respuesta a la pregunta original: Hay idiomas que conocen más de una variante en minúsculas para una variante en mayúsculas. A menos que conozca las reglas sobre cuándo usar qué representación (otro ejemplo en griego: letra sigma pequeña, use σ al comienzo de la palabra o en el medio, ς al final de las palabras (consulte en.wikipedia.org/wiki/Sigma ), no se puede convertir de nuevo de forma segura a la variante de caja inferior.
Aconcagua
19

Según MSDN , es más eficiente pasar las cadenas y decirle a la comparación que ignore el caso:

String.Compare (strA, strB, StringComparison.OrdinalIgnoreCase) es equivalente a ( pero más rápido que ) llamar

String.Compare (ToUpperInvariant (strA), ToUpperInvariant (strB), StringComparison.Ordinal).

Estas comparaciones siguen siendo muy rápidas.

Por supuesto, si está comparando una cadena una y otra vez, es posible que esto no sea así.

Rob Walker
fuente
12

Basado en cadenas que tienden a tener más entradas en minúsculas, ToLower debería ser teóricamente más rápido (muchas comparaciones, pero pocas asignaciones).

En C, o cuando se utilizan elementos accesibles individualmente de cada cadena (como cadenas C o el tipo de cadena de STL en C ++), en realidad es una comparación de bytes, por lo que comparar UPPERno es diferente de lower.

Si fuera astuto y cargara sus cadenas en longmatrices, obtendría una comparación muy rápida en toda la cadena porque podría comparar 4 bytes a la vez. Sin embargo, el tiempo de carga puede hacer que no valga la pena.

¿Por qué necesitas saber cuál es más rápido? A menos que esté haciendo una gran cantidad de comparaciones métricas, una que se ejecute un par de ciclos más rápido es irrelevante para la velocidad de ejecución general y suena como una optimización prematura :)

madriguera
fuente
11
Para responder a la pregunta de por qué necesito saber cuál es más rápido: no necesito saber, simplemente quiero saber. :) Es simplemente un caso de ver a alguien hacer una afirmación (como "¡comparar cadenas en mayúsculas es más rápido!") Y querer saber si es realmente cierto y / o por qué hizo esa afirmación.
Parappa
1
que tiene sentido - Estoy eternamente curiosa en este tipo de cosas, también :)
Warren
Con cadenas C, para convertir sy ten matrices de longs de modo que las cadenas sean iguales si las matrices son iguales, debe caminar por syt hasta encontrar el '\0'carácter de terminación (o de lo contrario, puede comparar la basura más allá del final de las cadenas, que puede ser un acceso ilegal a la memoria que invoca un comportamiento indefinido). Pero entonces, ¿por qué no hacer las comparaciones mientras recorre los personajes uno por uno? Con cadenas de C ++, probablemente pueda obtener la longitud y .c_str(), convertir a long *ay comparar un prefijo de longitud .size() - .size()%(sizeof long). Aunque me parece un poco sospechoso.
Jonas Kölker
6

Microsoft ha optimizado ToUpperInvariant(), no ToUpper(). La diferencia es que invariante es más amigable con la cultura. Si necesita hacer comparaciones que no distingan entre mayúsculas y minúsculas en cadenas que pueden variar en la cultura, use Invariant; de lo contrario, el rendimiento de la conversión invariante no debería importar.

Sin embargo, no puedo decir si ToUpper () o ToLower () es más rápido. Nunca lo probé, ya que nunca había tenido una situación en la que el rendimiento fuera tan importante.

Dan Herbert
fuente
si Microsoft ha optimizado el código para realizar comparaciones en mayúsculas, ¿es porque el código ASCII para letras mayúsculas tiene solo dos dígitos 65 - 90 mientras que el código ASCII letras minúsculas 97-122 que contiene 3 dígitos (necesita más procesamiento)?
Medo Medo
3
@Medo No recuerdo las razones exactas de la optimización, pero es casi seguro que 2 vs 3 dígitos no es la razón, ya que todas las letras se almacenan como números binarios, por lo que los dígitos decimales realmente no tienen significado según la forma en que se almacenan.
Dan Herbert
4

Si está haciendo una comparación de cadenas en C #, es significativamente más rápido usar .Equals () en lugar de convertir ambas cadenas a mayúsculas o minúsculas. Otra gran ventaja de usar .Equals () es que no se asigna más memoria para las 2 nuevas cadenas de mayúsculas / minúsculas.

Jon Tackabury
fuente
4
Y como beneficio adicional, si elige las opciones correctas, en realidad le dará los resultados correctos :)
Jon Skeet
1

Realmente nunca debería importar. Con los caracteres ASCII, definitivamente no importa: son solo algunas comparaciones y un poco de cambio en cualquier dirección. Unicode puede ser un poco más complicado, ya que hay algunos caracteres que cambian entre mayúsculas y minúsculas de formas extrañas, pero realmente no debería haber ninguna diferencia a menos que su texto esté lleno de esos caracteres especiales.

Adam Rosenfield
fuente
1

Haciéndolo bien, debería haber una ventaja de velocidad pequeña e insignificante si convierte a minúsculas, pero esto, como muchos han insinuado, depende de la cultura y no se hereda en la función sino en las cadenas que convierte (muchas letras minúsculas significa pocas asignaciones a la memoria): convertir a mayúsculas es más rápido si tiene una cadena con muchas letras mayúsculas.

Más claro
fuente
0

Depende. Como se indicó anteriormente, solo ASCII simple, es idéntico. En .NET, lea y use String.Compare su correcto para las cosas i18n (culturas de idiomas y unicode). Si sabe algo sobre la probabilidad de la entrada, use el caso más común.

Recuerde, si está haciendo varias comparaciones de cadenas, la longitud es un excelente primer discriminador.

Sanjaya R
fuente
-2

Si se trata de ASCII puro, no importa. Es solo un OR x, 32 frente a un AND x, 224. Unicode, no tengo ni idea ...

Brian Knoblauch
fuente
4
Esto es completamente incorrecto: OR con 32 solo funciona para AZ y los caracteres 64-127; arruina a todos los demás personajes. Hacer Y con 32 es aún más incorrecto: el resultado siempre será 0 (nulo) o 32 (espacio).
Adam Rosenfield