Maiuscolo vs minuscolo


86

Quando si eseguono confronti senza distinzione tra maiuscole e minuscole, è più efficiente convertire la stringa in maiuscolo o minuscolo? Ha importanza?

In questo post SO si suggerisce che C # sia più efficiente con ToUpper perché "Microsoft lo ha ottimizzato in questo modo". Ma ho anche letto questo argomento secondo cui la conversione di ToLower rispetto a ToUpper dipende da cosa contengono più stringhe e che in genere le stringhe contengono più caratteri minuscoli che rendono ToLower più efficiente.

In particolare, vorrei sapere:

  • C'è un modo per ottimizzare ToUpper o ToLower in modo che uno sia più veloce dell'altro?
  • È più veloce fare un confronto senza distinzione tra maiuscole e minuscole tra stringhe maiuscole o minuscole e perché?
  • Esistono ambienti di programmazione (es. C, C #, Python, qualunque cosa) in cui un caso è chiaramente migliore dell'altro e perché?

Risposte:


90

La conversione in maiuscolo o minuscolo per fare confronti senza distinzione tra maiuscole e minuscole non è corretta a causa delle caratteristiche "interessanti" di alcune culture, in particolare la Turchia. Utilizzare invece un StringComparer con le opzioni appropriate.

MSDN ha alcune ottime linee guida sulla gestione delle stringhe. Potresti anche voler verificare che il tuo codice superi il test della Turchia .

EDIT: nota il commento di Neil sui confronti ordinali senza distinzione tra maiuscole e minuscole. L'intero regno è piuttosto oscuro :(


15
Sì StringComparer è ottimo, ma la domanda non ha avuto risposta ... In situazioni in cui non è possibile utilizzare StringComparer come un'istruzione swtich su una stringa; dovrei ToUpper o ToLower nell'interruttore?
joshperry

7
Utilizzare un StringComparer e "if" / "else" invece di utilizzare ToUpper o ToLower.
Jon Skeet,

5
John, so che la conversione in minuscolo non è corretta, ma non avevo sentito dire che la conversione in maiuscolo non è corretta. Potete offrire un esempio o un riferimento? L'articolo MSDN a cui hai collegato dice questo: "I confronti effettuati utilizzando OrdinalIgnoreCase sono comportamentali la composizione di due chiamate: chiamare ToUpperInvariant su entrambi gli argomenti di stringa ed eseguire un confronto ordinale." Nella sezione intitolata "Operazioni su stringhe ordinali", lo ribadisce nel codice.
Neil

2
@ Neil: Interessante, non avevo visto quel pezzo. Per un confronto ordinale senza distinzione tra maiuscole e minuscole, immagino che sia abbastanza giusto. Dopotutto, deve scegliere qualcosa . Per i confronti senza distinzione tra maiuscole e minuscole culturalmente sensibili, penso che ci sarebbe ancora spazio per qualche comportamento strano. Indicherò il tuo commento nella risposta ...
Jon Skeet

4
@Triynko: Penso che sia importante concentrarsi principalmente sulla correttezza, con il punto che ottenere la risposta sbagliata velocemente di solito non è meglio (ea volte è peggio) che ottenere la risposta sbagliata lentamente.
Jon Skeet

25

Da Microsoft su MSDN:

Best practice per l'utilizzo di stringhe in .NET Framework

Raccomandazioni per l'uso delle stringhe

Perché? Da Microsoft :

Normalizza le stringhe in maiuscolo

C'è un piccolo gruppo di caratteri che una volta convertito in minuscolo non può fare un viaggio di andata e ritorno.

Qual è l'esempio di un personaggio del genere che non può fare un viaggio di andata e ritorno?

  • Inizio : simbolo greco Rho (U + 03f1) ϱ
  • Maiuscolo: Capitale greca Rho (U + 03a1) Ρ
  • Minuscolo: piccolo greco Rho (U + 03c1) ρ

ϱ, Ρ , ρ

.NET Fiddle

Original: ϱ
ToUpper: Ρ
ToLower: ρ

Questo è il motivo per cui, se vuoi fare confronti senza distinzione tra maiuscole e minuscole, converti le stringhe in maiuscolo e non in minuscolo.

Quindi, se devi sceglierne uno, scegli Maiuscolo .


3
Tornando alla risposta alla domanda originale: ci sono lingue che conoscono più di una variante minuscola per una variante maiuscola. A meno che tu non conosca le regole per quando usare quale rappresentazione (un altro esempio in greco: lettera sigma minuscola, usi σ all'inizio della parola o al centro, ς alla fine delle parole (vedi en.wikipedia.org/wiki/Sigma ), non è possibile riconvertire in modo sicuro alla variante minuscola.
Aconcagua

In realtà che dire del tedesco "ß", se lo chiami ToUpper()si trasformerà in "SS" su molti sistemi. Quindi anche questo in realtà non è disponibile per il viaggio di andata e ritorno.
Sebastian

se Microsoft ha ottimizzato il codice per eseguire confronti maiuscoli è perché il codice ASCII per le lettere maiuscole ha solo due cifre 65-90 mentre il codice ASCII Lettere minuscole 97-122 che contiene 3 cifre (necessita di più elaborazione)
Medo Medo

Va notato che sia "ϱ" che "ς" restituiscono se stessi da ToUpperInvariant(), quindi sarebbe comunque bello vedere esempi reali di perché maiuscolo è meglio di minuscolo
max630

Questa risposta non sembra essere pertinente. Secondo il collegamento Microsoft, ciò è importante solo quando si cambiano le impostazioni internazionali di una stringa: "Fare un round trip significa convertire i caratteri da una locale a un'altra che rappresenta i dati dei caratteri in modo diverso, e quindi recuperare accuratamente i caratteri originali dal caratteri convertiti. " Ma la domanda non implica la conversione in una lingua diversa.
ToolmakerSteve

18

Secondo MSDN è più efficiente passare le stringhe e dire al confronto di ignorare il caso:

String.Compare (strA, strB, StringComparison.OrdinalIgnoreCase) è equivalente a ( ma più veloce ) chiamare

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

Questi confronti sono ancora molto rapidi.

Naturalmente, se stai confrontando una stringa più e più volte, questo potrebbe non essere valido.


11

Basato su stringhe che tendono ad avere più voci minuscole, ToLower dovrebbe teoricamente essere più veloce (molti confronti, ma pochi incarichi).

In C, o quando si utilizzano elementi accessibili individualmente di ogni stringa (come le stringhe C o il tipo di stringa STL in C ++), in realtà è un confronto di byte, quindi il confronto UPPERnon è diverso da lower.

Se fossi subdolo e invece caricassi le tue stringhe in longarray, otterresti un confronto molto veloce sull'intera stringa perché potrebbe confrontare 4 byte alla volta. Tuttavia, il tempo di caricamento potrebbe non renderlo utile.

Perché hai bisogno di sapere qual è il più veloce? A meno che tu non stia facendo un carico metrico di confronti, uno che esegue un paio di cicli più velocemente è irrilevante per la velocità di esecuzione complessiva e suona come un'ottimizzazione prematura :)


11
Per rispondere alla domanda perché ho bisogno di sapere quale è più veloce: non ho bisogno di sapere, voglio solo sapere. :) Si tratta semplicemente di vedere qualcuno fare un'affermazione (come "confrontare stringhe maiuscole è più veloce!") E voler sapere se è veramente vero e / o perché ha fatto tale affermazione.
Parappa

1
ha senso - anch'io sono eternamente curioso di cose come questa :)
warren

1
Con le stringhe C, per convertire se tin matrici di long in modo tale che le stringhe siano uguali se e e se le matrici sono uguali devi camminare su set finché non trovi il '\0'carattere di terminazione (oppure potresti confrontare spazzatura oltre la fine delle stringhe, che può essere un accesso illegale alla memoria che richiama un comportamento indefinito). Ma allora perché non fare i confronti semplicemente camminando sui personaggi uno per uno? Con le stringhe C ++, puoi probabilmente ottenere la lunghezza e .c_str(), eseguire il cast a long *e confrontare un prefisso di lunghezza .size() - .size()%(sizeof long). A me sembra un po 'strano, comunque.
Jonas Kölker

@ JonasKölker - caricare la stringa in un array di longs solo per motivi di confronto sarebbe sciocco. Ma se lo stai facendo "molto", potrei vedere un possibile argomento per farlo.
warren

5

Microsoft ha ottimizzato ToUpperInvariant(), no ToUpper(). La differenza è che l'invariante è più adatto alla cultura. Se è necessario eseguire confronti senza distinzione tra maiuscole e minuscole su stringhe che possono variare in cultura, utilizzare Invariant, altrimenti le prestazioni della conversione invariante non dovrebbero avere importanza.

Non posso dire se ToUpper () o ToLower () sia più veloce però. Non l'ho mai provato da quando non ho mai avuto una situazione in cui le prestazioni fossero così importanti.


se Microsoft ha ottimizzato il codice per eseguire confronti in maiuscolo è perché il codice ASCII per le lettere maiuscole ha solo due cifre 65 - 90 mentre il codice ASCII Lettere minuscole 97-122 che contiene 3 cifre (necessita di più elaborazione)?
Medo Medo

4
@Medo Non ricordo i motivi esatti dell'ottimizzazione, ma 2 vs 3 cifre non è quasi certamente il motivo poiché tutte le lettere sono memorizzate come numeri binari, quindi le cifre decimali non hanno davvero significato in base al modo in cui sono memorizzate.
Dan Herbert

3

Se stai confrontando stringhe in C #, è molto più veloce usare .Equals () invece di convertire entrambe le stringhe in lettere maiuscole o minuscole. Un altro grande vantaggio per l'utilizzo di .Equals () è che non viene allocata più memoria per le 2 nuove stringhe maiuscole / minuscole.


4
E come bonus, se scegli le opzioni giuste, otterrai effettivamente i risultati corretti :)
Jon Skeet

