Megjegyzés
Az oldalhoz való hozzáféréshez engedély szükséges. Megpróbálhat bejelentkezni vagy módosítani a címtárat.
Az oldalhoz való hozzáféréshez engedély szükséges. Megpróbálhatja módosítani a címtárat.
A .NET széles körű támogatást nyújt a honosított és globalizált alkalmazások fejlesztéséhez, és megkönnyíti a jelenlegi vagy egy adott kultúra konvenciók alkalmazását olyan gyakori műveletek végrehajtásakor, mint a sztringek rendezése és megjelenítése. A karakterláncok rendezése és összehasonlítása azonban nem mindig kulturálisan érzékeny művelet. Az alkalmazások által belsőleg használt sztringeket például általában minden kultúrában azonos módon kell kezelni. Ha a kulturálisan független sztringadatok, például XML-címkék, HTML-címkék, felhasználónevek, fájlelérési utak és a rendszerobjektumok nevei úgy vannak értelmezve, mintha kultúraérzékenyek lennének, az alkalmazáskódok apró hibákra, gyenge teljesítményre és bizonyos esetekben biztonsági problémákra lehetnek kitéve.
Ez a cikk a .NET-ben a sztringek rendezési, összehasonlítási és burkolatkezelési módszereit vizsgálja, javaslatot tesz a megfelelő sztringkezelési módszer kiválasztására, és további információkat nyújt a sztringkezelési módszerekről.
Javaslatok sztringhasználatra
Ha .NET keretrendszerrel fejleszt, kövesse ezeket a javaslatokat a karakterláncok összehasonlításakor.
Jótanács
A karakterlánccal kapcsolatos különböző metódusok összehasonlítást végeznek. Ilyenek például a következők: String.Equals, String.Compare, String.IndexOfés String.StartsWith.
- Használjon túlterheléseket, amelyek explicit módon határozzák meg a karakterlánc-összehasonlítási szabályokat a karakterláncműveletek esetében. Ez általában egy metódus túlterhelésének meghívását foglalja magában, amely típusparaméterrel StringComparisonrendelkezik.
- Az összehasonlításokhoz használja a StringComparison.Ordinal vagy StringComparison.OrdinalIgnoreCase karaktereket, mint biztonságos alapértelmezést a kultúrafüggetlen sztringegyeztetésekhez.
- Jobb teljesítmény érdekében használjon összehasonlítást a StringComparison.Ordinal vagy StringComparison.OrdinalIgnoreCase.
- A StringComparison.CurrentCulture alapján sztringműveleteket használjon, amikor kimenetet jelenít meg a felhasználónak.
- Sztringműveletek StringComparison.Ordinal helyett használjon nem nyelvi StringComparison.OrdinalIgnoreCase vagy CultureInfo.InvariantCulture értékeket, ha az összehasonlítás nyelvi szempontból irreleváns (például szimbolikus).
- Az String.ToUpperInvariant metódust használja az String.ToLowerInvariant metódus helyett, amikor sztringeket normalizál összehasonlításhoz.
- Használja a String.Equals metódus egy túlterhelt változatát annak tesztelésére, hogy két sztring egyenlő-e.
- A String.Compare és String.CompareTo metódusokat a sztringek rendezésére használja, ne pedig az egyenlőség ellenőrzésére.
- A nem sztringadatokat, például számokat és dátumokat a felhasználói felületen kulturális szempontból érzékeny formázással jelenítheti meg. A nem sztring típusú adatok sztring formában való megőrzéséhez használja a formázást az invariáns kultúrával .
Sztringek összehasonlítása esetén kerülje az alábbi eljárásokat:
- Ne használjon olyan túlterheléseket, amelyek nem explicit módon vagy implicit módon határozzák meg a sztring-összehasonlítási szabályokat a sztringműveletek esetében.
- Legtöbb esetben ne használjon sztringműveleteket StringComparison.InvariantCulture. A néhány kivétel egyike, ha nyelvileg jelentőséggel bíró, de kulturálisan agnosztikus adatokat őriz meg.
- Ne használja a String.Compare vagy CompareTo metódus túlterhelését, és ne tesztelje a nulla visszatérési értéket annak megállapításához, hogy két karakterlánc egyenlő-e.
Jótanács
A CA1307, CA1309 és CA1310 kódelemzési szabályok segítenek azonosítani azokat a híváshelyeket, ahol a nyelvi összehasonlítót véletlenül használják. Ha engedélyezni szeretné ezeket, és a szabálysértéseket építési hibákként szeretné megjeleníteni, állítsa be a következő tulajdonságokat a projektfájlban:
<PropertyGroup>
<AnalysisMode>All</AnalysisMode>
<WarningsAsErrors>$(WarningsAsErrors);CA1307;CA1309;CA1310</WarningsAsErrors>
</PropertyGroup>
Adja meg kifejezetten a szöveg-összehasonlításokat.
A .NET-ben a sztringmanipulálási módszerek többsége túlterhelt. Általában egy vagy több túlterhelés fogadja el az alapértelmezett beállításokat, míg mások nem fogadnak el alapértelmezett értékeket, és ehelyett meghatározzák a sztringek összehasonlításának vagy módosításának pontos módját. Az alapértelmezett értékekre nem támaszkodó metódusok többsége tartalmaz egy típusparamétert StringComparison, amely egy számbavétel, amely explicit módon határozza meg a sztringek összehasonlításának szabályait kultúra és eset szerint. Az alábbi táblázat az enumerálási StringComparison tagokat ismerteti.
StringComparison tag |
Leírás |
|---|---|
| CurrentCulture | Kis- és nagybetűket megkülönböztető összehasonlítást végez az aktuális kultúra szerint. |
| CurrentCultureIgnoreCase | Kis- és nagybetűkre nem érzékeny összehasonlítást hajt végre az aktuális kultúra alapján. |
| InvariantCulture | Az invariáns kultúra használatával kis- és nagybetűket megkülönböztető összehasonlítást végez. |
| InvariantCultureIgnoreCase | Kis- és nagybetűket nem érzékelyítő összehasonlítást végez az invariáns kultúra használatával. |
| Ordinal | Ordinális összehasonlítást végez. |
| OrdinalIgnoreCase | Kis- és nagybetűkre nem érzékeny ordinális összehasonlítás. |
A IndexOf metódus például, amely visszaadja egy karakternek vagy sztringnek megfelelő részsztring indexét egy String objektumban, kilenc túlterheléssel rendelkezik.
- IndexOf(Char), IndexOf(Char, Int32), és IndexOf(Char, Int32, Int32), amely alapértelmezés szerint egy ordinalis (kis- és nagybetűkre érzékeny és kultúrafüggetlen) keresést hajt végre a sztringben egy karakterre.
- IndexOf(String), IndexOf(String, Int32), és IndexOf(String, Int32, Int32), amely alapértelmezés szerint egy kis- és nagybetűkre érzékeny, valamint kultúraérzékeny keresést hajt végre a karakterláncban lévő részstringre.
- IndexOf(String, StringComparison), IndexOf(String, Int32, StringComparison)és IndexOf(String, Int32, Int32, StringComparison), amelyek olyan típusú StringComparison paramétert tartalmaznak, amely lehetővé teszi az összehasonlítás formájának megadását.
Javasoljuk, hogy az alábbi okokból válasszon túlterhelést, amely nem használ alapértelmezett értékeket:
Néhány túlterhelés (amelyek alapértelmezett paraméterekkel egy adott karaktert, például a Char-t keresnek a sztringpéldányban) sorszám-összehasonlítást végez, míg mások (amelyek sztringet keresnek a sztringpéldányban) kulturálisan érzékenyek. Nehéz megjegyezni, hogy melyik metódus melyik alapértelmezett értéket használja, és könnyen összekeverheti a túlterheléseket.
A metódushívások alapértelmezett értékeire támaszkodó kód szándéka nem egyértelmű. A következő példában, amely az alapértelmezett értékekre támaszkodik, nehéz megállapítani, hogy a fejlesztő valójában két sztring ordinális vagy nyelvi összehasonlítását szánta-e, vagy a
url.Schemeés a "https" közötti kis- és nagybetűk közötti különbség okozhatja-e az egyenlőségi teszt visszatérésétfalse.Uri url = new("https://learn.microsoft.com/"); // Incorrect if (string.Equals(url.Scheme, "https")) { // ...Code to handle HTTPS protocol. }Dim url As New Uri("https://learn.microsoft.com/") ' Incorrect If String.Equals(url.Scheme, "https") Then ' ...Code to handle HTTPS protocol. End If
Általában azt javasoljuk, hogy olyan metódust hívjon meg, amely nem támaszkodik az alapértelmezett értékekre, mert egyértelművé teszi a kód szándékát. Ez pedig olvashatóbbá és könnyebben kezelhetővé teszi a kódot. Az alábbi példa az előző példával kapcsolatban felmerült kérdésekre ad választ. Egyértelművé teszi, hogy a rendszer a sorszám-összehasonlítást használja, és hogy a különbségeket figyelmen kívül hagyja.
Uri url = new("https://learn.microsoft.com/");
// Correct
if (string.Equals(url.Scheme, "https", StringComparison.OrdinalIgnoreCase))
{
// ...Code to handle HTTPS protocol.
}
Dim url As New Uri("https://learn.microsoft.com/")
' Incorrect
If String.Equals(url.Scheme, "https", StringComparison.OrdinalIgnoreCase) Then
' ...Code to handle HTTPS protocol.
End If
A sztringek összehasonlításának részletei
A sztringek összehasonlítása számos sztringgel kapcsolatos művelet középpontjában áll, különösen az egyenlőség rendezése és tesztelése. Sztringek meghatározott sorrendben rendezve: Ha a "my" a "sztring" előtt jelenik meg a sztringek rendezett listájában, a "my" értéknek kisebbnek vagy egyenlőnek kell lennie a "sztring" értéknél. Emellett az összehasonlítás implicit módon definiálja az egyenlőséget. Az összehasonlító művelet nullát ad vissza az általa egyenlőnek ítélt sztringekhez. Jó megállapítás, hogy egyik sztring sem kisebb a másiknál. A sztringeket tartalmazó legérthetőbb műveletek közé tartozik az egyik vagy mindkét eljárás: egy másik sztringgel való összehasonlítás és egy jól definiált rendezési művelet végrehajtása.
Megjegyzés
Letöltheti a Rendezési súlytáblákat, a Windows operációs rendszerek rendezési és összehasonlítási műveleteihez használt karaktersúlyokra vonatkozó információkat tartalmazó szövegfájlokat, valamint a Linux és macOS rendszerekhez készült rendezési súlytáblázat legújabb verzióját, az Alapértelmezett Unicode rendezési elem táblát. A linuxos és macOS rendszerű rendezési súlytábla konkrét verziója a rendszeren telepített Unicode-kódtárak nemzetközi összetevőinek verziójától függ. Az ICU-verziókról és az általuk implementálható Unicode-verziókról további információt az ICU letöltése című témakörben talál.
Az egyenlőségi vagy rendezési sorrend két sztringjének kiértékelése azonban nem eredményez egyetlen, helyes eredményt; az eredmény a sztringek összehasonlításához használt feltételektől függ. Az aktuális kultúra vagy az invariáns kultúra (az angol nyelvre épülő területi-agnosztikus kultúra) keretezési és rendezési konvencióin alapuló sztring-összehasonlítások eltérő eredményeket eredményezhetnek.
Emellett a .NET különböző verzióit használó, illetve a .NET-et különböző operációs rendszereken vagy operációsrendszer-verziókon használó sztring-összehasonlítások eltérő eredményeket adhatnak vissza. A .NET az International Components for Unicode (ICU) kódtárat használja a nyelvi sztringek összehasonlításához az összes támogatott platformon. További információ: Strings and the Unicode Standard and .NET globalization and ICU.
Az aktuális kultúrát alkalmazó sztring-összehasonlítások
Az egyik kritérium a sztringek összehasonlítása során az aktuális kultúra konvencióit használja. Az aktuális kultúrán alapuló összehasonlítások a szál aktuális kultúráját vagy területi beállítását használják. Ha a kultúrát nem a felhasználó állítja be, alapértelmezés szerint az operációs rendszer beállítását veszi át. Mindig olyan összehasonlításokat kell használnia, amelyek a jelenlegi kultúrán alapulnak, amikor az adatok nyelvi szempontból relevánsak, és amikor azok a kultúra szempontjából érzékeny felhasználói interakciókat tükrözik.
A .NET-ben azonban az összehasonlítás és a burkolat viselkedése megváltozik, amikor a kultúra megváltozik. Ez akkor fordul elő, ha egy alkalmazás olyan számítógépen fut, amely más kultúrával rendelkezik, mint az a számítógép, amelyen az alkalmazást fejlesztették, vagy amikor a végrehajtó szál megváltoztatja a kultúráját. Ez a viselkedés szándékos, de sok fejlesztő számára nem nyilvánvaló. Az alábbi példa az amerikai angol ("en-US") és a svéd ("sv-SE") kultúrák közötti rendezési sorrend különbségeit mutatja be. Vegye figyelembe, hogy az "ångström", a "Windows" és a "Visual Studio" szavak a rendezett sztringtömbök különböző pozícióiban jelennek meg.
using System.Globalization;
// Words to sort
string[] values= { "able", "ångström", "apple", "Æble",
"Windows", "Visual Studio" };
// Current culture
Array.Sort(values);
DisplayArray(values);
// Change culture to Swedish (Sweden)
string originalCulture = CultureInfo.CurrentCulture.Name;
Thread.CurrentThread.CurrentCulture = new CultureInfo("sv-SE");
Array.Sort(values);
DisplayArray(values);
// Restore the original culture
Thread.CurrentThread.CurrentCulture = new CultureInfo(originalCulture);
static void DisplayArray(string[] values)
{
Console.WriteLine($"Sorting using the {CultureInfo.CurrentCulture.Name} culture:");
foreach (string value in values)
Console.WriteLine($" {value}");
Console.WriteLine();
}
// The example displays the following output:
// Sorting using the en-US culture:
// able
// Æble
// ångström
// apple
// Visual Studio
// Windows
//
// Sorting using the sv-SE culture:
// able
// apple
// Visual Studio
// Windows
// ångström
// Æble
Imports System.Globalization
Imports System.Threading
Module Program
Sub Main()
' Words to sort
Dim values As String() = {"able", "ångström", "apple", "Æble",
"Windows", "Visual Studio"}
' Current culture
Array.Sort(values)
DisplayArray(values)
' Change culture to Swedish (Sweden)
Dim originalCulture As String = CultureInfo.CurrentCulture.Name
Thread.CurrentThread.CurrentCulture = New CultureInfo("sv-SE")
Array.Sort(values)
DisplayArray(values)
' Restore the original culture
Thread.CurrentThread.CurrentCulture = New CultureInfo(originalCulture)
End Sub
Sub DisplayArray(values As String())
Console.WriteLine($"Sorting using the {CultureInfo.CurrentCulture.Name} culture:")
For Each value As String In values
Console.WriteLine($" {value}")
Next
Console.WriteLine()
End Sub
End Module
' The example displays the following output:
' Sorting using the en-US culture:
' able
' Æble
' ångström
' apple
' Visual Studio
' Windows
'
' Sorting using the sv-SE culture:
' able
' apple
' Visual Studio
' Windows
' ångström
' Æble
A szál aktuális kultúráját használó kis- és nagybetűérzéketlen összehasonlítások megegyeznek a kultúraérzékeny összehasonlításokkal, azzal a különbséggel, hogy figyelmen kívül hagyják a kis- és nagybetűket az aktuális kultúra szerint. Lehet, hogy ez a viselkedés a rendezési sorrendekben is megnyilvánul.
Az alábbi módszerek alapértelmezetten az aktuális kulturális szemantikát használó összehasonlításokat használják:
- String.Compare túlterhelések, amelyek nem tartalmazzák a StringComparison paramétert.
- String.CompareTo Túlterhelés.
- Az alapértelmezett String.StartsWith(String) metódus és a String.StartsWith(String, Boolean, CultureInfo) paraméterrel
nullCultureInfo rendelkező metódus. - Az alapértelmezett String.EndsWith(String) metódus és a String.EndsWith(String, Boolean, CultureInfo) paraméterrel
nullCultureInfo rendelkező metódus. - String.IndexOf túlterhelések, amelyek String-t keresési paraméterként fogadnak el, és nem rendelkeznek StringComparison paraméterrel.
- String.LastIndexOf túlterhelések, amelyek String-t keresési paraméterként fogadnak el, és nem rendelkeznek StringComparison paraméterrel.
Mindenesetre azt javasoljuk, hogy olyan túlterhelést hívjon meg, amelynek paramétere StringComparison egyértelművé teszi a metódushívás szándékát.
Finom és nem annyira finom hibák akkor jelentkezhetnek, ha a nem nyelvi sztringadatokat nyelvileg értelmezik, vagy ha egy adott kultúrából származó sztringadatokat egy másik kultúra konvenciói alapján értelmeznek. A Turkish-I probléma a kanonikus példa.
Szinte minden latin ábécé esetében, beleértve az amerikai angolt is, az "i" (\u0069) karakter az "I" (\u0049) karakter kisbetűs változata. Ez a casing szabály gyorsan az alapértelmezetté válik egy ilyen kultúrában programozó személy számára. A török ("tr-TR") ábécé azonban tartalmaz egy "I with a dot" (İ) karaktert (\u0130), amely az "i" nagybetűs változata. A törökben szerepel egy pont nélküli kis "i" karakter, "ı" (\u0131), amely nagybetűvé "I" változik. Ez a viselkedés az azerbajdzsáni ("az") kultúrában is előfordul.
Ezért az "i" vagy az "I" nagybetűsítésével kapcsolatos feltételezések nem érvényesek minden kultúrában. Ha a sztring-összehasonlítási rutinokhoz az alapértelmezett túlterheléseket használja, akkor azok a kultúrák közötti eltérésnek lesznek kitéve. Ha az összehasonlítandó adatok nyelven kívüliek, az alapértelmezett beállítások nem kívánt eredményekhez vezethetnek, például ahogyan az alábbi kísérlet bemutatja a "bill" és "BILL" karakterláncok kis- és nagybetűkre nem érzékeny összehasonlítását.
using System.Globalization;
string name = "Bill";
Thread.CurrentThread.CurrentCulture = new CultureInfo("en-US");
Console.WriteLine($"Culture = {Thread.CurrentThread.CurrentCulture.DisplayName}");
Console.WriteLine($" Is 'Bill' the same as 'BILL'? {name.Equals("BILL", StringComparison.OrdinalIgnoreCase)}");
Console.WriteLine($" Does 'Bill' start with 'BILL'? {name.StartsWith("BILL", true, null)}");
Console.WriteLine();
Thread.CurrentThread.CurrentCulture = new CultureInfo("tr-TR");
Console.WriteLine($"Culture = {Thread.CurrentThread.CurrentCulture.DisplayName}");
Console.WriteLine($" Is 'Bill' the same as 'BILL'? {name.Equals("BILL", StringComparison.OrdinalIgnoreCase)}");
Console.WriteLine($" Does 'Bill' start with 'BILL'? {name.StartsWith("BILL", true, null)}");
//' The example displays the following output:
//'
//' Culture = English (United States)
//' Is 'Bill' the same as 'BILL'? True
//' Does 'Bill' start with 'BILL'? True
//'
//' Culture = Turkish (Türkiye)
//' Is 'Bill' the same as 'BILL'? True
//' Does 'Bill' start with 'BILL'? False
Imports System.Globalization
Imports System.Threading
Module Program
Sub Main()
Dim name As String = "Bill"
Thread.CurrentThread.CurrentCulture = New CultureInfo("en-US")
Console.WriteLine($"Culture = {Thread.CurrentThread.CurrentCulture.DisplayName}")
Console.WriteLine($" Is 'Bill' the same as 'BILL'? {name.Equals("BILL", StringComparison.OrdinalIgnoreCase)}")
Console.WriteLine($" Does 'Bill' start with 'BILL'? {name.StartsWith("BILL", True, Nothing)}")
Console.WriteLine()
Thread.CurrentThread.CurrentCulture = New CultureInfo("tr-TR")
Console.WriteLine($"Culture = {Thread.CurrentThread.CurrentCulture.DisplayName}")
Console.WriteLine($" Is 'Bill' the same as 'BILL'? {name.Equals("BILL", StringComparison.OrdinalIgnoreCase)}")
Console.WriteLine($" Does 'Bill' start with 'BILL'? {name.StartsWith("BILL", True, Nothing)}")
End Sub
End Module
' The example displays the following output:
'
' Culture = English (United States)
' Is 'Bill' the same as 'BILL'? True
' Does 'Bill' start with 'BILL'? True
'
' Culture = Turkish (Türkiye)
' Is 'Bill' the same as 'BILL'? True
' Does 'Bill' start with 'BILL'? False
Ez az összehasonlítás jelentős problémákat okozhat, ha a kultúrát véletlenül használják biztonsági szempontból érzékeny beállításokban, ahogyan az alábbi példában is látható. Egy metódushívás, például IsFileURI("file:") akkor ad true vissza, ha a jelenlegi kultúra amerikai angol, de false ha a jelenlegi kultúra török. Így a török rendszereken valaki megkerülheti a "FILE:" kezdetű, kis- és nagybetűkkel nem érzéketlen URI-khoz való hozzáférést megakadályozó biztonsági intézkedéseket.
public static bool IsFileURI(string path) =>
path.StartsWith("FILE:", true, null);
Public Shared Function IsFileURI(path As String) As Boolean
Return path.StartsWith("FILE:", True, Nothing)
End Function
Ebben az esetben, mivel a "file:" nem nyelvi, kulturális érzéketlen azonosítóként van értelmezve, a kódot inkább az alábbi példában látható módon kell megírni:
public static bool IsFileURI(string path) =>
path.StartsWith("FILE:", StringComparison.OrdinalIgnoreCase);
Public Shared Function IsFileURI(path As String) As Boolean
Return path.StartsWith("FILE:", StringComparison.OrdinalIgnoreCase)
End Function
Sorszámozott karakterlánc műveletek
A metódushívásban megadott StringComparison.Ordinal vagy StringComparison.OrdinalIgnoreCase érték nem nyelvi összehasonlítást jelent, amely figyelmen kívül hagyja a természetes nyelvek jellemzőit. Az ezekkel az StringComparison értékekkel meghívott metódusok a sztringműveleteket egyszerű bájt-összehasonlításokra alapozza a kultúra által paraméterezett casing vagy ekvivalenciatáblák helyett. Ez a megközelítés a legtöbb esetben a sztringek tervezett értelmezéséhez illeszkedik, miközben a kód gyorsabb és megbízhatóbb.
Az ordinális összehasonlítások olyan karakterlánc-összehasonlítások, amelyek során az egyes karakterláncok minden bájtját nyelvi értelmezés nélkül hasonlítják össze; például a "windows" nem egyezik a "Windows" kifejezéssel. Ez lényegében a C futtatókörnyezet strcmp függvény hívása. Ezt az összehasonlítást akkor használja, ha a környezet azt diktálja, hogy a karakterláncok pontosan egyezzenek meg, vagy konzervatív egyeztetési eljárást követeljen meg. Emellett a sorszámos összehasonlítás a leggyorsabb összehasonlítási művelet, mivel nem alkalmaz nyelvi szabályokat az eredmény meghatározásakor.
A OrdinalIgnoreCase összehasonlító továbbra is karakterenkénti alapon működik, de kiküszöböli a kis- és nagybetűk közötti különbségeket a művelet végrehajtása során. Összehasonlításkor a karakterpárok OrdinalIgnoreCase és 'd' az 'D' tekintendők, mint ahogy a karakterpárok és 'á' esetében is. A ékezet nélküli karakter 'a' azonban nem egyenlő az ékezetes karakterrel 'á'.
Erre néhány példát az alábbi táblázat tartalmaz:
| 1. sztring | Karakterlánc 2 |
Ordinal Összehasonlítás |
OrdinalIgnoreCase Összehasonlítás |
|---|---|---|---|
"dog" |
"dog" |
egyenlő | egyenlő |
"dog" |
"Dog" |
nem egyenlő | egyenlő |
"resume" |
"résumé" |
nem egyenlő | nem egyenlő |
A Unicode lehetővé teszi, hogy a sztringek több különböző memóriabeli ábrázolásokkal rendelkezzenek. Az e-akut (é) például kétféleképpen ábrázolható:
- Egyetlen literális
'é'karakter (más néven'\u00E9'). - Egy literális nem konstans
'e'karakter, amelyet egy ékezetmódosító karakter'\u0301'kombinálása követ.
Ez azt jelenti, hogy a következő négy sztring mind úgy jelenik meg, mint "résumé"annak ellenére, hogy azok alkotórészei eltérőek. A sztringek a literális 'é' karakterek vagy a literális ékezet nélküli 'e' karakterek kombinációját alkalmazzák az ékezetmódosító '\u0301' segítségével.
"r\u00E9sum\u00E9""r\u00E9sume\u0301""re\u0301sum\u00E9""re\u0301sume\u0301"
Egy sorszám-összehasonlítóval egyik karakterlánc sem egyezik a másikkal. Ennek az az oka, hogy mindegyik különböző mögöttes karaktersorozatokat tartalmaz, annak ellenére, hogy a képernyőre renderelve mindegyik ugyanúgy néz ki.
A string.IndexOf(..., StringComparison.Ordinal) művelet végrehajtásakor a futtatókörnyezet pontos részsztring egyezést keres. Az eredmények a következők.
Console.WriteLine("resume".IndexOf('e', StringComparison.Ordinal)); // "resume": prints '1'
Console.WriteLine("r\u00E9sum\u00E9".IndexOf('e', StringComparison.Ordinal)); // "résumé": prints '-1'
Console.WriteLine("r\u00E9sume\u0301".IndexOf('e', StringComparison.Ordinal)); // "résumé": prints '5'
Console.WriteLine("re\u0301sum\u00E9".IndexOf('e', StringComparison.Ordinal)); // "résumé": prints '1'
Console.WriteLine("re\u0301sume\u0301".IndexOf('e', StringComparison.Ordinal)); // "résumé": prints '1'
Console.WriteLine("resume".IndexOf('e', StringComparison.OrdinalIgnoreCase)); // "resume": prints '1'
Console.WriteLine("r\u00E9sum\u00E9".IndexOf('e', StringComparison.OrdinalIgnoreCase)); // "résumé": prints '-1'
Console.WriteLine("r\u00E9sume\u0301".IndexOf('e', StringComparison.OrdinalIgnoreCase)); // "résumé": prints '5'
Console.WriteLine("re\u0301sum\u00E9".IndexOf('e', StringComparison.OrdinalIgnoreCase)); // "résumé": prints '1'
Console.WriteLine("re\u0301sume\u0301".IndexOf('e', StringComparison.OrdinalIgnoreCase)); // "résumé": prints '1'
Sub IndexOfExample()
Console.WriteLine("resume".IndexOf("e"c, StringComparison.Ordinal)) ' "resume": prints '1'
Console.WriteLine(("r" & ChrW(&HE9) & "sum" & ChrW(&HE9)).IndexOf("e"c, StringComparison.Ordinal)) ' "résumé": prints '-1'
Console.WriteLine(("r" & ChrW(&HE9) & "sume" & ChrW(&H301)).IndexOf("e"c, StringComparison.Ordinal)) ' "résumé": prints '5'
Console.WriteLine(("re" & ChrW(&H301) & "sum" & ChrW(&HE9)).IndexOf("e"c, StringComparison.Ordinal)) ' "résumé": prints '1'
Console.WriteLine(("re" & ChrW(&H301) & "sume" & ChrW(&H301)).IndexOf("e"c, StringComparison.Ordinal)) ' "résumé": prints '1'
Console.WriteLine("resume".IndexOf("e"c, StringComparison.OrdinalIgnoreCase)) ' "resume": prints '1'
Console.WriteLine(("r" & ChrW(&HE9) & "sum" & ChrW(&HE9)).IndexOf("e"c, StringComparison.OrdinalIgnoreCase)) ' "résumé": prints '-1'
Console.WriteLine(("r" & ChrW(&HE9) & "sume" & ChrW(&H301)).IndexOf("e"c, StringComparison.OrdinalIgnoreCase)) ' "résumé": prints '5'
Console.WriteLine(("re" & ChrW(&H301) & "sum" & ChrW(&HE9)).IndexOf("e"c, StringComparison.OrdinalIgnoreCase)) ' "résumé": prints '1'
Console.WriteLine(("re" & ChrW(&H301) & "sume" & ChrW(&H301)).IndexOf("e"c, StringComparison.OrdinalIgnoreCase)) ' "résumé": prints '1'
End Sub
A szokásos keresési és összehasonlító rutinokat soha nem befolyásolja az aktuális szál kulturális beállítása.
A .NET-sztringek beágyazott null karaktereket (és más nem nyomtatható karaktereket) tartalmazhatnak. A sorszám- és kultúraérzékeny összehasonlítás (beleértve az invariáns kultúrát használó összehasonlításokat is) egyik legtisztább különbsége a beágyazott null karakterek sztringben való kezelésére vonatkozik. Ezeket a karaktereket a rendszer figyelmen kívül hagyja, amikor a String.Compare kulturális szempontból érzékeny összehasonlításokat és String.Equals metódusokat használja (beleértve az invariáns kultúrát használó összehasonlításokat is). Ennek eredményeképpen a beágyazott null karaktereket tartalmazó sztringek egyenlőnek tekinthetők azokkal a sztringekkel, amelyek nem. Beágyazott nem nyomtatható karakterek kihagyhatók a karakterlánc-összehasonlító módszerek, például String.StartsWith esetében.
Fontos
Bár a sztring-összehasonlító metódusok figyelmen kívül hagyják a beágyazott null karaktereket, a sztringkeresési módszerek, mint például a String.Contains, String.EndsWith, String.IndexOf, String.LastIndexOf és String.StartsWith nem.
Az alábbi példa az "Aa" sztring kultúraérzékeny összehasonlítását hajtja végre egy hasonló sztringgel, amely több beágyazott null karaktert tartalmaz az "A" és az "a" között, és megmutatja, hogy a két sztring hogyan tekinthető egyenlőnek:
string str1 = "Aa";
string str2 = "A" + new string('\u0000', 3) + "a";
Thread.CurrentThread.CurrentCulture = System.Globalization.CultureInfo.GetCultureInfo("en-us");
Console.WriteLine($"Comparing '{str1}' ({ShowBytes(str1)}) and '{str2}' ({ShowBytes(str2)}):");
Console.WriteLine(" With String.Compare:");
Console.WriteLine($" Current Culture: {string.Compare(str1, str2, StringComparison.CurrentCulture)}");
Console.WriteLine($" Invariant Culture: {string.Compare(str1, str2, StringComparison.InvariantCulture)}");
Console.WriteLine(" With String.Equals:");
Console.WriteLine($" Current Culture: {string.Equals(str1, str2, StringComparison.CurrentCulture)}");
Console.WriteLine($" Invariant Culture: {string.Equals(str1, str2, StringComparison.InvariantCulture)}");
string ShowBytes(string value)
{
string hexString = string.Empty;
for (int index = 0; index < value.Length; index++)
{
string result = Convert.ToInt32(value[index]).ToString("X4");
result = string.Concat(" ", result.Substring(0,2), " ", result.Substring(2, 2));
hexString += result;
}
return hexString.Trim();
}
// The example displays the following output:
// Comparing 'Aa' (00 41 00 61) and 'Aa' (00 41 00 00 00 00 00 00 00 61):
// With String.Compare:
// Current Culture: 0
// Invariant Culture: 0
// With String.Equals:
// Current Culture: True
// Invariant Culture: True
Module Program
Sub Main()
Dim str1 As String = "Aa"
Dim str2 As String = "A" & New String(Convert.ToChar(0), 3) & "a"
Console.WriteLine($"Comparing '{str1}' ({ShowBytes(str1)}) and '{str2}' ({ShowBytes(str2)}):")
Console.WriteLine(" With String.Compare:")
Console.WriteLine($" Current Culture: {String.Compare(str1, str2, StringComparison.CurrentCulture)}")
Console.WriteLine($" Invariant Culture: {String.Compare(str1, str2, StringComparison.InvariantCulture)}")
Console.WriteLine(" With String.Equals:")
Console.WriteLine($" Current Culture: {String.Equals(str1, str2, StringComparison.CurrentCulture)}")
Console.WriteLine($" Invariant Culture: {String.Equals(str1, str2, StringComparison.InvariantCulture)}")
End Sub
Function ShowBytes(str As String) As String
Dim hexString As String = String.Empty
For ctr As Integer = 0 To str.Length - 1
Dim result As String = Convert.ToInt32(str.Chars(ctr)).ToString("X4")
result = String.Concat(" ", result.Substring(0, 2), " ", result.Substring(2, 2))
hexString &= result
Next
Return hexString.Trim()
End Function
' The example displays the following output:
' Comparing 'Aa' (00 41 00 61) and 'Aa' (00 41 00 00 00 00 00 00 00 61):
' With String.Compare:
' Current Culture: 0
' Invariant Culture: 0
' With String.Equals:
' Current Culture: True
' Invariant Culture: True
End Module
A sztringek azonban nem tekinthetők egyenlőnek ordinalis összehasonlítás használatakor, ahogy az alábbi példa mutatja.
string str1 = "Aa";
string str2 = "A" + new String('\u0000', 3) + "a";
Console.WriteLine($"Comparing '{str1}' ({ShowBytes(str1)}) and '{str2}' ({ShowBytes(str2)}):");
Console.WriteLine(" With String.Compare:");
Console.WriteLine($" Ordinal: {string.Compare(str1, str2, StringComparison.Ordinal)}");
Console.WriteLine(" With String.Equals:");
Console.WriteLine($" Ordinal: {string.Equals(str1, str2, StringComparison.Ordinal)}");
string ShowBytes(string str)
{
string hexString = string.Empty;
for (int ctr = 0; ctr < str.Length; ctr++)
{
string result = Convert.ToInt32(str[ctr]).ToString("X4");
result = " " + result.Substring(0, 2) + " " + result.Substring(2, 2);
hexString += result;
}
return hexString.Trim();
}
// The example displays the following output:
// Comparing 'Aa' (00 41 00 61) and 'A a' (00 41 00 00 00 00 00 00 00 61):
// With String.Compare:
// Ordinal: 97
// With String.Equals:
// Ordinal: False
Module Program
Sub Main()
Dim str1 As String = "Aa"
Dim str2 As String = "A" & New String(Convert.ToChar(0), 3) & "a"
Console.WriteLine($"Comparing '{str1}' ({ShowBytes(str1)}) and '{str2}' ({ShowBytes(str2)}):")
Console.WriteLine(" With String.Compare:")
Console.WriteLine($" Ordinal: {String.Compare(str1, str2, StringComparison.Ordinal)}")
Console.WriteLine(" With String.Equals:")
Console.WriteLine($" Ordinal: {String.Equals(str1, str2, StringComparison.Ordinal)}")
End Sub
Function ShowBytes(str As String) As String
Dim hexString As String = String.Empty
For ctr As Integer = 0 To str.Length - 1
Dim result As String = Convert.ToInt32(str.Chars(ctr)).ToString("X4")
result = String.Concat(" ", result.Substring(0, 2), " ", result.Substring(2, 2))
hexString &= result
Next
Return hexString.Trim()
End Function
' The example displays the following output:
' Comparing 'Aa' (00 41 00 61) and 'A a' (00 41 00 00 00 00 00 00 00 61):
' With String.Compare:
' Ordinal: 97
' With String.Equals:
' Ordinal: False
End Module
A kis- és nagybetűket figyelmen kívül hagyó összehasonlítás a következő legkonzervatívabb megközelítés. Ezek az összehasonlítások figyelmen kívül hagyják a legtöbb burkolatot; például a "windows" egyezik a "Windows" értékkel. Az ASCII-karakterek kezelésekor ez a szabályzat egyenértékű StringComparison.Ordinalazzal a különbséggel, hogy figyelmen kívül hagyja a szokásos ASCII-burkolatot. Ezért az [A, Z] (\u0041-\u005A) bármely karaktere megegyezik az [a,z] (\u0061-\007A) megfelelő karakterével. Az ASCII-tartományon kívüli burkolatok az invariáns kultúra tábláit használják. Ezért a következő összehasonlítás:
string.Compare(strA, strB, StringComparison.OrdinalIgnoreCase);
String.Compare(strA, strB, StringComparison.OrdinalIgnoreCase)
egyenértékű az összehasonlítással (de gyorsabb), mint:
string.Compare(strA.ToUpperInvariant(), strB.ToUpperInvariant(), StringComparison.Ordinal);
String.Compare(strA.ToUpperInvariant(), strB.ToUpperInvariant(), StringComparison.Ordinal)
Ezek az összehasonlítások még mindig nagyon gyorsak.
Mindkettőt StringComparison.Ordinal , és StringComparison.OrdinalIgnoreCase használja közvetlenül a bináris értékeket, és a legjobban alkalmas az egyeztetésre. Ha nem biztos az összehasonlítási beállításokban, használja a két érték egyikét. Mivel azonban bájtonkénti összehasonlítást végeznek, nem nyelvi rendezési sorrend (például angol szótár) szerint rendeznek, hanem bináris rendezési sorrend szerint. Az eredmények a legtöbb környezetben furcsának tűnhetnek, ha a felhasználók számára megjelennek.
Az ordinális szemantika az alapértelmezett viselkedés az olyan túlterhelések esetében, amelyek nem tartalmaznak String.Equals argumentumot (ideértve az egyenlőségi operátort is). Mindenesetre azt javasoljuk, hogy egy StringComparison paraméterrel rendelkező túlterhelést hívjon meg.
Nyelvi karakterláncok összehasonlítása
A nyelvi keresési és összehasonlító rutinok rendezési elemekre bontanak egy sztringet, és ezeken az elemeken végeznek keresést vagy összehasonlításokat. Nem feltétlenül van 1:1 leképezés egy karakterlánc karakterei és az azt alkotó összehasonlító elemek között. Egy 2. hosszúságú sztring például csak egyetlen rendezési elemből állhat. Ha két sztringet hasonlít össze nyelvtudatos módon, az összehasonlító ellenőrzi, hogy a két sztring rendezési elemei azonos szemantikai jelentéssel rendelkeznek-e, még akkor is, ha a sztring literális karakterei eltérőek.
Vegye figyelembe az előző szakaszban leírt sztringet "résumé" és annak négy különböző ábrázolását. Az alábbi táblázat az egyes ábrázolásokat rendezési elemekre bontva mutatja be.
| Lánc | Rendezési elemekként |
|---|---|
"r\u00E9sum\u00E9" |
"r" + "\u00E9" + "s" + "u" + "m" + "\u00E9" |
"r\u00E9sume\u0301" |
"r" + "\u00E9" + "s" + "u" + "m" + "e\u0301" |
"re\u0301sum\u00E9" |
"r" + "e\u0301" + "s" + "u" + "m" + "\u00E9" |
"re\u0301sume\u0301" |
"r" + "e\u0301" + "s" + "u" + "m" + "e\u0301" |
A rendezési elem lazán megfelel annak, amit az olvasók egyetlen karakternek vagy karakterhalmaznak gondolnának. Fogalmilag hasonló a gráffürthöz , de egy valamivel nagyobb esernyőt foglal magában.
Nyelvi összehasonlító esetén nincs szükség pontos egyezésekre. A rendezési elemeket inkább szemantikai jelentésük alapján hasonlítják össze. A nyelvi összehasonlítók például egyenlőként kezelik a részszűkítéseket "\u00E9""e\u0301" , mivel mindkettő szemantikailag "egy kisbetűt és egy akut hangsúlymódosítót" jelent. Ez lehetővé teszi, hogy a IndexOf metódus egy nagyobb sztringen belül egyezzen a szemantikailag egyenértékű részsztringgel "e\u0301""\u00E9", ahogy az az alábbi kódmintában látható.
Console.WriteLine("r\u00E9sum\u00E9".IndexOf("e")); // "résumé": prints '-1' (not found)
Console.WriteLine("r\u00E9sum\u00E9".IndexOf("\u00E9")); // "résumé": prints '1'
Console.WriteLine("\u00E9".IndexOf("e\u0301")); // prints '0'
Sub IndexOfStringExample()
Console.WriteLine(("r" & ChrW(&HE9) & "sum" & ChrW(&HE9)).IndexOf("e")) ' "résumé": prints '-1' (not found)
Console.WriteLine(("r" & ChrW(&HE9) & "sum" & ChrW(&HE9)).IndexOf(ChrW(&HE9).ToString())) ' "résumé": prints '1'
Console.WriteLine(ChrW(&HE9).ToString().IndexOf("e" & ChrW(&H301))) ' prints '0'
End Sub
Ennek következtében két különböző hosszúságú karakterlánc egyenlő lehet, ha nyelvi összehasonlítást használ. A hívónak ügyelnie kell arra, hogy az ilyen helyzetekben ne alkalmazzanak különleges logikát, amely a karakterlánc hosszával kapcsolatos.
A kultúratudatos keresési és összehasonlító rutinok a nyelvi keresési és összehasonlító rutinok speciális formái. A kultúratudatos összehasonlítók esetében a rendezési elem fogalma ki van terjesztve a megadott kultúrára jellemző információkra.
A magyar ábécében például, amikor a két karakter <dz> visszafelé jelenik meg, a d vagy <z> betűtől <> eltérő egyedi betűnek számít. Ez azt jelenti, hogy amikor <karakterláncban dz> látható, a magyar kultúratudatos összehasonlító egyetlen rendezési elemként kezeli.
| Lánc | Rendezési elemekként | Megjegyzések |
|---|---|---|
"endz" |
"e" + "n" + "d" + "z" |
(szabványos nyelvi összehasonlító használatával) |
"endz" |
"e" + "n" + "dz" |
(magyar kultúratudatos összehasonlító használata) |
Magyar kultúratudatos összehasonlító használata esetén a sztring "endz"nem végződik az aláhúzással megadott részszöveggel "z", mivel <a "dz"> és <a "z"> különböző szemantikai jelentésű rendezési elemeket képeznek.
// Set thread culture to Hungarian
CultureInfo.CurrentCulture = CultureInfo.GetCultureInfo("hu-HU");
Console.WriteLine("endz".EndsWith("z")); // Prints 'False'
// Set thread culture to invariant culture
CultureInfo.CurrentCulture = CultureInfo.InvariantCulture;
Console.WriteLine("endz".EndsWith("z")); // Prints 'True'
' Set thread culture to Hungarian
CultureInfo.CurrentCulture = CultureInfo.GetCultureInfo("hu-HU")
Console.WriteLine("endz".EndsWith("z")) ' Prints 'False'
' Set thread culture to invariant culture
CultureInfo.CurrentCulture = CultureInfo.InvariantCulture
Console.WriteLine("endz".EndsWith("z")) ' Prints 'True'
Megjegyzés
- Viselkedés: A nyelvi és kultúratudatos összehasonlítók időnként viselkedésbeli módosításokat hajthatnak végre. Az ICU és a régebbi Windows NLS-eszköz is frissül, hogy figyelembe vegyék a világnyelvek változását. További információkért lásd a Területi beállítások (kultúra) adatváltozás című blogbejegyzést. Az Ordinal comparer viselkedése soha nem változik, mivel pontosan bitenkénti keresést és összehasonlítást végez. Az OrdinalIgnoreCase összehasonlító viselkedése azonban megváltozhat, mivel a Unicode egyre több karakterkészletet foglal magában, és kijavítja a meglévő burkolatadatok kihagyását.
-
Használat: Az összehasonlítók
StringComparison.InvariantCultureésStringComparison.InvariantCultureIgnoreCasenyelvi összehasonlítók, amelyek nem kultúratudatosak. Ez azt jelzi, hogy ezek az összehasonlítók olyan fogalmakat ismernek, mint például az ékezetes karakter, amelynek több lehetséges mögöttes ábrázolása van, és hogy minden ilyen ábrázolás egyenlő. Azonban a nem kultúratudatos nyelvi összehasonlítók nem tartalmaznak speciális kezelést a <dz> esetében, amely különbözik a <d>-től vagy a <z>-től, ahogy az fent látható. Emellett nem lesznek olyan speciális karakterek, mint a német Eszett (ß).
A .NET az invariáns globalizációs módot is kínálja. Ez az opt-in mód letiltja a nyelvi keresési és összehasonlító rutinokkal foglalkozó kódútvonalakat. Ebben a módban minden művelet ordinali vagy OrdinalIgnoreCase viselkedést használ, függetlenül attól, hogy a hívó mit CultureInfo vagy StringComparison argumentumot biztosít. További információ: Futtatókörnyezeti konfigurációs beállítások a globalizációhoz és a .NET Core Globalization Invariant Módhoz.
Az invariáns kultúrát használó sztringműveletek
Az invariáns kultúrával való összehasonlítás a CompareInfo statikus CultureInfo.InvariantCulture tulajdonság által visszaadott tulajdonságot használja. Ez a viselkedés minden rendszeren ugyanaz; a tartományon kívüli karaktereket lefordítja egyenértékű invariáns karakterekké. Ez az irányelv hasznos lehet egy egységes karakterlánc-viselkedés fenntartásához a különböző kultúrákban, de gyakran váratlan eredményeket ad.
Az invariáns kultúrával való kis- és nagybetűs összehasonlítások a statikus CompareInfo tulajdonság által visszaadott statikus CultureInfo.InvariantCulture tulajdonságot is használják összehasonlítási információkhoz. A lefordított karakterek közötti különbségeket a rendszer figyelmen kívül hagyja.
Az ASCII-sztringeken azonos módon használt StringComparison.InvariantCulture és StringComparison.Ordinal működő összehasonlítások. Azonban olyan nyelvi döntéseket hoz, StringComparison.InvariantCulture amelyek nem feltétlenül megfelelőek a bájthalmazként értelmezendő sztringekhez. Az CultureInfo.InvariantCulture.CompareInfo objektum lehetővé teszi, hogy a Compare metódus bizonyos karakterkészleteket egyenértékűként értelmezzen. Például a következő egyenértékűség érvényes az invariáns kultúrában:
InvariantCulture: a + ̊ = å
A LATIN KIS BETŰ A karakter "a" (\u0061), amikor a "+ " ̊" karakter fölötti KOMBINÁLT GYŰRŰ (\u030a) mellett van, úgy értelmezik, mint az "å" (\u00e5) KARAKTERT TARTALMAZÓ LATIN KIS A BETŰT. Ahogy az alábbi példa is mutatja, ez a viselkedés eltér az ordinális összehasonlítástól.
string separated = "\u0061\u030a";
string combined = "\u00e5";
Console.WriteLine($"Equal sort weight of {separated} and {combined} using InvariantCulture: {string.Compare(separated, combined, StringComparison.InvariantCulture) == 0}");
Console.WriteLine($"Equal sort weight of {separated} and {combined} using Ordinal: {string.Compare(separated, combined, StringComparison.Ordinal) == 0}");
// The example displays the following output:
// Equal sort weight of a° and å using InvariantCulture: True
// Equal sort weight of a° and å using Ordinal: False
Module Program
Sub Main()
Dim separated As String = ChrW(&H61) & ChrW(&H30A)
Dim combined As String = ChrW(&HE5)
Console.WriteLine("Equal sort weight of {0} and {1} using InvariantCulture: {2}",
separated, combined,
String.Compare(separated, combined, StringComparison.InvariantCulture) = 0)
Console.WriteLine("Equal sort weight of {0} and {1} using Ordinal: {2}",
separated, combined,
String.Compare(separated, combined, StringComparison.Ordinal) = 0)
' The example displays the following output:
' Equal sort weight of a° and å using InvariantCulture: True
' Equal sort weight of a° and å using Ordinal: False
End Sub
End Module
Ha fájlneveket, cookie-kat vagy bármi mást értelmez, ahol egy kombináció, például az "å" jelenhet meg, adinális összehasonlítások továbbra is a legátláthatóbb és legilleszthetőbb viselkedést kínálják.
Egyensúlyban az invariáns kultúra kevés olyan tulajdonsággal rendelkezik, amelyek hasznossá teszik az összehasonlítást. Nyelvi szempontból releváns összehasonlítást végez, ami megakadályozza, hogy teljes szimbolikus egyenértékűséget garantáljon, de nem ez a választás a kulturális környezetben való megjelenítéshez. Az összehasonlítás néhány oka StringComparison.InvariantCulture közé tartozik a rendezett adatok megőrzése a kulturálisan azonos megjelenítéshez. Ha például egy nagyméretű adatfájl, amely egy alkalmazáshoz tartozó rendezetlen azonosítók listáját tartalmazza, a listához való hozzáadáshoz invariáns stílusú rendezés szükséges.
Hogyan válasszunk StringComparison tagot
Az alábbi táblázat a szemantikai sztringkörnyezet és az enumeráció StringComparison eleme közötti leképezést ismerteti.
| Adat | Magatartás | Megfelelő System.StringComparison érték |
|---|---|---|
| Kis- és nagybetűket megkülönböztető belső azonosítók. Kis- és nagybetűket megkülönböztető azonosítók olyan szabványokban, mint az XML és a HTTP. A kis- és nagybetűkre érzékeny biztonsági beállítások. |
Nem nyelvi azonosító, ahol a bájtok pontosan egyeznek. | Ordinal |
| Kis- és nagybetűket nem megkülönböztető belső azonosítók. Kis- és nagybetűket nem megkülönböztető azonosítók olyan szabványokban, mint az XML és a HTTP. Fájl elérési útjai. Beállításkulcsok és értékek. Környezeti változók. Erőforrás-azonosítók (például foglalatnevek). Esetérzékenységet nem figyelembe vevő biztonsági beállítások. |
Nem nyelvi azonosító, ahol az eset irreleváns. | OrdinalIgnoreCase |
| Néhány megőrzött, nyelvi szempontból releváns adat. Rögzített rendezési sorrendet igénylő nyelvi adatok megjelenítése. |
Kulturálisan agnosztikus adatok, amelyek még mindig nyelvi szempontból relevánsak. | InvariantCulture -vagy- InvariantCultureIgnoreCase |
| A felhasználó számára megjelenített adatok. A legtöbb felhasználói bemenet. |
Helyi nyelvi szokásokat igénylő adatok. | CurrentCulture -vagy- CurrentCultureIgnoreCase |
Biztonsági következmények
Ha az alkalmazás sztring API-kat használ szűréshez vagy hozzáférés-vezérléshez, használjon sorszám-összehasonlításokat. A jelenlegi kultúrán alapuló nyelvi összehasonlítások platformonként és területi beállításonként eltérő, váratlan eredményeket hozhatnak. Az alábbihoz hasonló kódminták érzékenyek lehetnek a biztonsági résekre:
//
// THIS SAMPLE CODE IS INCORRECT.
// DO NOT USE IT IN PRODUCTION.
//
bool ContainsHtmlSensitiveCharacters(string input)
{
if (input.IndexOf("<") >= 0) { return true; }
if (input.IndexOf("&") >= 0) { return true; }
return false;
}
'
' THIS SAMPLE CODE IS INCORRECT.
' DO NOT USE IT IN PRODUCTION.
'
Function ContainsHtmlSensitiveCharacters(input As String) As Boolean
If input.IndexOf("<") >= 0 Then Return True
If input.IndexOf("&") >= 0 Then Return True
Return False
End Function
Mivel a string.IndexOf(string) módszer alapértelmezés szerint nyelvi keresést használ, lehetséges, hogy egy sztring literális '<' vagy '&' karaktert tartalmaz, és string.IndexOf(string) visszaadja -1 azt, amely azt jelzi, hogy a keresési részsztring nem található. A CA1307 és a CA1309 kódelemzési szabályok megjelölik az ilyen hívási helyeket, és figyelmeztetik a fejlesztőt, hogy lehetséges probléma merül fel.
Gyakori sztring-összehasonlító módszerek a .NET-ben
Az alábbi szakaszok a sztringek összehasonlítására leggyakrabban használt módszereket ismertetik.
String.Compare
Alapértelmezett értelmezés: StringComparison.CurrentCulture.
Mivel a művelet a sztringértelmezés középpontjában áll, a metódushívások minden példányát meg kell vizsgálni annak megállapításához, hogy a sztringeket a jelenlegi kultúra szerint kell-e értelmezni, vagy (szimbolikusan) el kell-e bontani a kultúrától. Általában ez az utóbbi, és ehelyett StringComparison.Ordinal összehasonlítást kell használni.
A System.Globalization.CompareInfo osztály, amelyet a CultureInfo.CompareInfo tulajdonság ad vissza, tartalmaz egy Compare metódust is, amely a CompareOptions jelző felsorolás révén számos egyeztetési lehetőséget kínál (sorrendi, szóköz kihagyása, kana típus figyelmen kívül hagyása stb.).
String.CompareTo
Alapértelmezett értelmezés: StringComparison.CurrentCulture.
Ez a metódus jelenleg nem kínál túlterhelést, amely típust StringComparison határoz meg. Ezt a metódust általában az ajánlott String.Compare(String, String, StringComparison) űrlapra lehet konvertálni.
A metódust IComparable megvalósító típusok és IComparable<T> interfészek implementálják ezt a módszert. Mivel nem kínál paramétert StringComparison , a implementálási típusok gyakran lehetővé teszik, hogy a felhasználó konstruktorában adjon meg egy paramétert StringComparer . Az alábbi példa egy olyan osztályt FileName határoz meg, amelynek osztálykonstruktora tartalmaz egy paramétert StringComparer . Ezt az StringComparer objektumot ezután használja a FileName.CompareTo metódus.
class FileName : IComparable
{
private readonly StringComparer _comparer;
public string Name { get; }
public FileName(string name, StringComparer? comparer)
{
if (string.IsNullOrEmpty(name)) throw new ArgumentNullException(nameof(name));
Name = name;
if (comparer != null)
_comparer = comparer;
else
_comparer = StringComparer.OrdinalIgnoreCase;
}
public int CompareTo(object? obj)
{
if (obj == null) return 1;
if (obj is not FileName)
return _comparer.Compare(Name, obj.ToString());
else
return _comparer.Compare(Name, ((FileName)obj).Name);
}
}
Class FileName
Implements IComparable
Private ReadOnly _comparer As StringComparer
Public ReadOnly Property Name As String
Public Sub New(name As String, comparer As StringComparer)
If (String.IsNullOrEmpty(name)) Then Throw New ArgumentNullException(NameOf(name))
Me.Name = name
If comparer IsNot Nothing Then
_comparer = comparer
Else
_comparer = StringComparer.OrdinalIgnoreCase
End If
End Sub
Public Function CompareTo(obj As Object) As Integer Implements IComparable.CompareTo
If obj Is Nothing Then Return 1
If TypeOf obj IsNot FileName Then
Return _comparer.Compare(Name, obj.ToString())
Else
Return _comparer.Compare(Name, DirectCast(obj, FileName).Name)
End If
End Function
End Class
String.Equals
Alapértelmezett értelmezés: StringComparison.Ordinal.
Az String osztály lehetővé teszi az egyenlőség tesztelését a statikus vagy példánymetódus Equals túlterhelésének meghívásával, vagy a statikus egyenlőségi operátor használatával. A túlterhelések és az operátorok alapértelmezés szerint az ordinális összehasonlítást használják. Továbbra is azt javasoljuk, hogy használjon olyan túlterhelést, amely explicit módon meghatározza a StringComparison típust, még akkor is, ha egy ordinális összehasonlítást szeretne végrehajtani; ez megkönnyíti a kódban egy bizonyos sztringértelmezés szerinti keresést.
String.ToUpper és String.ToLower
Alapértelmezett értelmezés: StringComparison.CurrentCulture.
Legyen óvatos a String.ToUpper() és String.ToLower() metódusok használatakor, mert a sztring kis- vagy nagybetűssé alakítása gyakran egy kis normalizációként szolgál a sztringek összehasonlításához, függetlenül attól, hogy kis- vagy nagybetűkről van szó. Ha igen, fontolja meg a kis- és nagybetűket figyelmen kívül hagyó összehasonlítás használatát.
A String.ToUpperInvariant módszerek és String.ToLowerInvariant a metódusok is elérhetők. ToUpperInvariant a kis- és nagybetűk normalizálásának általános módja. A StringComparison.OrdinalIgnoreCase használatával végzett összehasonlítások viselkedési szempontból két hívás összetételét jelentik: a ToUpperInvariant alkalmazása mindkét sztringargumentumra, majd az összehasonlítás elvégzése a StringComparison.Ordinal használatával.
A túlterhelési lehetőségek egy adott kultúrában nagy- és kisbetűssé alakításra is elérhetők, ha átadjuk a módszernek azt a CultureInfo objektumot, amely az adott kultúrát képviseli.
Char.ToUpper és Char.ToLower
Alapértelmezett értelmezés: StringComparison.CurrentCulture.
A Char.ToUpper(Char) metódusok és Char.ToLower(Char) a String.ToUpper()String.ToLower() módszerek az előző szakaszban leírthoz hasonlóan működnek.
String.StartsWith és String.EndsWith
Alapértelmezett értelmezés: StringComparison.CurrentCulture (ha az első paraméter egy string), vagy StringComparison.Ordinal (ha az első paraméter a char).
Inkonzisztencia tapasztalható abban, hogy ezeknek a metódusoknak az alapértelmezett túlterhelései hogyan hajtanak végre összehasonlításokat. Azok a túlterhelések, amelyek elfogadnak egy char paramétert, sorrend szerinti összehasonlítást végeznek, de azok, amelyek elfogadnak egy string paramétert, kultúrafüggő összehasonlítást végezhetnek, és figyelmen kívül hagyhatják a nem nyomtatható karaktereket.
String.IndexOf és String.LastIndexOf
Alapértelmezett értelmezés: StringComparison.CurrentCulture.
Hiányzik a konzisztencia abban, hogy a metódusok alapértelmezett túlterhelései hogyan hajtanak végre összehasonlításokat. A String.IndexOf paramétert tartalmazó összes String.LastIndexOf és Char metódus sorszám szerinti összehasonlítást hajt végre, de az alapértelmezett String.IndexOf és String.LastIndexOf metódusok, amelyek tartalmazzák a String paramétert, kultúraérzékeny összehasonlítást végeznek.
Ha meghívja a String.IndexOf(String) vagy String.LastIndexOf(String) metódusokat, és egy sztringet ad meg az aktuális példányban való kereséshez, javasoljuk, hogy hívjon meg egy StringComparison túlterhelést, amely explicit módon meghatározza a típust. Az argumentumot Char tartalmazó túlterhelések nem teszik lehetővé a típus megadását StringComparison .
String.Contains
Alapértelmezett értelmezés: StringComparison.Ordinal.
Ellentétben String.IndexOfString.Contains metódussal, amely alapértelmezés szerint egy sorszám szerinti összehasonlítást alkalmaz mind a char, mind a string túlterheléseihez. Azonban, amikor a szándék fontos, továbbra is át kell adnia egy explicit StringComparison argumentumot, hogy a viselkedés egyértelmű legyen a hívási helyen.
MemoryExtensions.AsSpan.IndexOfAny és a SearchValues<T> típus
A .NET 8 bevezette a SearchValues<T> típust, amely optimalizált megoldást kínál adott karakterkészletek vagy bájtok keresésére a spanokon belül.
Ha egy sztringet ismétlődően összehasonlít egy rögzített ismert értékkészlettel, érdemes lehet láncolt összehasonlítások vagy LINQ-alapú megközelítések helyett a SearchValues<T>.Contains(T) metódust használni.
SearchValues<T> a belső keresési struktúrákat előre tudja konkompatitálni, és optimalizálni az összehasonlítási logikát a megadott értékek alapján. A teljesítménybeli előnyök megtekintéséhez hozza létre és gyorsítótárazza a SearchValues<string> példányt egyszer, majd használja újra összehasonlításhoz:
using System.Buffers;
namespace ExampleCode;
internal partial class DemoCode
{
private static readonly SearchValues<string> Commands =
SearchValues.Create(
["start", "run", "go", "begin", "commence"],
StringComparison.OrdinalIgnoreCase);
void ProcessCommand(string command)
{
if (Commands.Contains(command))
{
// ...
}
}
}
Imports System.Buffers
Namespace ExampleCode
Partial Friend Class DemoCode
Private Shared ReadOnly Commands As SearchValues(Of String) =
SearchValues.Create(
{"start", "run", "go", "begin", "commence"},
StringComparison.OrdinalIgnoreCase)
Sub ProcessCommand(command As String)
If Commands.Contains(command) Then
' ...
End If
End Sub
End Class
End Namespace
A .NET 9-ben kibővítették a SearchValues, hogy támogassa az alsztringek keresését egy nagyobb sztringben. Például lásd SearchValues a bővítést.
Sztring-összehasonlítást közvetetten végző metódusok
Egyes nem sztringalapú metódusok, amelyek központi műveletként sztring-összehasonlítással rendelkeznek, a típust használják StringComparer . Az StringComparer osztály hat statikus tulajdonságot tartalmaz, amelyek olyan példányokat ad vissza StringComparer , amelyek StringComparer.Compare metódusai a következő sztring-összehasonlításokat hajtják végre:
- Kultúraérzékeny karakterláncok összehasonlítása a jelenlegi kultúrának megfelelően. Ezt StringComparer az objektumot a StringComparer.CurrentCulture tulajdonság adja vissza.
- Kis- és nagybetűk érzéketlen összehasonlítása a jelenlegi kultúrával. Ezt StringComparer az objektumot a StringComparer.CurrentCultureIgnoreCase tulajdonság adja vissza.
- Kultúra-érzéketlen összehasonlítások az invariáns kultúra összehasonlítási szabályainak használatával. Ezt StringComparer az objektumot a StringComparer.InvariantCulture tulajdonság adja vissza.
- Kis- és nagybetűk érzéketlen és kulturális érzéketlen összehasonlításai az invariáns kultúra összehasonlító szabályainak használatával. Ezt StringComparer az objektumot a StringComparer.InvariantCultureIgnoreCase tulajdonság adja vissza.
- Sorrendi összehasonlítás. Ezt StringComparer az objektumot a StringComparer.Ordinal tulajdonság adja vissza.
- Kis- és nagybetűk érzéketlen összehasonlítása. Ezt StringComparer az objektumot a StringComparer.OrdinalIgnoreCase tulajdonság adja vissza.
Array.Sort és Array.BinarySearch
Alapértelmezett értelmezés: StringComparison.CurrentCulture.
Ha egy gyűjteményben tárol adatokat, vagy egy fájlból vagy adatbázisból származó tárolt adatokat olvas be gyűjteménybe, az aktuális kultúra váltása érvénytelenítheti a gyűjteményben lévő állandókat. A Array.BinarySearch metódus feltételezi, hogy a keresendő tömb elemei már rendezve vannak. A tömb bármely sztringelemének rendezéséhez a Array.Sort metódus meghívja a metódust String.Compare az egyes elemek rendezésére. A kultúraérzékeny összehasonlító használata veszélyes lehet, ha a kultúra megváltozik az array rendezése és a tartalom keresése között. Az alábbi kódban például a tárolás és a lekérés a tulajdonság által Thread.CurrentThread.CurrentCulture implicit módon biztosított összehasonlítón működik. Ha a kultúra megváltozhat a StoreNames és DoesNameExist közötti hívások során, és különösen, ha a tömb tartalma valahol a két metódushívás között megőrződik, a bináris keresés sikertelen lehet.
// Incorrect
string[] _storedNames;
public void StoreNames(string[] names)
{
_storedNames = new string[names.Length];
// Copy the array contents into a new array
Array.Copy(names, _storedNames, names.Length);
Array.Sort(_storedNames); // Line A
}
public bool DoesNameExist(string name) =>
Array.BinarySearch(_storedNames, name) >= 0; // Line B
' Incorrect
Dim _storedNames As String()
Sub StoreNames(names As String())
ReDim _storedNames(names.Length - 1)
' Copy the array contents into a new array
Array.Copy(names, _storedNames, names.Length)
Array.Sort(_storedNames) ' Line A
End Sub
Function DoesNameExist(name As String) As Boolean
Return Array.BinarySearch(_storedNames, name) >= 0 ' Line B
End Function
Az alábbi példában egy javasolt változat jelenik meg, amely ugyanazt a kultúrafüggetlen összehasonlító módszert használja a tömb rendezéséhez és kereséséhez. A változáskód a címkézett Line A sorokban és Line B a két példában is megjelenik.
// Correct
string[] _storedNames;
public void StoreNames(string[] names)
{
_storedNames = new string[names.Length];
// Copy the array contents into a new array
Array.Copy(names, _storedNames, names.Length);
Array.Sort(_storedNames, StringComparer.Ordinal); // Line A
}
public bool DoesNameExist(string name) =>
Array.BinarySearch(_storedNames, name, StringComparer.Ordinal) >= 0; // Line B
' Correct
Dim _storedNames As String()
Sub StoreNames(names As String())
ReDim _storedNames(names.Length - 1)
' Copy the array contents into a new array
Array.Copy(names, _storedNames, names.Length)
Array.Sort(_storedNames, StringComparer.Ordinal) ' Line A
End Sub
Function DoesNameExist(name As String) As Boolean
Return Array.BinarySearch(_storedNames, name, StringComparer.Ordinal) >= 0 ' Line B
End Function
Ha ezek az adatok megmaradnak és átkerülnek a kultúrák között, és a rendszer rendezéssel jeleníti meg ezeket az adatokat a felhasználónak, érdemes lehet használni StringComparison.InvariantCulture, amely nyelvileg működik a jobb felhasználói teljesítmény érdekében, de a kultúra változásai nem érintik. Az alábbi példa módosítja az előző két példát, hogy az invariáns kultúrát használja a tömb rendezéséhez és kereséséhez.
// Correct
string[] _storedNames;
public void StoreNames(string[] names)
{
_storedNames = new string[names.Length];
// Copy the array contents into a new array
Array.Copy(names, _storedNames, names.Length);
Array.Sort(_storedNames, StringComparer.InvariantCulture); // Line A
}
public bool DoesNameExist(string name) =>
Array.BinarySearch(_storedNames, name, StringComparer.InvariantCulture) >= 0; // Line B
' Correct
Dim _storedNames As String()
Sub StoreNames(names As String())
ReDim _storedNames(names.Length - 1)
' Copy the array contents into a new array
Array.Copy(names, _storedNames, names.Length)
Array.Sort(_storedNames, StringComparer.InvariantCulture) ' Line A
End Sub
Function DoesNameExist(name As String) As Boolean
Return Array.BinarySearch(_storedNames, name, StringComparer.InvariantCulture) >= 0 ' Line B
End Function
Gyűjtemények például: Hashtable konstruktor
A sztringek kivonatolása egy másik példája egy olyan műveletnek, amelyet a sztringek összehasonlítása befolyásol.
Az alábbi példa egy Hashtable objektumot példányosít azáltal, hogy átadja neki a StringComparer tulajdonság által visszaadott StringComparer.OrdinalIgnoreCase objektumot. Mivel egy StringComparer-ből származó osztályStringComparer az IEqualityComparer interfészt implementálja, ezért a GetHashCode metódusa a hash-táblában lévő karakterláncok kivonatkódjának kiszámítására szolgál.
using System.IO;
using System.Collections;
const int InitialCapacity = 100;
Hashtable creationTimeByFile = new(InitialCapacity, StringComparer.OrdinalIgnoreCase);
string directoryToProcess = Directory.GetCurrentDirectory();
// Fill the hash table
PopulateFileTable(directoryToProcess);
// Get some of the files and try to find them with upper cased names
foreach (var file in Directory.GetFiles(directoryToProcess))
PrintCreationTime(file.ToUpper());
void PopulateFileTable(string directory)
{
foreach (string file in Directory.GetFiles(directory))
creationTimeByFile.Add(file, File.GetCreationTime(file));
}
void PrintCreationTime(string targetFile)
{
object? dt = creationTimeByFile[targetFile];
if (dt is DateTime value)
Console.WriteLine($"File {targetFile} was created at time {value}.");
else
Console.WriteLine($"File {targetFile} does not exist.");
}
Imports System.IO
Module Program
Const InitialCapacity As Integer = 100
Private ReadOnly s_creationTimeByFile As New Hashtable(InitialCapacity, StringComparer.OrdinalIgnoreCase)
Private ReadOnly s_directoryToProcess As String = Directory.GetCurrentDirectory()
Sub Main()
' Fill the hash table
PopulateFileTable(s_directoryToProcess)
' Get some of the files and try to find them with upper cased names
For Each File As String In Directory.GetFiles(s_directoryToProcess)
PrintCreationTime(File.ToUpper())
Next
End Sub
Sub PopulateFileTable(directoryPath As String)
For Each file As String In Directory.GetFiles(directoryPath)
s_creationTimeByFile.Add(file, IO.File.GetCreationTime(file))
Next
End Sub
Sub PrintCreationTime(targetFile As String)
Dim dt As Object = s_creationTimeByFile(targetFile)
If TypeOf dt Is Date Then
Console.WriteLine($"File {targetFile} was created at time {DirectCast(dt, Date)}.")
Else
Console.WriteLine($"File {targetFile} does not exist.")
End If
End Sub
End Module
Példák gyűjteményekre: SortedSet<T> és List<T>.Sort
Ugyanez a területi érzékenységi probléma vonatkozik a karakterláncok rendezett gyűjteményének példányosítására vagy egy meglevő karakterlánc alapú gyűjtemény rendezésére. Mindig adjon meg explicit összehasonlítót:
// Words to sort
string[] values = [ "able", "ångström", "apple", "Æble",
"Windows", "Visual Studio" ];
//
// Potentially incorrect code - behavior might vary based on locale.
//
SortedSet<string> mySet = [.. values]; // No comparer specified
List<string> list = [.. values];
list.Sort(); // No comparer specified
//
// Corrected code - uses ordinal sorting; doesn't vary by locale.
//
SortedSet<string> mySet2 = new(values, StringComparer.Ordinal);
List<string> list2 = [.. values];
list2.Sort(StringComparer.Ordinal);
' Words to sort
Dim values As String() = {"able", "ångström", "apple", "Æble",
"Windows", "Visual Studio"}
'
' Potentially incorrect code - behavior might vary based on locale.
'
Dim mySet As New SortedSet(Of String)(values) ' No comparer specified
Dim list As New List(Of String)(values)
list.Sort() ' No comparer specified
'
' Corrected code - uses ordinal sorting; doesn't vary by locale.
'
Dim mySet2 As New SortedSet(Of String)(values, StringComparer.Ordinal)
Dim list2 As New List(Of String)(values)
list2.Sort(StringComparer.Ordinal)
Különbségek a .NET és a .NET-keretrendszer között
A .NET és a .NET-keretrendszer eltérően kezeli a globalizációt. A windowsos .NET-keretrendszer az operációs rendszer nemzeti nyelvi támogatási (NLS) eszközét használja a nyelvi sztringek összehasonlításához. A .NET az International Components for Unicode (ICU) kódtárat használja a nyelvi sztringek összehasonlításához az összes támogatott platformon.
Mivel az ICU és az NLS eltérő logikát implementál a nyelvi összehasonlítókban, a kultúraérzékeny összehasonlítást használó sztringmetóterek eredményei eltérhetnek a .NET és a .NET-keretrendszer között. Ez minden olyan módszer esetében számít, amely alapértelmezés szerint nyelvi összehasonlítót használ, beleértve a következőket:
- String.Compare
-
String.EndsWith (ha az első paraméter a
string) -
String.IndexOf (ha az első paraméter a
string) -
String.StartsWith (ha az első paraméter a
string) - String.ToLower
- String.ToLowerInvariant
- String.ToUpper
- String.ToUpperInvariant
- System.Globalization.TextInfo (a legtöbb tag)
- System.Globalization.CompareInfo (a legtöbb tag)
- Array.Sort (sztringtömbök rendezésekor)
- List<T>.Sort() (ha a listaelemek sztringek)
- System.Collections.Generic.SortedDictionary<TKey,TValue> (ha a kulcsok karakterláncok)
- System.Collections.Generic.SortedList<TKey,TValue> (ha a kulcsok karakterláncok)
- System.Collections.Generic.SortedSet<T> (ha a készlet sztringeket tartalmaz)
Megjegyzés
Ez nem az érintett API-k teljes listája.
Az egyik jelentős különbség a beágyazott null karakter és más vezérlőkarakterek kezelése. Ha az NLS alatt használ nyelvi összehasonlítót, bizonyos vezérlőkarakterek, például a null karakter (\0) bizonyos összehasonlító kontextusokban figyelmen kívül hagyhatók. Az ICU keretrendszerben ezek a karakterek a sztring tényleges karaktereiként vannak kezelve. Ez eltérő string.IndexOf(string) eredményeket eredményezhet, ha a keresési sztring null karaktert tartalmaz.
A következő kód például az aktuális futtatókörnyezettől függően eltérő választ adhat:
const string greeting = "Hel\0lo";
Console.WriteLine($"{greeting.IndexOf("\0")}");
// The snippet prints:
//
// '3' when running on .NET Framework and .NET Core 2.x - 3.x (Windows)
// '0' when running on .NET 5 or later (Windows)
// '0' when running on .NET Core 2.x - 3.x or .NET 5 (non-Windows)
// '3' when running on .NET Core 2.x or .NET 5+ (in invariant mode)
Const greeting As String = "Hel" & vbNullChar & "lo"
Console.WriteLine($"{greeting.IndexOf(CStr(vbNullChar))}")
' The snippet prints:
'
' '3' when running on .NET Framework and .NET Core 2.x - 3.x (Windows)
' '0' when running on .NET 5 or later (Windows)
' '0' when running on .NET Core 2.x - 3.x or .NET 5 (non-Windows)
' '3' when running on .NET Core 2.x or .NET 5+ (in invariant mode)
A platformfüggetlen és implementációk közötti meglepetések elkerülésének legjobb módja, ha mindig explicit StringComparison argumentumot ad át a sztring-összehasonlító módszereknek, és a nem nyelvi összehasonlításokhoz a StringComparison.Ordinal vagy StringComparison.OrdinalIgnoreCase-t használja.
Ha egy alkalmazást a .NET-keretrendszerből a .NET-be migrál, és a Windows régi NLS-viselkedéseire támaszkodik, konfigurálhatja az alkalmazást az NLS használatára. További információ: .NET-globalizáció és ICU.