Differenza tra InvariantCulture e confronto di stringhe ordinali


548

Quando si confrontano due stringhe in c # per l'uguaglianza, qual è la differenza tra InvariantCulture e Ordinal comparation?



2
Per quelli che usano String1.Equals(String2, StringComparison.Ordinal), è meglio usare ciò String1 == String2che è intrinsecamente String1.Equals(String2)ed è di default un confronto ordinale sensibile al maiuscolo / minuscolo.
Ghasan,

3
@Ghasan Non sono sicuro che ciò renda =="migliore", ma è a) più breve, b) meno esplicito su ciò che fa esattamente e c) String1può essere nullo senza il confronto che getta a NullReferenceException.
Eugene Beresovsky,

3
@Ghasan le migliori pratiche ufficiali MSDN per l'utilizzo delle stringhe nella pagina .NET Framework ( msdn.microsoft.com/en-us/library/… ) consiglia l'uso di sovraccarichi che specificano esplicitamente il StringComparisontipo. Nel caso del confronto tra stringhe, significa String.Equals.
Ohad Schneider,

3
@EugeneBeresovsky Per evitare NullReferenceExceptionsi può semplicemente utilizzare il metodo statico: String.Equals(string1, string2, StringComparison.Ordinal).
Ohad Schneider,

Risposte:


302

InvariantCulture

Utilizza un set "standard" di ordini di caratteri (a, b, c, ... ecc.). Ciò è in contrasto con alcuni locali specifici, che possono ordinare i caratteri in diversi ordini ('a-with-acute' può essere prima o dopo 'a', a seconda delle impostazioni locali e così via).

Ordinale

D'altra parte, guarda puramente i valori dei byte grezzi che rappresentano il carattere.


C'è un ottimo esempio su http://msdn.microsoft.com/en-us/library/e6883c06.aspx che mostra i risultati dei vari valori StringComparison. Alla fine, mostra (estratto):

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)

Puoi vedere quello dove produce InvariantCulture (U + 0069, U + 0049, U + 00131), i rendimenti ordinali (U + 0049, U + 0069, U + 00131).


25
Il confronto ordinale esamina i punti di codice , non i byte.
Joey,

144
Mi sento come un'informazione utile, ma in realtà non risponde alla domanda. Quando si determina l' uguaglianza di due stringhe, c'è qualche motivo per usare InvarintCulture invece di Ordinal? Sembra che InvariantCulture sarebbe usato per ordinare le stringhe e Ordinal dovrebbe essere usato per il controllo di uguaglianza (non ci interessa che l'accento-a venga prima o dopo a, è semplicemente diverso). Tuttavia, io stesso non sono sicuro di questo punto.
MPavlak,

18
Vedere msdn.microsoft.com/en-us/library/ms230117%28v=vs.90%29.aspx e notare che si consiglia la normalizzazione delle stringhe e il confronto ordinale.
MPavlak,

23
Ordinal è molto più veloce
Darren,

9
Esistono buoni risultati dei test delle prestazioni pubblicati Test di confronto delle stringhe C # che indicano le prestazioni di ogni diverso metodo di confronto delle stringhe e il loro tempo.
Kumar C,

262

Ad esempio, importa: esiste una cosa chiamata espansione del personaggio

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

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

Con InvariantCultureil carattere ß viene espanso in ss.


1
Questa cosa differisce anche in qualche modo tra Ordinale InvariantCulture? Ecco di cosa tratta la domanda originale.
Matthijs Wessels,

3
Per quelli che non lo sanno ß, va notato che ßalmeno in tedesco equivale a una doppia s, Fonte: en.wikipedia.org/wiki/%C3%9F
Peter

20
@Peter non è del tutto corretto, non puoi usare ße ssintercambiabile in tedesco (sono un madrelingua). Ci sono casi in cui entrambi sono legali (ma spesso uno è obsoleto / non raccomandato) e ci sono casi in cui è consentito un solo modulo.
Enzi,

5
Questo semplice esempio dimostra chiaramente la differenza tra i 2 confronti. Penso che sto ricevendo questo ora.
BrianLegg,