0

Non dovrebbe mai importare. Con i caratteri ASCII, sicuramente non ha importanza: sono solo alcuni confronti e un po 'di capovolgimento per entrambe le direzioni. Unicode potrebbe essere un po 'più complicato, dal momento che ci sono alcuni caratteri che cambiano maiuscolo in modi strani, ma in realtà non dovrebbe esserci alcuna differenza a meno che il tuo testo non sia pieno di quei caratteri speciali.


0

Facendolo correttamente, dovrebbe esserci un piccolo, insignificante vantaggio di velocità se converti in minuscolo, ma questo, come molti hanno accennato, dipende dalla cultura e non è ereditato dalla funzione ma dalle stringhe che converti (molte lettere minuscole significa poche assegnazioni alla memoria) - la conversione in maiuscolo è più veloce se hai una stringa con molte lettere maiuscole.


0

Volevo alcuni dati reali su questo, quindi ho estratto l'elenco completo dei caratteri a due byte che falliscono con ToLowero ToUpper. Ho quindi eseguito questo test di seguito:

using System;

class Program {
   static void Main() {
      char[][] pairs = {
new[]{'\u00E5','\u212B'},new[]{'\u00C5','\u212B'},new[]{'\u0399','\u1FBE'},
new[]{'\u03B9','\u1FBE'},new[]{'\u03B2','\u03D0'},new[]{'\u03B5','\u03F5'},
new[]{'\u03B8','\u03D1'},new[]{'\u03B8','\u03F4'},new[]{'\u03D1','\u03F4'},
new[]{'\u03B9','\u1FBE'},new[]{'\u0345','\u03B9'},new[]{'\u0345','\u1FBE'},
new[]{'\u03BA','\u03F0'},new[]{'\u00B5','\u03BC'},new[]{'\u03C0','\u03D6'},
new[]{'\u03C1','\u03F1'},new[]{'\u03C2','\u03C3'},new[]{'\u03C6','\u03D5'},
new[]{'\u03C9','\u2126'},new[]{'\u0392','\u03D0'},new[]{'\u0395','\u03F5'},
new[]{'\u03D1','\u03F4'},new[]{'\u0398','\u03D1'},new[]{'\u0398','\u03F4'},
new[]{'\u0345','\u1FBE'},new[]{'\u0345','\u0399'},new[]{'\u0399','\u1FBE'},
new[]{'\u039A','\u03F0'},new[]{'\u00B5','\u039C'},new[]{'\u03A0','\u03D6'},
new[]{'\u03A1','\u03F1'},new[]{'\u03A3','\u03C2'},new[]{'\u03A6','\u03D5'},
new[]{'\u03A9','\u2126'},new[]{'\u0398','\u03F4'},new[]{'\u03B8','\u03F4'},
new[]{'\u03B8','\u03D1'},new[]{'\u0398','\u03D1'},new[]{'\u0432','\u1C80'},
new[]{'\u0434','\u1C81'},new[]{'\u043E','\u1C82'},new[]{'\u0441','\u1C83'},
new[]{'\u0442','\u1C84'},new[]{'\u0442','\u1C85'},new[]{'\u1C84','\u1C85'},
new[]{'\u044A','\u1C86'},new[]{'\u0412','\u1C80'},new[]{'\u0414','\u1C81'},
new[]{'\u041E','\u1C82'},new[]{'\u0421','\u1C83'},new[]{'\u1C84','\u1C85'},
new[]{'\u0422','\u1C84'},new[]{'\u0422','\u1C85'},new[]{'\u042A','\u1C86'},
new[]{'\u0463','\u1C87'},new[]{'\u0462','\u1C87'}
      };
      int upper = 0, lower = 0;
      foreach (char[] pair in pairs) {
         Console.Write(
            "U+{0:X4} U+{1:X4} pass: ",
            Convert.ToInt32(pair[0]),
            Convert.ToInt32(pair[1])
         );
         if (Char.ToUpper(pair[0]) == Char.ToUpper(pair[1])) {
            Console.Write("ToUpper ");
            upper++;
         } else {
            Console.Write("        ");
         }
         if (Char.ToLower(pair[0]) == Char.ToLower(pair[1])) {
            Console.Write("ToLower");
            lower++;
         }
         Console.WriteLine();
      }
      Console.WriteLine("upper pass: {0}, lower pass: {1}", upper, lower);
   }
}

