Så här jämför du strängar i C#

Du jämför strängar för att besvara en av två frågor: "Är dessa två strängar lika?" eller "I vilken ordning ska dessa strängar placeras när de sorteras?"

Dessa två frågor kompliceras av faktorer som påverkar strängjämförelser:

  • Du kan välja en ordningstals- eller språkjämförelse.
  • Du kan välja om ärendet är viktigt.
  • Du kan välja kulturspecifika jämförelser.
  • Språkjämförelser är kultur- och plattformsberoende.

Uppräkningsfälten System.StringComparison representerar följande alternativ:

  • CurrentCulture: Jämför strängar med kulturkänsliga sorteringsregler och den aktuella kulturen.
  • CurrentCultureIgnoreCase: Jämför strängar med kulturkänsliga sorteringsregler, den aktuella kulturen och ignorera fallet med de strängar som jämförs.
  • InvariantCulture: Jämför strängar med kulturkänsliga sorteringsregler och den invarianta kulturen.
  • InvariantCultureIgnoreCase: Jämför strängar med kulturkänsliga sorteringsregler, den invarianta kulturen och ignorerar fallet med de strängar som jämförs.
  • Ordningstal: Jämför strängar med ordningstal (binära) sorteringsregler.
  • OrdinalIgnoreCase: Jämför strängar med ordningsföljdsregler (binär) och ignorera fallet med de strängar som jämförs.

Kommentar

C#-exemplen i den här artikeln körs i Try.NET infogad kodlöpare och lekplats. Välj knappen Kör för att köra ett exempel i ett interaktivt fönster. När du har kört koden kan du ändra den och köra den ändrade koden genom att välja Kör igen. Den ändrade koden körs antingen i det interaktiva fönstret eller, om kompilering misslyckas, visar det interaktiva fönstret alla felmeddelanden för C#-kompilatorn.

När du jämför strängar definierar du en ordning mellan dem. Jämförelser används för att sortera en sekvens med strängar. När sekvensen är i en känd ordning är det lättare att söka, både efter programvara och för människor. Andra jämförelser kan kontrollera om strängarna är desamma. Dessa likhetskontroller liknar likhet, men vissa skillnader, till exempel skiftlägesskillnader, kan ignoreras.

Standardmåttjämförelser

Som standard är de vanligaste åtgärderna:

string root = @"C:\users";
string root2 = @"C:\Users";

bool result = root.Equals(root2);
Console.WriteLine($"Ordinal comparison: <{root}> and <{root2}> are {(result ? "equal." : "not equal.")}");

result = root.Equals(root2, StringComparison.Ordinal);
Console.WriteLine($"Ordinal comparison: <{root}> and <{root2}> are {(result ? "equal." : "not equal.")}");

Console.WriteLine($"Using == says that <{root}> and <{root2}> are {(root == root2 ? "equal" : "not equal")}");

Standardmåttjämförelsen tar inte hänsyn till språkregler vid jämförelse av strängar. Den jämför binärvärdet för varje Char objekt i två strängar. Därför är standardmåttjämförelsen också skiftlägeskänslig.

Testet för likhet med String.Equals och == operatorerna och != skiljer sig från strängjämförelse med metoderna String.CompareTo och Compare(String, String) . De utför alla en skiftlägeskänslig jämförelse. Men medan testerna för likhet utför en ordningstalsjämförelse, CompareTo utför metoderna och Compare en kulturmedveten språkjämförelse med hjälp av den aktuella kulturen. Gör avsikten med koden tydlig genom att anropa en överlagring som uttryckligen anger vilken typ av jämförelse som ska utföras.

Fallokänsliga ordningstalsjämförelser

Med String.Equals(String, StringComparison) metoden kan du ange ett StringComparison värde StringComparison.OrdinalIgnoreCase för för en skiftlägesokänslig ordningstalsjämförelse. Det finns också en statisk String.Compare(String, String, StringComparison) metod som utför en skiftlägeskänslig ordningstalsjämförelse om du anger värdet StringComparison.OrdinalIgnoreCase för argumentet StringComparison . Dessa jämförelser visas i följande kod:

string root = @"C:\users";
string root2 = @"C:\Users";

bool result = root.Equals(root2, StringComparison.OrdinalIgnoreCase);
bool areEqual = String.Equals(root, root2, StringComparison.OrdinalIgnoreCase);
int comparison = String.Compare(root, root2, comparisonType: StringComparison.OrdinalIgnoreCase);

Console.WriteLine($"Ordinal ignore case: <{root}> and <{root2}> are {(result ? "equal." : "not equal.")}");
Console.WriteLine($"Ordinal static ignore case: <{root}> and <{root2}> are {(areEqual ? "equal." : "not equal.")}");
if (comparison < 0)
    Console.WriteLine($"<{root}> is less than <{root2}>");
else if (comparison > 0)
    Console.WriteLine($"<{root}> is greater than <{root2}>");
else
    Console.WriteLine($"<{root}> and <{root2}> are equivalent in order");

Dessa metoder använder höljekonventionerna i den invarianta kulturen när du utför en fallokänslig ordningstalsjämförelse.

Språkliga jämförelser

Många strängjämförelsemetoder (till exempel String.StartsWith) använder språkregler för den aktuella kulturen som standard för att beställa sina indata. Den här språkliga jämförelsen kallas ibland "ordsorteringsordning". När du gör en språkjämförelse kan vissa icke-numeriska Unicode-tecken ha särskilda vikter tilldelade. Bindestrecket "-" kan till exempel ha en liten vikt tilldelad till det så att "co-op" och "coop" visas bredvid varandra i sorteringsordning. Vissa icke-utskrivbara kontrolltecken kan ignoreras. Dessutom kan vissa Unicode-tecken motsvara en sekvens av Char instanser. I följande exempel används frasen "De dansar på gatan." på tyska med "ss" (U+0073 U+0073) i en sträng och "ß" (U+00DF) i en annan. Språkligt (i Windows) är "ss" lika med den tyska Esszet: "ß" karaktär i både "en-US" och "de-DE" kulturer.

string first = "Sie tanzen auf der Straße.";
string second = "Sie tanzen auf der Strasse.";

Console.WriteLine($"First sentence is <{first}>");
Console.WriteLine($"Second sentence is <{second}>");

bool equal = String.Equals(first, second, StringComparison.InvariantCulture);
Console.WriteLine($"The two strings {(equal == true ? "are" : "are not")} equal.");
showComparison(first, second);

string word = "coop";
string words = "co-op";
string other = "cop";

showComparison(word, words);
showComparison(word, other);
showComparison(words, other);
void showComparison(string one, string two)
{
    int compareLinguistic = String.Compare(one, two, StringComparison.InvariantCulture);
    int compareOrdinal = String.Compare(one, two, StringComparison.Ordinal);
    if (compareLinguistic < 0)
        Console.WriteLine($"<{one}> is less than <{two}> using invariant culture");
    else if (compareLinguistic > 0)
        Console.WriteLine($"<{one}> is greater than <{two}> using invariant culture");
    else
        Console.WriteLine($"<{one}> and <{two}> are equivalent in order using invariant culture");
    if (compareOrdinal < 0)
        Console.WriteLine($"<{one}> is less than <{two}> using ordinal comparison");
    else if (compareOrdinal > 0)
        Console.WriteLine($"<{one}> is greater than <{two}> using ordinal comparison");
    else
        Console.WriteLine($"<{one}> and <{two}> are equivalent in order using ordinal comparison");
}

I Windows, före .NET 5, ändras sorteringsordningen "cop", "coop" och "co-op" när du byter från en språklig jämförelse till en ordningstalsjämförelse. De två tyska meningarna jämför också olika med hjälp av de olika jämförelsetyperna. Före .NET 5 använde .NET-globaliserings-API :erna NLS-bibliotek (National Language Support). I .NET 5 och senare versioner använder .NET-globaliserings-API:erna internationella komponenter för Unicode-bibliotek (ICU), vilket förenar . NET:s globaliseringsbeteende i alla operativsystem som stöds.

Jämförelser med specifika kulturer