4
Dovevo provarlo: ideone.com/j8DvFate così bello! Una piccola lezione anche in tedesco.
Mi chiedo

111

Indicando le migliori pratiche per l'utilizzo delle stringhe in .NET Framework :

  • Utilizzare StringComparison.Ordinalo StringComparison.OrdinalIgnoreCaseper i confronti come valore predefinito sicuro per la corrispondenza delle stringhe indipendenti dalla cultura.
  • Utilizzare i confronti con StringComparison.Ordinalo StringComparison.OrdinalIgnoreCaseper prestazioni migliori.
  • Utilizzare le operazioni non linguistiche StringComparison.Ordinalo StringComparison.OrdinalIgnoreCasevalori anziché le stringhe in base a CultureInfo.InvariantCulturequando il confronto è linguisticamente irrilevante (simbolico, ad esempio).

E infine:

  • StringComparison.InvariantCultureNella maggior parte dei casi non utilizzare le operazioni di stringa basate su . Una delle poche eccezioni è quando si persistono dati linguisticamente significativi ma culturalmente agnostici.

56

Un'altra utile differenza (in inglese in cui gli accenti sono rari) è che un confronto InvariantCulture confronta prima le stringhe senza distinzione tra maiuscole e minuscole, quindi, se necessario (e richiesto), distingue per caso dopo aver prima confrontato solo su lettere distinte. (Puoi anche fare un confronto senza distinzione tra maiuscole e minuscole, ovviamente, che non si distingue per caso.) Corretto:Le lettere accentate sono considerate come un altro sapore delle stesse lettere e la stringa viene confrontata prima ignorando gli accenti e quindi calcolandoli se le lettere generali corrispondono tutte (come nel caso diverso, tranne che alla fine non vengono ignorate in un confronto senza distinzione tra maiuscole e minuscole). Questo raggruppa le versioni accentate della parola altrimenti identica l'una vicino all'altra invece di separarle completamente alla prima differenza di accento. Questo è il tipo di ordinamento che potresti trovare in un dizionario, con le parole in maiuscolo che appaiono accanto ai loro equivalenti minuscoli e le lettere accentate che si trovano vicino alla corrispondente lettera non accentata.

Un confronto ordinale confronta rigorosamente i valori dei caratteri numerici, fermandosi alla prima differenza. Questo ordina le lettere maiuscole completamente separate dalle lettere minuscole (e le lettere accentate presumibilmente separate da quelle), quindi le parole maiuscole non si collocano da nessuna parte vicino ai loro equivalenti minuscoli.

InvariantCulture considera anche le maiuscole più grandi delle lettere minuscole, mentre Ordinale considera le maiuscole meno che minuscole (un blocco di ASCII dai vecchi tempi prima che i computer avessero lettere minuscole, le lettere maiuscole venivano allocate per prime e quindi avevano valori più bassi delle lettere minuscole aggiunto in seguito).

Ad esempio, da Ordinal: "0" < "9" < "A" < "Ab" < "Z" < "a" < "aB" < "ab" < "z" < "Á" < "Áb" < "á" < "áb"

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


Ho dato un'altra occhiata a questo e ho notato un'incoerenza tra l'esempio di InvariantCulture e la mia spiegazione sulla gestione dei personaggi accentati. L'esempio sembra essere corretto, quindi ho corretto la spiegazione per essere coerente. Il confronto InvariantCulture non si ferma al primo accento diverso e sembra prendere in considerazione una differenza di accento sulla stessa lettera solo se il resto delle stringhe corrisponde oltre agli accenti e al caso. Una differenza di accento viene quindi considerata prima di una differenza di caso precedente, quindi "Aaba" <"aába".
Rob Parker,

31

Sebbene la domanda riguardi l' uguaglianza , per un rapido riferimento visivo, qui l'ordine di alcune stringhe è stato ordinato usando un paio di culture che illustrano alcune delle idiosincrasie là fuori.

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 ß     