Risultato sotto. Nota ho anche provato con le Invariantversioni e il risultato è stato lo stesso. È interessante notare che una delle coppie fallisce con entrambi. Ma basato su questo ToUpper è l'opzione migliore .

U+00E5 U+212B pass:         ToLower
U+00C5 U+212B pass:         ToLower
U+0399 U+1FBE pass: ToUpper
U+03B9 U+1FBE pass: ToUpper
U+03B2 U+03D0 pass: ToUpper
U+03B5 U+03F5 pass: ToUpper
U+03B8 U+03D1 pass: ToUpper
U+03B8 U+03F4 pass:         ToLower
U+03D1 U+03F4 pass:
U+03B9 U+1FBE pass: ToUpper
U+0345 U+03B9 pass: ToUpper
U+0345 U+1FBE pass: ToUpper
U+03BA U+03F0 pass: ToUpper
U+00B5 U+03BC pass: ToUpper
U+03C0 U+03D6 pass: ToUpper
U+03C1 U+03F1 pass: ToUpper
U+03C2 U+03C3 pass: ToUpper
U+03C6 U+03D5 pass: ToUpper
U+03C9 U+2126 pass:         ToLower
U+0392 U+03D0 pass: ToUpper
U+0395 U+03F5 pass: ToUpper
U+03D1 U+03F4 pass:
U+0398 U+03D1 pass: ToUpper
U+0398 U+03F4 pass:         ToLower
U+0345 U+1FBE pass: ToUpper
U+0345 U+0399 pass: ToUpper
U+0399 U+1FBE pass: ToUpper
U+039A U+03F0 pass: ToUpper
U+00B5 U+039C pass: ToUpper
U+03A0 U+03D6 pass: ToUpper
U+03A1 U+03F1 pass: ToUpper
U+03A3 U+03C2 pass: ToUpper
U+03A6 U+03D5 pass: ToUpper
U+03A9 U+2126 pass:         ToLower
U+0398 U+03F4 pass:         ToLower
U+03B8 U+03F4 pass:         ToLower
U+03B8 U+03D1 pass: ToUpper
U+0398 U+03D1 pass: ToUpper
U+0432 U+1C80 pass: ToUpper
U+0434 U+1C81 pass: ToUpper
U+043E U+1C82 pass: ToUpper
U+0441 U+1C83 pass: ToUpper
U+0442 U+1C84 pass: ToUpper
U+0442 U+1C85 pass: ToUpper
U+1C84 U+1C85 pass: ToUpper
U+044A U+1C86 pass: ToUpper
U+0412 U+1C80 pass: ToUpper
U+0414 U+1C81 pass: ToUpper
U+041E U+1C82 pass: ToUpper
U+0421 U+1C83 pass: ToUpper
U+1C84 U+1C85 pass: ToUpper
U+0422 U+1C84 pass: ToUpper
U+0422 U+1C85 pass: ToUpper
U+042A U+1C86 pass: ToUpper
U+0463 U+1C87 pass: ToUpper
U+0462 U+1C87 pass: ToUpper
upper pass: 46, lower pass: 8
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.