I följande exempel lagras CultureInfo objekt för kulturerna en-US och de-DE. Jämförelserna utförs med hjälp av ett CultureInfo objekt för att säkerställa en kulturspecifik jämförelse. Den kultur som används påverkar språkliga jämförelser. I följande exempel visas resultatet av att jämföra de två tyska meningarna med kulturen "en-US" och "de-DE"-kulturen:

string first = "Sie tanzen auf der Straße.";
string second = "Sie tanzen auf der Strasse.";

Console.WriteLine($"First sentence is <{first}>");
Console.WriteLine($"Second sentence is <{second}>");

var en = new System.Globalization.CultureInfo("en-US");

// For culture-sensitive comparisons, use the String.Compare
// overload that takes a StringComparison value.
int i = String.Compare(first, second, en, System.Globalization.CompareOptions.None);
Console.WriteLine($"Comparing in {en.Name} returns {i}.");

var de = new System.Globalization.CultureInfo("de-DE");
i = String.Compare(first, second, de, System.Globalization.CompareOptions.None);
Console.WriteLine($"Comparing in {de.Name} returns {i}.");

bool b = String.Equals(first, second, StringComparison.CurrentCulture);
Console.WriteLine($"The two strings {(b ? "are" : "are not")} equal.");

string word = "coop";
string words = "co-op";
string other = "cop";

showComparison(word, words, en);
showComparison(word, other, en);
showComparison(words, other, en);
void showComparison(string one, string two, System.Globalization.CultureInfo culture)
{
    int compareLinguistic = String.Compare(one, two, en, System.Globalization.CompareOptions.None);
    int compareOrdinal = String.Compare(one, two, StringComparison.Ordinal);
    if (compareLinguistic < 0)
        Console.WriteLine($"<{one}> is less than <{two}> using en-US culture");
    else if (compareLinguistic > 0)
        Console.WriteLine($"<{one}> is greater than <{two}> using en-US culture");
    else
        Console.WriteLine($"<{one}> and <{two}> are equivalent in order using en-US culture");
    if (compareOrdinal < 0)
        Console.WriteLine($"<{one}> is less than <{two}> using ordinal comparison");
    else if (compareOrdinal > 0)
        Console.WriteLine($"<{one}> is greater than <{two}> using ordinal comparison");
    else
        Console.WriteLine($"<{one}> and <{two}> are equivalent in order using ordinal comparison");
}

Kulturkänsliga jämförelser används vanligtvis för att jämföra och sortera indata för strängar av användare med andra strängar som indata från användare. Tecknen och sorteringskonventionerna för dessa strängar kan variera beroende på användarens dators nationella inställningar. Även strängar som innehåller identiska tecken kan sorteras olika beroende på kulturen i den aktuella tråden.

Språklig sortering och sökning i strängar i matriser

I följande exempel visas hur du sorterar och söker efter strängar i en matris med hjälp av en språkjämförelse som är beroende av den aktuella kulturen. Du använder de statiska Array metoder som tar en System.StringComparer parameter.

I följande exempel visas hur du sorterar en matris med strängar med den aktuella kulturen:

string[] lines = new string[]
{
    @"c:\public\textfile.txt",
    @"c:\public\textFile.TXT",
    @"c:\public\Text.txt",
    @"c:\public\testfile2.txt"
};

Console.WriteLine("Non-sorted order:");
foreach (string s in lines)
{
    Console.WriteLine($"   {s}");
}

Console.WriteLine("\n\rSorted order:");

// Specify Ordinal to demonstrate the different behavior.
Array.Sort(lines, StringComparer.CurrentCulture);

foreach (string s in lines)
{
    Console.WriteLine($"   {s}");
}

När matrisen har sorterats kan du söka efter poster med hjälp av en binär sökning. En binär sökning startar i mitten av samlingen för att avgöra vilken halva av samlingen som skulle innehålla den sökta strängen. Varje efterföljande jämförelse delar upp den återstående delen av samlingen i hälften. Matrisen sorteras med hjälp av StringComparer.CurrentCulture. Den lokala funktionen ShowWhere visar information om var strängen hittades. Om strängen inte hittades anger det returnerade värdet var den skulle vara om den hittades.