osservazioni:

  • de-DE, ja-JPe en-USordina allo stesso modo
  • Invariantsolo una specie sse in modo ßdiverso dalle tre culture precedenti
  • da-DK ordina in modo abbastanza diverso
  • la IgnoreCasebandiera è importante per tutte le culture campionate

Il codice utilizzato per generare la tabella sopra:

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));
}

1
Hmmm - OK, è bello che tu abbia fatto questa ricerca e pubblicato i tuoi risultati, anche se non sono esattamente sicuro di quale sia il tuo punto. Comunque, il danese potrebbe non essere una delle "culture più importanti" (anche se 5 milioni di danesi sono in realtà piuttosto affezionati alla loro cultura), ma se si inserisce "aa" come stringa di test aggiuntiva e "da-DK" in as un'ulteriore cultura di test, vedrai alcuni risultati interessanti.
RenniePet,

1
@RenniePet Grazie per quello. Ho aggiunto il danese, in quanto ordina in modo molto diverso rispetto alle altre 3 culture utilizzate. (Dato che le emoticon che indicano l'ironia non sembrano essere ben comprese nel web della lettura della lingua inglese come avrei immaginato, ho rimosso il commento sulle "culture più importanti". Dopotutto, il BCL non presenta un CultureComparerche potremmo usare per verificare. Per questa tabella, la Danishcultura (informazioni) si è rivelata molto importante.)
Eugene Beresovsky,

1
Grazie. Mi sono reso conto che il tuo commento sulle "culture più importanti" doveva essere preso con un granello di sale - è solo che sono diventato troppo vecchio per usare le emoticon. Immagino che mandare SMS sia diventato così comune che usare le emoticon sia un po 'come spiegare le tue battute dopo averle raccontate, indipendentemente dal fatto che qualcuno rida o meno. Per inciso, le altre culture scandinave (finlandese, norvegese e svedese) sono le stesse del danese, ad eccezione della gestione molto speciale di "aa" - che dimostra che il danese è la cultura superiore, ovviamente.
RenniePet,

1
Per quello che vale, il danese ordina ä e aa in modo diverso a causa della posizione delle lettere speciali æ (ae), ø (oe, ö) e å (aa, ä) alla fine dell'alfabeto nell'ordine scritto.
Alrekr


5

Ecco un esempio in cui il confronto dell'uguaglianza delle stringhe usando InvariantCultureIgnoreCase e OrdinalIgnoreCase non darà gli stessi risultati:

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 lo esegui, equals1 sarà falso e equals2 sarà vero.


Solo per aggiungere un altro esempio simile, ma con valori letterali di stringa, se a="\x00e9"(e acuto) e b="\x0065\x0301"(e combinato con un accento acuto), StringComparer.Ordinal.Equals(a, b)restituirà false mentre StringComparer.InvariantCulture.Equals(a, b)restituirà true.
George Helyar, l'

2

Non c'è bisogno di usare fantasiosi esempi di caratteri Unicode per mostrare la differenza. Ecco un semplice esempio che ho scoperto oggi che è sorprendente, costituito solo da caratteri ASCII.

Secondo la tabella ASCII, 0(0x48) è inferiore a _(0x95) se confrontato ordinariamente. InvariantCulture direbbe il contrario (codice PowerShell di seguito):

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

-7

Cerca sempre di utilizzare InvariantCulture in quei metodi stringa che lo accettano come sovraccarico. Usando InvariantCulture sei al sicuro. Molti programmatori .NET potrebbero non utilizzare questa funzionalità ma se il tuo software verrà utilizzato da culture diverse, InvariantCulture è una funzionalità estremamente utile.


3
Se il tuo software non verrà utilizzato da culture diverse, è comunque molto più lento di Ordinal.
Kyle

4
Ho considerato il downvoting perché sicuramente non hai pensato alla tua risposta casuale. Anche se al suo interno c'è un granello di verità. SE la tua applicazione è diffusa in massa tra più culture ... Ciò non garantisce le tue parole iniziali di "Cerca sempre di usare InvariantCulture", vero? Sono sorpreso che non sia tornato indietro nel corso degli anni per modificare questa follia dopo aver ricevuto un voto negativo, e forse più esperienza.
Suamere,
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.