string[] lines = new string[]
{
    @"c:\public\textfile.txt",
    @"c:\public\textFile.TXT",
    @"c:\public\Text.txt",
    @"c:\public\testfile2.txt"
};
Array.Sort(lines, StringComparer.CurrentCulture);

string searchString = @"c:\public\TEXTFILE.TXT";
Console.WriteLine($"Binary search for <{searchString}>");
int result = Array.BinarySearch(lines, searchString, StringComparer.CurrentCulture);
ShowWhere<string>(lines, result);

Console.WriteLine($"{(result > 0 ? "Found" : "Did not find")} {searchString}");

void ShowWhere<T>(T[] array, int index)
{
    if (index < 0)
    {
        index = ~index;

        Console.Write("Not found. Sorts between: ");

        if (index == 0)
            Console.Write("beginning of sequence and ");
        else
            Console.Write($"{array[index - 1]} and ");

        if (index == array.Length)
            Console.WriteLine("end of sequence.");
        else
            Console.WriteLine($"{array[index]}.");
    }
    else
    {
        Console.WriteLine($"Found at index {index}.");
    }
}

Ordningstalssortering och sökning i samlingar

Följande kod använder samlingsklassen System.Collections.Generic.List<T> för att lagra strängar. Strängarna sorteras med hjälp av List<T>.Sort metoden . Den här metoden behöver ett ombud som jämför och beställer två strängar. Metoden String.CompareTo tillhandahåller den jämförelsefunktionen. Kör exemplet och observera ordningen. Den här sorteringsåtgärden använder en ordningstals skiftlägeskänslig sortering. Du använder statiska String.Compare metoder för att ange olika jämförelseregler.

List<string> lines = new List<string>
{
    @"c:\public\textfile.txt",
    @"c:\public\textFile.TXT",
    @"c:\public\Text.txt",
    @"c:\public\testfile2.txt"
};

Console.WriteLine("Non-sorted order:");
foreach (string s in lines)
{
    Console.WriteLine($"   {s}");
}

Console.WriteLine("\n\rSorted order:");

lines.Sort((left, right) => left.CompareTo(right));
foreach (string s in lines)
{
    Console.WriteLine($"   {s}");
}

När du har sorterat kan listan med strängar sökas igenom med hjälp av en binär sökning. Följande exempel visar hur du söker i den sorterade listan med hjälp av samma jämförelsefunktion. Den lokala funktionen ShowWhere visar var den begärda texten är eller skulle vara:

List<string> lines = new List<string>
{
    @"c:\public\textfile.txt",
    @"c:\public\textFile.TXT",
    @"c:\public\Text.txt",
    @"c:\public\testfile2.txt"
};
lines.Sort((left, right) => left.CompareTo(right));

string searchString = @"c:\public\TEXTFILE.TXT";
Console.WriteLine($"Binary search for <{searchString}>");
int result = lines.BinarySearch(searchString);
ShowWhere<string>(lines, result);

Console.WriteLine($"{(result > 0 ? "Found" : "Did not find")} {searchString}");

void ShowWhere<T>(IList<T> collection, int index)
{
    if (index < 0)
    {
        index = ~index;

        Console.Write("Not found. Sorts between: ");

        if (index == 0)
            Console.Write("beginning of sequence and ");
        else
            Console.Write($"{collection[index - 1]} and ");

        if (index == collection.Count)
            Console.WriteLine("end of sequence.");
        else
            Console.WriteLine($"{collection[index]}.");
    }
    else
    {
        Console.WriteLine($"Found at index {index}.");
    }
}

Se alltid till att använda samma typ av jämförelse för sortering och sökning. Att använda olika jämförelsetyper för sortering och sökning ger oväntade resultat.

Samlingsklasser som System.Collections.Hashtable, System.Collections.Generic.Dictionary<TKey,TValue>och System.Collections.Generic.List<T> har konstruktorer som tar en System.StringComparer parameter när typen av element eller nycklar är string. I allmänhet bör du använda dessa konstruktorer när det är möjligt och ange antingen StringComparer.Ordinal eller StringComparer.OrdinalIgnoreCase.

Se även