System.String, klasa

Ten artykuł zawiera dodatkowe uwagi dotyczące dokumentacji referencyjnej dla tego interfejsu API.

Ciąg jest sekwencyjną kolekcją znaków, która jest używana do reprezentowania tekstu. String Obiekt jest sekwencyjną kolekcją System.Char obiektów reprezentujących ciąg; System.Char obiekt odpowiada jednostce kodu UTF-16. Wartość String obiektu jest zawartością sekwencyjnej kolekcji System.Char obiektów, a ta wartość jest niezmienna (czyli tylko do odczytu). Aby uzyskać więcej informacji na temat niezmienności ciągów, zobacz sekcję Immutability (Niezmienność) i StringBuilder class (Klasa StringBuilder). Maksymalny rozmiar String obiektu w pamięci wynosi 2 GB lub około 1 mld znaków.

Aby uzyskać więcej informacji na temat unicode, UTF-16, jednostek kodu, punktów kodu i Char typów, Rune zobacz Wprowadzenie do kodowania znaków na platformie .NET.

Tworzenie wystąpienia obiektu String

Wystąpienie obiektu można utworzyć String w następujący sposób:

  • Przypisując literał ciągu do zmiennej String . Jest to najczęściej używana metoda tworzenia ciągu. W poniższym przykładzie użyto przypisania do utworzenia kilku ciągów. Należy pamiętać, że w językach C# i F#, ponieważ ukośnik odwrotny (\) jest znakiem ucieczki, ukośniki literału w ciągu muszą zostać uniknięty lub cały ciąg musi być @-quoted.

    string string1 = "This is a string created by assignment.";
    Console.WriteLine(string1);
    string string2a = "The path is C:\\PublicDocuments\\Report1.doc";
    Console.WriteLine(string2a);
    string string2b = @"The path is C:\PublicDocuments\Report1.doc";
    Console.WriteLine(string2b);
    // The example displays the following output:
    //       This is a string created by assignment.
    //       The path is C:\PublicDocuments\Report1.doc
    //       The path is C:\PublicDocuments\Report1.doc
    
    let string1 = "This is a string created by assignment."
    printfn "%s" string1
    let string2a = "The path is C:\\PublicDocuments\\Report1.doc"
    printfn "%s" string2a
    let string2b = @"The path is C:\PublicDocuments\Report1.doc"
    printfn "%s" string2b
    // The example displays the following output:
    //       This is a string created by assignment.
    //       The path is C:\PublicDocuments\Report1.doc
    //       The path is C:\PublicDocuments\Report1.doc
    
    Dim string1 As String = "This is a string created by assignment."
    Console.WriteLine(string1)
    Dim string2 As String = "The path is C:\PublicDocuments\Report1.doc"
    Console.WriteLine(string2)
    ' The example displays the following output:
    '       This is a string created by assignment.
    '       The path is C:\PublicDocuments\Report1.doc
    
  • Wywołując String konstruktor klasy. Poniższy przykład tworzy wystąpienia ciągów przez wywołanie kilku konstruktorów klas. Należy pamiętać, że niektóre konstruktory zawierają wskaźniki do tablic znaków lub podpisane tablice bajtów jako parametry. Visual Basic nie obsługuje wywołań tych konstruktorów. Aby uzyskać szczegółowe informacje o String konstruktorach, zobacz podsumowanie konstruktora String .

    char[] chars = { 'w', 'o', 'r', 'd' };
    sbyte[] bytes = { 0x41, 0x42, 0x43, 0x44, 0x45, 0x00 };
    
    // Create a string from a character array.
    string string1 = new string(chars);
    Console.WriteLine(string1);
    
    // Create a string that consists of a character repeated 20 times.
    string string2 = new string('c', 20);
    Console.WriteLine(string2);
    
    string stringFromBytes = null;
    string stringFromChars = null;
    unsafe
    {
       fixed (sbyte* pbytes = bytes)
       {
          // Create a string from a pointer to a signed byte array.
          stringFromBytes = new string(pbytes);
       }
       fixed (char* pchars = chars)
       {
          // Create a string from a pointer to a character array.
          stringFromChars = new string(pchars);
       }
    }
    Console.WriteLine(stringFromBytes);
    Console.WriteLine(stringFromChars);
    // The example displays the following output:
    //       word
    //       cccccccccccccccccccc
    //       ABCDE
    //       word
    
    let chars = [| 'w'; 'o'; 'r'; 'd' |]
    let bytes = [| 0x41y; 0x42y; 0x43y; 0x44y; 0x45y; 0x00y |]
    
    // Create a string from a character array.
    let string1 = String chars
    printfn "%s" string1
    
    // Create a string that consists of a character repeated 20 times.
    let string2 = String('c', 20)
    printfn "%s" string2
    
    let stringFromBytes =
        // Create a string from a pointer to a signed byte array.
        use pbytes = fixed bytes
        String pbytes
    let stringFromChars = 
        // Create a string from a pointer to a character array.
        use pchars = fixed chars
        String pchars
    
    printfn $"{stringFromBytes}"
    printfn $"{stringFromChars}"
    // The example displays the following output:
    //       word
    //       cccccccccccccccccccc
    //       ABCDE
    //       word
    
    Dim chars() As Char = {"w"c, "o"c, "r"c, "d"c}
    
    ' Create a string from a character array.
    Dim string1 As New String(chars)
    Console.WriteLine(string1)
    
    ' Create a string that consists of a character repeated 20 times.
    Dim string2 As New String("c"c, 20)
    Console.WriteLine(string2)
    ' The example displays the following output:
    '       word
    '       cccccccccccccccccccc
    
  • Używając operatora łączenia ciągów (+ w języku C# i F# oraz i + w Visual Basic), aby utworzyć pojedynczy ciąg z dowolnej kombinacji String wystąpień i literałów ciągu. Poniższy przykład ilustruje użycie operatora łączenia ciągów.

    string string1 = "Today is " + DateTime.Now.ToString("D") + ".";
    Console.WriteLine(string1);
    
    string string2 = "This is one sentence. " + "This is a second. ";
    string2 += "This is a third sentence.";
    Console.WriteLine(string2);
    // The example displays output like the following:
    //    Today is Tuesday, July 06, 2011.
    //    This is one sentence. This is a second. This is a third sentence.
    
    let string1 = "Today is " + DateTime.Now.ToString("D") + "."
    printfn $"{string1}"
    
    let string2 = "This is one sentence. " + "This is a second. "
    let string2 = string2 + "This is a third sentence."
    printfn $"{string2}"
    // The example displays output like the following:
    //    Today is Tuesday, July 06, 2011.
    //    This is one sentence. This is a second. This is a third sentence.
    
    Dim string1 As String = "Today is " + Date.Now.ToString("D") + "."
    Console.WriteLine(string1)
    Dim string2 As String = "This is one sentence. " + "This is a second. "
    string2 += "This is a third sentence."
    Console.WriteLine(string2)
    ' The example displays output like the following:
    '    Today is Tuesday, July 06, 2011.
    '    This is one sentence. This is a second. This is a third sentence.
    
  • Przez pobranie właściwości lub wywołanie metody zwracającej ciąg. W poniższym przykładzie String użyto metod klasy do wyodrębnienia podciągów z większego ciągu.

    string sentence = "This sentence has five words.";
    // Extract the second word.
    int startPosition = sentence.IndexOf(" ") + 1;
    string word2 = sentence.Substring(startPosition,
                                      sentence.IndexOf(" ", startPosition) - startPosition);
    Console.WriteLine("Second word: " + word2);
    // The example displays the following output:
    //       Second word: sentence
    
    let sentence = "This sentence has five words."
    // Extract the second word.
    let startPosition = sentence.IndexOf " " + 1
    let word2 = 
        sentence.Substring(startPosition, sentence.IndexOf(" ", startPosition) - startPosition)
    printfn $"Second word: {word2}"
    // The example displays the following output:
    //       Second word: sentence
    
    Dim sentence As String = "This sentence has five words."
    ' Extract the second word.
    Dim startPosition As Integer = sentence.IndexOf(" ") + 1
    Dim word2 As String = sentence.Substring(startPosition,
                                           sentence.IndexOf(" ", startPosition) - startPosition)
    Console.WriteLine("Second word: " + word2)
    ' The example displays the following output:
    '       Second word: sentence
    
  • Wywołując metodę formatowania, aby przekonwertować wartość lub obiekt na jego reprezentację ciągu. W poniższym przykładzie użyto funkcji formatowania złożonego, aby osadzić reprezentację ciągu dwóch obiektów w ciągu.

    DateTime dateAndTime = new DateTime(2011, 7, 6, 7, 32, 0);
    double temperature = 68.3;
    string result = String.Format("At {0:t} on {0:D}, the temperature was {1:F1} degrees Fahrenheit.",
                                  dateAndTime, temperature);
    Console.WriteLine(result);
    // The example displays the following output:
    //       At 7:32 AM on Wednesday, July 06, 2011, the temperature was 68.3 degrees Fahrenheit.
    
    let dateAndTime = DateTime(2011, 7, 6, 7, 32, 0)
    let temperature = 68.3
    String.Format("At {0:t} on {0:D}, the temperature was {1:F1} degrees Fahrenheit.", dateAndTime, temperature)
    |> printfn "%s"
    // The example displays the following output:
    //       At 7:32 AM on Wednesday, July 06, 2011, the temperature was 68.3 degrees Fahrenheit.
    
    Dim dateAndTime As DateTime = #07/06/2011 7:32:00AM#
    Dim temperature As Double = 68.3
    Dim result As String = String.Format("At {0:t} on {0:D}, the temperature was {1:F1} degrees Fahrenheit.",
                                       dateAndTime, temperature)
    Console.WriteLine(result)
    ' The example displays the following output:
    '       At 7:32 AM on Wednesday, July 06, 2011, the temperature was 68.3 degrees Fahrenheit.
    

Obiekty char i znaki Unicode

Każdy znak w ciągu jest definiowany przez wartość skalarną Unicode, nazywaną również punktem kodu Unicode lub wartością porządkową (liczbową) znaku Unicode. Każdy punkt kodu jest kodowany przy użyciu kodowania UTF-16, a wartość liczbowa każdego elementu kodowania jest reprezentowana przez Char obiekt.

Uwaga

Należy pamiętać, że ponieważ String wystąpienie składa się z sekwencyjnej kolekcji jednostek kodu UTF-16, można utworzyć String obiekt, który nie jest poprawnie sformułowanym ciągiem Unicode. Na przykład można utworzyć ciąg, który ma niski surogat bez odpowiadającego mu wysokiego zastępcy. Chociaż niektóre metody, takie jak metody kodowania i dekodowania obiektów w System.Text przestrzeni nazw, mogą wykonywać kontrole, aby upewnić się, że ciągi są poprawnie sformułowane, String składowe klasy nie zapewniają prawidłowego utworzenia ciągu.

Pojedynczy Char obiekt zwykle reprezentuje pojedynczy punkt kodu, czyli wartość Char liczbową punktu kodu. Na przykład punkt kodu znaku "a" to U+0061. Jednak punkt kodu może wymagać więcej niż jednego zakodowanego elementu (więcej niż jeden Char obiekt). Standard Unicode definiuje dwa typy znaków, które odpowiadają wielu Char obiektom: grafu i dodatkowe punkty kodu Unicode, które odpowiadają znakom w płaszczyznach dodatkowych Unicode.

  • Grafeme jest reprezentowany przez znak podstawowy, po którym następuje co najmniej jeden znak łączący. Na przykład znak ä jest reprezentowany przez Char obiekt, którego punkt kodu to U+0061, po którym następuje Char obiekt, którego punkt kodu to U+0308. Ten znak można również zdefiniować za pomocą pojedynczego Char obiektu, który ma punkt kodu U+00E4. Jak pokazano w poniższym przykładzie, porównanie uwzględniające kulturę równości wskazuje, że te dwie reprezentacje są równe, chociaż zwykłe porównanie porządkowe nie. Jeśli jednak dwa ciągi są znormalizowane, porównanie porządkowe wskazuje również, że są one równe. (Aby uzyskać więcej informacji na temat normalizacji ciągów, zobacz Sekcja normalizacji .)

    using System;
    using System.Globalization;
    using System.IO;
    
    public class Example5
    {
       public static void Main()
       {
          StreamWriter sw = new StreamWriter(@".\graphemes.txt");
          string grapheme = "\u0061\u0308";
          sw.WriteLine(grapheme);
          
          string singleChar = "\u00e4";
          sw.WriteLine(singleChar);
                
          sw.WriteLine("{0} = {1} (Culture-sensitive): {2}", grapheme, singleChar, 
                       String.Equals(grapheme, singleChar, 
                                     StringComparison.CurrentCulture));
          sw.WriteLine("{0} = {1} (Ordinal): {2}", grapheme, singleChar, 
                       String.Equals(grapheme, singleChar, 
                                     StringComparison.Ordinal));
          sw.WriteLine("{0} = {1} (Normalized Ordinal): {2}", grapheme, singleChar, 
                       String.Equals(grapheme.Normalize(), 
                                     singleChar.Normalize(), 
                                     StringComparison.Ordinal));
          sw.Close(); 
       }
    }
    // The example produces the following output:
    //       ä
    //       ä
    //       ä = ä (Culture-sensitive): True
    //       ä = ä (Ordinal): False
    //       ä = ä (Normalized Ordinal): True
    
    open System
    open System.IO
    
    do
        use sw = new StreamWriter(@".\graphemes.txt")
        let grapheme = "\u0061\u0308"
        sw.WriteLine grapheme
    
        let singleChar = "\u00e4"
        sw.WriteLine singleChar
    
        sw.WriteLine("{0} = {1} (Culture-sensitive): {2}", grapheme, singleChar, 
                    String.Equals(grapheme, singleChar,
                                    StringComparison.CurrentCulture))
        sw.WriteLine("{0} = {1} (Ordinal): {2}", grapheme, singleChar,
                    String.Equals(grapheme, singleChar,
                                    StringComparison.Ordinal))
        sw.WriteLine("{0} = {1} (Normalized Ordinal): {2}", grapheme, singleChar,
                    String.Equals(grapheme.Normalize(),
                                    singleChar.Normalize(),
                                    StringComparison.Ordinal))
    // The example produces the following output:
    //       ä
    //       ä
    //       ä = ä (Culture-sensitive): True
    //       ä = ä (Ordinal): False
    //       ä = ä (Normalized Ordinal): True
    
    Imports System.Globalization
    Imports System.IO
    
    Module Example9
        Public Sub Main()
            Dim sw As New StreamWriter(".\graphemes.txt")
            Dim grapheme As String = ChrW(&H61) + ChrW(&H308)
            sw.WriteLine(grapheme)
    
            Dim singleChar As String = ChrW(&HE4)
            sw.WriteLine(singleChar)
    
            sw.WriteLine("{0} = {1} (Culture-sensitive): {2}", grapheme, singleChar,
                       String.Equals(grapheme, singleChar,
                                     StringComparison.CurrentCulture))
            sw.WriteLine("{0} = {1} (Ordinal): {2}", grapheme, singleChar,
                       String.Equals(grapheme, singleChar,
                                     StringComparison.Ordinal))
            sw.WriteLine("{0} = {1} (Normalized Ordinal): {2}", grapheme, singleChar,
                       String.Equals(grapheme.Normalize(),
                                     singleChar.Normalize(),
                                     StringComparison.Ordinal))
            sw.Close()
        End Sub
    End Module
    ' The example produces the following output:
    '       ä
    '       ä
    '       ä = ä (Culture-sensitive): True
    '       ä = ä (Ordinal): False
    '       ä = ä (Normalized Ordinal): True
    
  • Dodatkowy punkt kodu Unicode (para zastępcza) jest reprezentowany przez Char obiekt, którego punkt kodu jest wysokim zastępcą, po którym następuje obiekt, którego punkt kodu jest niskim zastępcą Char . Jednostki kodu o wysokim stopniu zastępców wahają się od U+D800 do U+DBFF. Jednostki kodu o niskiej wartości zastępczej wahają się od U+DC00 do U+DFFF. Pary zastępcze są używane do reprezentowania znaków w 16 płaszczyznach dodatkowych Unicode. Poniższy przykład tworzy znak zastępczy i przekazuje go do Char.IsSurrogatePair(Char, Char) metody w celu określenia, czy jest to para zastępcza.

    string surrogate = "\uD800\uDC03";
    for (int ctr = 0; ctr < surrogate.Length; ctr++) 
       Console.Write($"U+{(ushort)surrogate[ctr]:X2} ");
    
    Console.WriteLine();
    Console.WriteLine("   Is Surrogate Pair: {0}", 
                      Char.IsSurrogatePair(surrogate[0], surrogate[1]));
    // The example displays the following output:
    //       U+D800 U+DC03
    //          Is Surrogate Pair: True
    
    open System
    
    let surrogate = "\uD800\uDC03"
    for i = 0 to surrogate.Length - 1 do
        printf $"U+{uint16 surrogate[i]:X2} "
    
    printfn $"\n   Is Surrogate Pair: {Char.IsSurrogatePair(surrogate[0], surrogate[1])}"
    // The example displays the following output:
    //       U+D800 U+DC03
    //          Is Surrogate Pair: True
    
    Module Example20
        Public Sub Main()
            Dim surrogate As String = ChrW(&HD800) + ChrW(&HDC03)
            For ctr As Integer = 0 To surrogate.Length - 1
                Console.Write("U+{0:X2} ", Convert.ToUInt16(surrogate(ctr)))
            Next
            Console.WriteLine()
            Console.WriteLine("   Is Surrogate Pair: {0}",
                            Char.IsSurrogatePair(surrogate(0), surrogate(1)))
        End Sub
    End Module
    
    ' The example displays the following output:
    '       U+D800 U+DC03
    '          Is Surrogate Pair: True
    

Standard Unicode

Znaki w ciągu są reprezentowane przez zakodowane jednostki kodu UTF-16, które odpowiadają wartościom Char .

Każdy znak w ciągu ma skojarzona kategoria znaków Unicode, która jest reprezentowana na platformie .NET przez UnicodeCategory wyliczenie. Kategorię znaku lub pary zastępczej można określić przez wywołanie CharUnicodeInfo.GetUnicodeCategory metody .

Platforma .NET przechowuje własną tabelę znaków i odpowiadające im kategorie, co gwarantuje, że określona wersja implementacji platformy .NET uruchomiona na różnych platformach zwraca identyczne informacje o kategorii znaków. We wszystkich wersjach platformy .NET i na wszystkich platformach systemu operacyjnego informacje o kategorii znaków są udostępniane przez bazę danych znaków Unicode.

W poniższej tabeli wymieniono wersje platformy .NET i wersje standardu Unicode, na których opierają się ich kategorie znaków.

Wersja platformy .NET Wersja standardu Unicode
.NET Framework 1.1 Standard Unicode w wersji 4.0.0
.NET Framework 2.0 Standard Unicode w wersji 5.0.0
.NET Framework 3.5 Standard Unicode w wersji 5.0.0
.NET Framework 4 Standard Unicode w wersji 5.0.0
.NET Framework 4.5 Standard Unicode w wersji 6.3.0
.NET Framework 4.5.1 Standard Unicode w wersji 6.3.0
.NET Framework 4.5.2 Standard Unicode w wersji 6.3.0
.NET framework 4.6 Standard Unicode w wersji 6.3.0
.NET Framework 4.6.1 Standard Unicode w wersji 6.3.0
.NET Framework 4.6.2 i nowsze wersje Standard Unicode w wersji 8.0.0
.NET Core 2.1 Standard Unicode w wersji 8.0.0
.NET Core 3.1 Standard Unicode w wersji 11.0.0
.NET 5 Standard Unicode w wersji 13.0.0

Ponadto platforma .NET obsługuje porównywanie ciągów i sortowanie na podstawie standardu Unicode. Począwszy od programu .NET Framework 4.5 działającego w systemie operacyjnym Windows 8 lub nowszym, środowisko uruchomieniowe deleguje porównanie ciągów i operacje sortowania do systemu operacyjnego. W programach .NET Core i .NET 5 lub nowszych porównanie ciągów i sortowanie są dostarczane przez składniki międzynarodowe dla bibliotek Unicode (z wyjątkiem wersji systemu Windows wcześniejszych niż Aktualizacja systemu Windows 10 z maja 2019). W poniższej tabeli wymieniono wersje platformy .NET oraz wersje standardu Unicode, na których są oparte porównania i sortowania znaków.

Wersja platformy .NET Wersja standardu Unicode
.NET Framework 4.5 lub nowszy w systemie Windows 7 Standard Unicode w wersji 5.0.0
Program .NET Framework 4.5 lub nowszy w systemach operacyjnych Windows 8 lub nowszych Standard Unicode w wersji 6.3.0
.NET Core i .NET 5+ Zależy od wersji standardu Unicode obsługiwanej przez bazowy system operacyjny.

Osadzone znaki null

Na platformie .NET String obiekt może zawierać osadzone znaki null, które są liczone jako część długości ciągu. Jednak w niektórych językach, takich jak C i C++, znak null wskazuje koniec ciągu; nie jest traktowana jako część ciągu i nie jest liczone jako część długości ciągu. Oznacza to, że następujące typowe założenia, że programiści lub biblioteki języka C++ napisane w języku C lub C++ mogą nie być prawidłowe w przypadku zastosowania do String obiektów:

  • Wartość zwracana przez strlen funkcje lub wcslen nie musi być równa String.Length.

  • Ciąg utworzony przez strcpy_s funkcje lub wcscpy_s nie musi być identyczny z ciągiem utworzonym przez metodę String.Copy .

Należy upewnić się, że natywny kod C i C++, który tworzy wystąpienie String obiektów i kod przekazywany String przez wywołanie platformy, nie zakłada, że osadzony znak null oznacza koniec ciągu.

Osadzone znaki null w ciągu są również traktowane inaczej, gdy ciąg jest sortowany (lub porównywany) i podczas wyszukiwania ciągu. Znaki null są ignorowane podczas przeprowadzania porównań wrażliwych na kulturę między dwoma ciągami, w tym porównania przy użyciu niezmiennej kultury. Są one uznawane tylko za porównania porządkowe lub porządkowe bez uwzględniania wielkości liter. Z drugiej strony osadzone znaki null są zawsze brane pod uwagę podczas wyszukiwania ciągu za pomocą metod, takich jak Contains, StartsWithi IndexOf.

Ciągi i indeksy

Indeks jest pozycją Char obiektu (a nie znakiem Unicode) w obiekcie String. Indeks jest liczbą nienależącą do zera, która rozpoczyna się od pierwszej pozycji w ciągu, która jest pozycją indeksu zero. Wiele metod wyszukiwania, takich jak IndexOf i LastIndexOf, zwraca indeks znaku lub podciąg w wystąpieniu ciągu.

Właściwość Chars[] umożliwia dostęp do poszczególnych Char obiektów według ich pozycji indeksu w ciągu. Chars[] Ponieważ właściwość jest właściwością domyślną (w Visual Basic) lub indeksatorem (w języku C# i F#), można uzyskać dostęp do poszczególnych Char obiektów w ciągu przy użyciu kodu takiego jak poniżej. Ten kod wyszukuje znaki odstępu lub znaki interpunkcyjne w ciągu, aby określić, ile wyrazów zawiera ciąg.

string s1 = "This string consists of a single short sentence.";
int nWords = 0;

s1 = s1.Trim();      
for (int ctr = 0; ctr < s1.Length; ctr++) {
   if (Char.IsPunctuation(s1[ctr]) | Char.IsWhiteSpace(s1[ctr]))
      nWords++;              
}
Console.WriteLine("The sentence\n   {0}\nhas {1} words.",
                  s1, nWords);                                                                     
// The example displays the following output:
//       The sentence
//          This string consists of a single short sentence.
//       has 8 words.
let s1 = "This string consists of a single short sentence."
let mutable nWords = 0

for i = 0 to s1.Length - 1 do
    if Char.IsPunctuation s1[i] || Char.IsWhiteSpace s1[i] then
        nWords <- nWords + 1
printfn $"The sentence\n   {s1}\nhas {nWords} words."
// The example displays the following output:
//       The sentence
//          This string consists of a single short sentence.
//       has 8 words.
Module Example12
    Public Sub Main()
        Dim s1 As String = "This string consists of a single short sentence."
        Dim nWords As Integer = 0

        s1 = s1.Trim()
        For ctr As Integer = 0 To s1.Length - 1
            If Char.IsPunctuation(s1(ctr)) Or Char.IsWhiteSpace(s1(ctr)) Then
                nWords += 1
            End If
        Next
        Console.WriteLine("The sentence{2}   {0}{2}has {1} words.",
                        s1, nWords, vbCrLf)
    End Sub
End Module
' The example displays the following output:
'       The sentence
'          This string consists of a single short sentence.
'       has 8 words.

String Ponieważ klasa implementuje IEnumerable interfejs, można również iterować przez Char obiekty w ciągu przy użyciu foreach konstrukcji, jak pokazano w poniższym przykładzie.

string s1 = "This string consists of a single short sentence.";
int nWords = 0;

s1 = s1.Trim();      
foreach (var ch in s1) {
   if (Char.IsPunctuation(ch) | Char.IsWhiteSpace(ch))
      nWords++;              
}
Console.WriteLine("The sentence\n   {0}\nhas {1} words.",
                  s1, nWords);                                                                     
// The example displays the following output:
//       The sentence
//          This string consists of a single short sentence.
//       has 8 words.
let s1 = "This string consists of a single short sentence."
let mutable nWords = 0

for ch in s1 do
    if Char.IsPunctuation ch || Char.IsWhiteSpace ch then
        nWords <- nWords + 1
printfn $"The sentence\n   {s1}\nhas {nWords} words."
// The example displays the following output:
//       The sentence
//          This string consists of a single short sentence.
//       has 8 words.
Module Example13
    Public Sub Main()
        Dim s1 As String = "This string consists of a single short sentence."
        Dim nWords As Integer = 0

        s1 = s1.Trim()
        For Each ch In s1
            If Char.IsPunctuation(ch) Or Char.IsWhiteSpace(ch) Then
                nWords += 1
            End If
        Next
        Console.WriteLine("The sentence{2}   {0}{2}has {1} words.",
                        s1, nWords, vbCrLf)
    End Sub
End Module
' The example displays the following output:
'       The sentence
'          This string consists of a single short sentence.
'       has 8 words.

Kolejne wartości indeksu mogą nie odpowiadać kolejnym znakom Unicode, ponieważ znak Unicode może być zakodowany jako więcej niż jeden Char obiekt. W szczególności ciąg może zawierać wieloznaczne jednostki tekstu, które są tworzone przez znak podstawowy, po którym następuje co najmniej jeden znak łączący lub przez pary zastępcze. Aby pracować z znakami Unicode zamiast Char obiektów, użyj System.Globalization.StringInfo klas i TextElementEnumerator lub String.EnumerateRunes metody i Rune struktury. Poniższy przykład ilustruje różnicę między kodem, który działa z obiektami i kodem, który działa z znakami Char Unicode. Porównuje liczbę znaków lub elementów tekstowych w każdym słowie zdania. Ciąg zawiera dwie sekwencje znaku podstawowego, po którym następuje znak łączący.

// First sentence of The Mystery of the Yellow Room, by Leroux.
string opening = "Ce n'est pas sans une certaine émotion que "+
                 "je commence à raconter ici les aventures " +
                 "extraordinaires de Joseph Rouletabille."; 
// Character counters.
int nChars = 0;
// Objects to store word count.
List<int> chars = new List<int>();
List<int> elements = new List<int>();

foreach (var ch in opening) {
   // Skip the ' character.
   if (ch == '\u0027') continue;
        
   if (Char.IsWhiteSpace(ch) | (Char.IsPunctuation(ch))) {
      chars.Add(nChars);
      nChars = 0;
   }
   else {
      nChars++;
   }
}

System.Globalization.TextElementEnumerator te = 
   System.Globalization.StringInfo.GetTextElementEnumerator(opening);
while (te.MoveNext()) {
   string s = te.GetTextElement();   
   // Skip the ' character.
   if (s == "\u0027") continue;
   if ( String.IsNullOrEmpty(s.Trim()) | (s.Length == 1 && Char.IsPunctuation(Convert.ToChar(s)))) {
      elements.Add(nChars);         
      nChars = 0;
   }
   else {
      nChars++;
   }
}

// Display character counts.
Console.WriteLine("{0,6} {1,20} {2,20}",
                  "Word #", "Char Objects", "Characters"); 
for (int ctr = 0; ctr < chars.Count; ctr++) 
   Console.WriteLine("{0,6} {1,20} {2,20}",
                     ctr, chars[ctr], elements[ctr]); 
// The example displays the following output:
//       Word #         Char Objects           Characters
//            0                    2                    2
//            1                    4                    4
//            2                    3                    3
//            3                    4                    4
//            4                    3                    3
//            5                    8                    8
//            6                    8                    7
//            7                    3                    3
//            8                    2                    2
//            9                    8                    8
//           10                    2                    1
//           11                    8                    8
//           12                    3                    3
//           13                    3                    3
//           14                    9                    9
//           15                   15                   15
//           16                    2                    2
//           17                    6                    6
//           18                   12                   12
open System
open System.Globalization

// First sentence of The Mystery of the Yellow Room, by Leroux.
let opening = "Ce n'est pas sans une certaine émotion que je commence à raconter ici les aventures extraordinaires de Joseph Rouletabille."
// Character counters.
let mutable nChars = 0
// Objects to store word count.
let chars = ResizeArray<int>()
let elements = ResizeArray<int>()

for ch in opening do
    // Skip the ' character.
    if ch <> '\u0027' then
        if Char.IsWhiteSpace ch || Char.IsPunctuation ch then
            chars.Add nChars
            nChars <- 0
        else
            nChars <- nChars + 1

let te = StringInfo.GetTextElementEnumerator opening
while te.MoveNext() do
    let s = te.GetTextElement()
    // Skip the ' character.
    if s <> "\u0027" then
        if String.IsNullOrEmpty(s.Trim()) || (s.Length = 1 && Char.IsPunctuation(Convert.ToChar s)) then
            elements.Add nChars
            nChars <- 0
        else
            nChars <- nChars + 1

// Display character counts.
printfn "%6s %20s %20s" "Word #" "Char Objects " "Characters"
for i = 0 to chars.Count - 1 do
    printfn "%6d %20d %20d" i chars[i] elements[i]
// The example displays the following output:
//       Word #         Char Objects           Characters
//            0                    2                    2
//            1                    4                    4
//            2                    3                    3
//            3                    4                    4
//            4                    3                    3
//            5                    8                    8
//            6                    8                    7
//            7                    3                    3
//            8                    2                    2
//            9                    8                    8
//           10                    2                    1
//           11                    8                    8
//           12                    3                    3
//           13                    3                    3
//           14                    9                    9
//           15                   15                   15
//           16                    2                    2
//           17                    6                    6
//           18                   12                   12
Imports System.Collections.Generic
Imports System.Globalization

Module Example14
    Public Sub Main()
        ' First sentence of The Mystery of the Yellow Room, by Leroux.
        Dim opening As String = "Ce n'est pas sans une certaine émotion que " +
                              "je commence à raconter ici les aventures " +
                              "extraordinaires de Joseph Rouletabille."
        ' Character counters.
        Dim nChars As Integer = 0
        ' Objects to store word count.
        Dim chars As New List(Of Integer)()
        Dim elements As New List(Of Integer)()

        For Each ch In opening
            ' Skip the ' character.
            If ch = ChrW(&H27) Then Continue For

            If Char.IsWhiteSpace(ch) Or Char.IsPunctuation(ch) Then
                chars.Add(nChars)
                nChars = 0
            Else
                nChars += 1
            End If
        Next

        Dim te As TextElementEnumerator = StringInfo.GetTextElementEnumerator(opening)
        Do While te.MoveNext()
            Dim s As String = te.GetTextElement()
            ' Skip the ' character.
            If s = ChrW(&H27) Then Continue Do
            If String.IsNullOrEmpty(s.Trim()) Or (s.Length = 1 AndAlso Char.IsPunctuation(Convert.ToChar(s))) Then
                elements.Add(nChars)
                nChars = 0
            Else
                nChars += 1
            End If
        Loop

        ' Display character counts.
        Console.WriteLine("{0,6} {1,20} {2,20}",
                        "Word #", "Char Objects", "Characters")
        For ctr As Integer = 0 To chars.Count - 1
            Console.WriteLine("{0,6} {1,20} {2,20}",
                           ctr, chars(ctr), elements(ctr))
        Next
    End Sub
End Module
' The example displays the following output:
'    Word #         Char Objects           Characters
'         0                    2                    2
'         1                    4                    4
'         2                    3                    3
'         3                    4                    4
'         4                    3                    3
'         5                    8                    8
'         6                    8                    7
'         7                    3                    3
'         8                    2                    2
'         9                    8                    8
'        10                    2                    1
'        11                    8                    8
'        12                    3                    3
'        13                    3                    3
'        14                    9                    9
'        15                   15                   15
'        16                    2                    2
'        17                    6                    6
'        18                   12                   12

Ten przykład działa z elementami tekstowymi przy użyciu StringInfo.GetTextElementEnumerator metody i TextElementEnumerator klasy, aby wyliczyć wszystkie elementy tekstowe w ciągu. Możesz również pobrać tablicę zawierającą indeks początkowy każdego elementu tekstowego, wywołując metodę StringInfo.ParseCombiningCharacters .

Aby uzyskać więcej informacji na temat pracy z jednostkami tekstu, a nie poszczególnymi Char wartościami, zobacz Wprowadzenie do kodowania znaków na platformie .NET.

Ciągi zerowe i puste ciągi

Ciąg, który został zadeklarowany, ale nie został przypisany, jest nullwartością . Próba wywołania metod w tym ciągu zgłasza błąd NullReferenceException. Ciąg o wartości null różni się od pustego ciągu, który jest ciągiem, którego wartość to "" lub String.Empty. W niektórych przypadkach przekazanie ciągu null lub pustego ciągu jako argumentu w wywołaniu metody zgłasza wyjątek. Na przykład przekazanie ciągu null do Int32.Parse metody zgłasza ArgumentNullExceptionwartość , a przekazanie pustego ciągu zgłasza wartość FormatException. W innych przypadkach argument metody może być ciągiem null lub pustym ciągiem. Jeśli na przykład udostępniasz implementację IFormattable dla klasy, chcesz porównać zarówno ciąg o wartości null, jak i pusty ciąg z ogólnym specyfikatorem formatu ("G").

Klasa String zawiera następujące dwie wygodne metody, które umożliwiają przetestowanie, czy ciąg jest null , czy pusty:

  • IsNullOrEmpty, który wskazuje, czy ciąg jest null albo jest równy String.Empty. Ta metoda eliminuje konieczność użycia kodu, takiego jak:

    if (str == null || str.Equals(String.Empty))
    
    if str = null || str.Equals String.Empty then
    
    If str Is Nothing OrElse str.Equals(String.Empty) Then
    
  • IsNullOrWhiteSpace, który wskazuje, czy ciąg to null, równa String.Emptysię , lub składa się wyłącznie z białych znaków. Ta metoda eliminuje konieczność użycia kodu, takiego jak:

    if (str == null || str.Equals(String.Empty) || str.Trim().Equals(String.Empty))
    
    if str = null || str.Equals String.Empty || str.Trim().Equals String.Empty then
    
    If str Is Nothing OrElse str.Equals(String.Empty) OrElse str.Trim().Equals(String.Empty) Then
    

W poniższym przykładzie użyto IsNullOrEmpty metody w IFormattable.ToString implementacji klasy niestandardowej Temperature . Metoda obsługuje ciągi formatu "G", "C", "F" i "K". Jeśli pusty ciąg formatu lub ciąg formatu, którego wartość jest null przekazywana do metody, jego wartość zostanie zmieniona na ciąg formatu "G".

public string ToString(string format, IFormatProvider provider) 
{
   if (String.IsNullOrEmpty(format)) format = "G";  
   if (provider == null) provider = CultureInfo.CurrentCulture;
   
   switch (format.ToUpperInvariant())
   {
      // Return degrees in Celsius.    
      case "G":
      case "C":
         return temp.ToString("F2", provider) + "°C";
      // Return degrees in Fahrenheit.
      case "F": 
         return (temp * 9 / 5 + 32).ToString("F2", provider) + "°F";
      // Return degrees in Kelvin.
      case "K":   
         return (temp + 273.15).ToString();
      default:
         throw new FormatException(
               String.Format("The {0} format string is not supported.", 
                             format));
   }                                   
}
member _.ToString(format: string, provider: IFormatProvider) =
    let format = 
        if String.IsNullOrEmpty format then "G" else format
    
    let provider: IFormatProvider = 
        if provider = null then CultureInfo.CurrentCulture else provider

    match format.ToUpperInvariant() with
    // Return degrees in Celsius.
    | "G"
    | "C" ->
        temp.ToString("F2", provider) + "°C"
    // Return degrees in Fahrenheit.
    | "F" ->
        (temp * 9. / 5. + 32.).ToString("F2", provider) + "°F"
    // Return degrees in Kelvin.
    | "K" ->
        (temp + 273.15).ToString()
    | _ ->
        raise (FormatException(String.Format("The {0} format string is not supported.",format)))
Public Overloads Function ToString(fmt As String, provider As IFormatProvider) As String _
               Implements IFormattable.ToString
    If String.IsNullOrEmpty(fmt) Then fmt = "G"
    If provider Is Nothing Then provider = CultureInfo.CurrentCulture

    Select Case fmt.ToUpperInvariant()
     ' Return degrees in Celsius.    
        Case "G", "C"
            Return temp.ToString("F2", provider) + "°C"
     ' Return degrees in Fahrenheit.
        Case "F"
            Return (temp * 9 / 5 + 32).ToString("F2", provider) + "°F"
     ' Return degrees in Kelvin.
        Case "K"
            Return (temp + 273.15).ToString()
        Case Else
            Throw New FormatException(
              String.Format("The {0} format string is not supported.",
                            fmt))
    End Select
End Function

Niezmienność i klasa StringBuilder

String Obiekt jest nazywany niezmiennym (tylko do odczytu), ponieważ nie można zmodyfikować jego wartości po jego utworzeniu. Metody, które wydają się modyfikować String obiekt, faktycznie zwracają nowy String obiekt, który zawiera modyfikację.

Ponieważ ciągi są niezmienne, procedury manipulowania ciągami, które wykonują powtarzające się dodatki lub usunięcia do tego, co wydaje się być pojedynczym ciągiem, może dokładnie określić znaczną karę za wydajność. Na przykład poniższy kod używa generatora liczb losowych do utworzenia ciągu z 1000 znakami w zakresie 0x0001 do 0x052F. Mimo że kod wydaje się używać łączenia ciągów w celu dołączenia nowego znaku do istniejącego ciągu o nazwie str, faktycznie tworzy nowy String obiekt dla każdej operacji łączenia.

using System;
using System.IO;
using System.Text;

public class Example6
{
   public static void Main()
   {
      Random rnd = new Random();
      
      string str = String.Empty;
      StreamWriter sw = new StreamWriter(@".\StringFile.txt", 
                           false, Encoding.Unicode);

      for (int ctr = 0; ctr <= 1000; ctr++) {
         str += (char)rnd.Next(1, 0x0530);
         if (str.Length % 60 == 0)
            str += Environment.NewLine;          
      }                    
      sw.Write(str);
      sw.Close();
   }
}
open System
open System.IO
open System.Text

do
    let rnd = Random()

    let mutable str = String.Empty
    use sw = new StreamWriter(@".\StringFile.txt", false, Encoding.Unicode)
    for _ = 0 to 1000 do
        str <- str + (rnd.Next(1, 0x0530) |> char |> string)
        if str.Length % 60 = 0 then
            str <- str + Environment.NewLine
    sw.Write str
Imports System.IO
Imports System.Text

Module Example10
    Public Sub Main()
        Dim rnd As New Random()

        Dim str As String = String.Empty
        Dim sw As New StreamWriter(".\StringFile.txt",
                           False, Encoding.Unicode)

        For ctr As Integer = 0 To 1000
            str += ChrW(rnd.Next(1, &H530))
            If str.Length Mod 60 = 0 Then str += vbCrLf
        Next
        sw.Write(str)
        sw.Close()
    End Sub
End Module

Możesz użyć StringBuilder klasy zamiast String klasy dla operacji, które wprowadzają wiele zmian w wartości ciągu. W przeciwieństwie do wystąpień String klasy StringBuilder obiekty są modyfikowalne. Podczas łączenia, dołączania lub usuwania podciągów z ciągu operacje są wykonywane na jednym ciągu. Po zakończeniu modyfikowania wartości StringBuilder obiektu można wywołać jego StringBuilder.ToString metodę, aby przekonwertować ją na ciąg. W poniższym przykładzie użyto String w poprzednim przykładzie wartości , aby połączyć 1000 losowych znaków w zakresie, aby 0x0001 0x052F z obiektem StringBuilder .

using System;
using System.IO;
using System.Text;

public class Example10
{
   public static void Main()
   {
      Random rnd = new Random();
      StringBuilder sb = new StringBuilder();
      StreamWriter sw = new StreamWriter(@".\StringFile.txt", 
                                         false, Encoding.Unicode);

      for (int ctr = 0; ctr <= 1000; ctr++) {
         sb.Append((char)rnd.Next(1, 0x0530));
         if (sb.Length % 60 == 0)
            sb.AppendLine();          
      }                    
      sw.Write(sb.ToString());
      sw.Close();
   }
}
open System
open System.IO
open System.Text

do
    let rnd = Random()
    let sb = StringBuilder()
    use sw = new StreamWriter(@".\StringFile.txt", false, Encoding.Unicode)

    for _ = 0 to 1000 do
        sb.Append(rnd.Next(1, 0x0530) |> char) |> ignore
        if sb.Length % 60 = 0 then
            sb.AppendLine() |> ignore
    sw.Write(string sb)
Imports System.IO
Imports System.Text

Module Example11
    Public Sub Main()
        Dim rnd As New Random()
        Dim sb As New StringBuilder()
        Dim sw As New StreamWriter(".\StringFile.txt",
                                 False, Encoding.Unicode)

        For ctr As Integer = 0 To 1000
            sb.Append(ChrW(rnd.Next(1, &H530)))
            If sb.Length Mod 60 = 0 Then sb.AppendLine()
        Next
        sw.Write(sb.ToString())
        sw.Close()
    End Sub
End Module

Operacje porządkowe vs. operacje zależne od kultury

String Elementy członkowskie klasy wykonują operacje porządkowe lub wrażliwe na kulturę String (językowe) na obiekcie. Operacja porządkowa działa na wartości liczbowej każdego Char obiektu. Operacja wrażliwa na kulturę działa na wartość String obiektu i bierze pod uwagę reguły wielkości liter specyficzne dla kultury, sortowania, formatowania i analizowania. Operacje wrażliwe na kulturę są wykonywane w kontekście jawnie zadeklarowanej kultury lub niejawnej bieżącej kultury. Dwa rodzaje operacji mogą generować bardzo różne wyniki, gdy są wykonywane na tym samym ciągu.

Platforma .NET obsługuje również niewrażliwe na kulturę operacje ciągów językowych przy użyciu niezmiennej kultury (CultureInfo.InvariantCulture), która jest luźno oparta na ustawieniach kultury języka angielskiego niezależnie od regionu. W przeciwieństwie do innych System.Globalization.CultureInfo ustawień, ustawienia niezmiennej kultury są gwarantowane, aby zachować spójność na jednym komputerze, od systemu do systemu i w różnych wersjach platformy .NET. Niezmienna kultura może być postrzegana jako rodzaj czarnej skrzynki, która zapewnia stabilność porównań ciągów i porządkowania we wszystkich kulturach.

Ważne

Jeśli aplikacja podejmuje decyzję o zabezpieczeniach o identyfikatorze symbolicznym, takim jak nazwa pliku lub nazwany potok lub o utrwalonych danych, takich jak dane tekstowe w pliku XML, operacja powinna używać porównania porządkowego zamiast porównania wrażliwego na kulturę. Wynika to z faktu, że porównanie wrażliwe na kulturę może przynieść różne wyniki w zależności od kultury, natomiast porównanie porządkowe zależy wyłącznie od wartości binarnej porównywanych znaków.

Ważne

Większość metod wykonujących operacje na ciągach obejmuje przeciążenie, które ma parametr typu StringComparison, co umożliwia określenie, czy metoda wykonuje operację porządkową, czy wrażliwą na kulturę. Ogólnie rzecz biorąc, należy wywołać to przeciążenie, aby intencja wywołania metody jest jasna. Aby uzyskać najlepsze rozwiązania i wskazówki dotyczące używania operacji porządkowych i wrażliwych na kulturę w ciągach, zobacz Best Practices for Using Strings (Najlepsze rozwiązania dotyczące używania ciągów).

Operacje na potrzeby wielkości liter, analizowania i formatowania, porównywania i sortowania oraz testowania równości mogą być wrażliwe na porządkowe lub kulturowe. W poniższych sekcjach omówiono każdą kategorię operacji.

Napiwek

Zawsze należy wywołać przeciążenie metody, które sprawia, że intencja wywołania metody jest jasna. Na przykład zamiast wywoływać Compare(String, String) metodę w celu przeprowadzenia porównania z uwzględnieniem kultury dwóch ciągów przy użyciu konwencji bieżącej kultury, należy wywołać Compare(String, String, StringComparison) metodę z wartością StringComparison.CurrentCulture dla argumentu comparisonType . Aby uzyskać więcej informacji, zobacz Najlepsze rozwiązania dotyczące używania ciągów.

Tabele wag sortowania, zestaw plików tekstowych zawierających informacje o wagach znaków używanych w operacjach sortowania i porównywania, można pobrać z następujących linków:

Wielkość liter

Reguły wielkości liter określają sposób zmiany wielkości liter znaku Unicode; na przykład od małych liter do wielkich. Często operacja wielkości liter jest wykonywana przed porównaniem ciągów. Na przykład ciąg może zostać przekonwertowany na wielkie litery, aby można było go porównać z innym wielkim ciągiem. Znaki w ciągu można przekonwertować na małe litery, wywołując ToLower metodę or ToLowerInvariant , a następnie przekonwertować je na wielkie litery, wywołując ToUpper metodę or ToUpperInvariant . Ponadto można użyć TextInfo.ToTitleCase metody , aby przekonwertować ciąg na przypadek tytułu.

Uwaga

Platforma .NET Core uruchomiona tylko w systemach Linux i macOS: zachowanie sortowania dla kultur C i Posix jest zawsze uwzględniane wielkości liter, ponieważ te kultury nie używają oczekiwanej kolejności sortowania Unicode. Zalecamy używanie kultury innej niż C lub Posix do wykonywania operacji sortowania uwzględniających wielkość liter.

Operacje wielkości liter mogą być oparte na regułach bieżącej kultury, określonej kultury lub niezmiennej kultury. Ponieważ mapowania wielkości liter mogą się różnić w zależności od używanej kultury, wynik operacji wielkości liter może się różnić w zależności od kultury. Rzeczywiste różnice w wielkości liter to trzy rodzaje:

  • Różnice w przypadku mapowania LITERY ALFABETU ŁACIŃSKIEGO I (U+0049), ALFABET ŁACIŃSKI I (U+0069), WIELKA LITERA I Z KROPKĄ POWYŻEJ (U+0130) i ALFABET ŁACIŃSKI MAŁA LITERA KROPKOWA I (U+0131). W kulturach tr-TR (Turecki (Turcja)) i az-Latn-AZ (Azerbejdżan, Łaciński) oraz w kulturach tr, az i az-Latn neutralnych, małe litery alfabetu łacińskiego I to ALFABET ŁACIŃSKI MAŁA LITERA DOTLESS I, a wielkie litery alfabetu łacińskiego I to LITERA WIELKA I LITERA ALFABETU ŁACIŃSKIEGO I Z KROPKĄ POWYŻEJ. We wszystkich innych kulturach, w tym niezmiennej kultury, ALFABET ŁACIŃSKI MAŁE LITERY I i ALFABET ŁACIŃSKI WIELKA LITERA I są małymi literami i wielkimi odpowiednikami.

    W poniższym przykładzie pokazano, w jaki sposób porównanie ciągów mające na celu zapobieganie awarii dostępu do systemu plików może zakończyć się niepowodzeniem, jeśli opiera się na porównywaniu wielkości liter z uwzględnieniem kultury. (Powinny zostać użyte konwencje wielkości liter niezmiennej kultury).

    using System;
    using System.Globalization;
    using System.Threading;
    
    public class Example1
    {
       const string disallowed = "file";
       
       public static void Main()
       {
          IsAccessAllowed(@"FILE:\\\c:\users\user001\documents\FinancialInfo.txt");
       }
    
       private static void IsAccessAllowed(String resource)
       {
          CultureInfo[] cultures = { CultureInfo.CreateSpecificCulture("en-US"),
                                     CultureInfo.CreateSpecificCulture("tr-TR") };
          String scheme = null;
          int index = resource.IndexOfAny( new Char[] { '\\', '/' } );
          if (index > 0) 
             scheme = resource.Substring(0, index - 1);
    
          // Change the current culture and perform the comparison.
          foreach (var culture in cultures) {
             Thread.CurrentThread.CurrentCulture = culture;
             Console.WriteLine("Culture: {0}", CultureInfo.CurrentCulture.DisplayName);
             Console.WriteLine(resource);
             Console.WriteLine("Access allowed: {0}", 
                               ! String.Equals(disallowed, scheme, StringComparison.CurrentCultureIgnoreCase));      
             Console.WriteLine();
          }   
       }
    }
    // The example displays the following output:
    //       Culture: English (United States)
    //       FILE:\\\c:\users\user001\documents\FinancialInfo.txt
    //       Access allowed: False
    //       
    //       Culture: Turkish (Turkey)
    //       FILE:\\\c:\users\user001\documents\FinancialInfo.txt
    //       Access allowed: True
    
    open System
    open System.Globalization
    open System.Threading
    
    let disallowed = "file"
    
    let isAccessAllowed (resource: string) =
        let cultures = 
            [| CultureInfo.CreateSpecificCulture "en-US"
               CultureInfo.CreateSpecificCulture "tr-TR" |]
        let index = resource.IndexOfAny [| '\\'; '/' |]
        let scheme =
            if index > 0 then
                resource.Substring(0, index - 1)
            else 
                null
    
        // Change the current culture and perform the comparison.
        for culture in cultures do
            Thread.CurrentThread.CurrentCulture <- culture
            printfn $"Culture: {CultureInfo.CurrentCulture.DisplayName}"
            printfn $"{resource}"
            printfn $"Access allowed: {String.Equals(disallowed, scheme, StringComparison.CurrentCultureIgnoreCase) |> not}"
            printfn ""
            
    isAccessAllowed @"FILE:\\\c:\users\user001\documents\FinancialInfo.txt"
    // The example displays the following output:
    //       Culture: English (United States)
    //       FILE:\\\c:\users\user001\documents\FinancialInfo.txt
    //       Access allowed: False
    //
    //       Culture: Turkish (Turkey)
    //       FILE:\\\c:\users\user001\documents\FinancialInfo.txt
    //       Access allowed: True
    
    Imports System.Globalization
    Imports System.Threading
    
    Module Example2
        Const disallowed = "file"
    
        Public Sub Main()
            IsAccessAllowed("FILE:\\\c:\users\user001\documents\FinancialInfo.txt")
        End Sub
    
        Private Sub IsAccessAllowed(resource As String)
            Dim cultures() As CultureInfo = {CultureInfo.CreateSpecificCulture("en-US"),
                                            CultureInfo.CreateSpecificCulture("tr-TR")}
            Dim scheme As String = Nothing
            Dim index As Integer = resource.IndexOfAny({"\"c, "/"c})
            If index > 0 Then scheme = resource.Substring(0, index - 1)
    
            ' Change the current culture and perform the comparison.
            For Each culture In cultures
                Thread.CurrentThread.CurrentCulture = culture
                Console.WriteLine("Culture: {0}", CultureInfo.CurrentCulture.DisplayName)
                Console.WriteLine(resource)
                Console.WriteLine("Access allowed: {0}",
                               Not String.Equals(disallowed, scheme, StringComparison.CurrentCultureIgnoreCase))
                Console.WriteLine()
            Next
        End Sub
    End Module
    ' The example displays the following output:
    '       Culture: English (United States)
    '       FILE:\\\c:\users\user001\documents\FinancialInfo.txt
    '       Access allowed: False
    '       
    '       Culture: Turkish (Turkey)
    '       FILE:\\\c:\users\user001\documents\FinancialInfo.txt
    '       Access allowed: True
    
  • Różnice w przypadku mapowania między niezmienną kulturą a wszystkimi innymi kulturami. W takich przypadkach użycie reguł wielkości liter niezmiennej kultury w celu zmiany znaku na wielkie lub małe litery zwraca ten sam znak. W przypadku wszystkich innych kultur zwraca inny znak. Niektóre znaki, których dotyczy problem, są wymienione w poniższej tabeli.

    Znak W przypadku zmiany na Powroty
    ZNAK MIKRON (U+00B5) Wielkie litery WIELKA LITERA MU (U+-39C)
    WIELKA LITERA I Z KROPKĄ POWYŻEJ (U+0130) Małe litery MAŁA LITERA I (U+0069)
    MAŁA LITERA KROPKOWA I (U+0131) Wielkie litery WIELKA LITERA I (U+0049)
    MAŁA LITERA S (U+017F) Wielkie litery WIELKA LITERA S (U+0053)
    WIELKA LITERA D Z MAŁĄ LITERĄ Z CARON (U+01C5) Małe litery MAŁA LITERA DZ Z KARONEM (U+01C6)
    POŁĄCZENIE GRECKIEGO YPOGEGRAMMENI (U+0345) Wielkie litery WIELKA LITERA IOTA (U+0399)
  • Różnice w przypadku mapowania dwuliterowych par wielkości liter w zakresie znaków ASCII. W większości kultur dwuliterowa para dwuliterowa jest równa równoważnej dwuliterowej wielkiej litery lub małej parze. Nie dotyczy to następujących dwuliterowych par w następujących kulturach, ponieważ w każdym przypadku są one porównywane z digrafem:

    • "lJ" i "nJ" w kulturze hr-HR (Chorwacki (Chorwacja)).
    • "cH" w kulturach cs-CZ (Czechy)) i sk-SK (Słowacki (Słowacja)).
    • "aA" w kulturze da-DK (Duński (Dania)).
    • "cS", "dZ", "dZS", "nY", "sZ", "tY" i "zS" w kulturze hu-HU (Węgry).
    • "cH" i "lL" w kulturze es-ES_tradnl (Hiszpania, Tradycyjny Sort)).
    • "cH", "gI", "kH", "nG" "nH", "pH", "qU', "tH" i "tR" w kulturze vi-VN (Wietnam (Wietnam)).

    Jednak nietypowe jest napotkanie sytuacji, w której wrażliwe na kulturę porównanie tych par powoduje problemy, ponieważ te pary są nietypowe w stałych ciągach lub identyfikatorach.

Poniższy przykład ilustruje niektóre różnice w regułach wielkości liter między kulturami podczas konwertowania ciągów na wielkie litery.

using System;
using System.Globalization;
using System.IO;

public class Example
{
   public static void Main()
   {
      StreamWriter sw = new StreamWriter(@".\case.txt");   
      string[] words = { "file", "sıfır", "Dženana" };
      CultureInfo[] cultures = { CultureInfo.InvariantCulture, 
                                 new CultureInfo("en-US"),  
                                 new CultureInfo("tr-TR") };

      foreach (var word in words) {
         sw.WriteLine("{0}:", word);
         foreach (var culture in cultures) {
            string name = String.IsNullOrEmpty(culture.Name) ? 
                                 "Invariant" : culture.Name;
            string upperWord = word.ToUpper(culture);
            sw.WriteLine("   {0,10}: {1,7} {2, 38}", name, 
                         upperWord, ShowHexValue(upperWord));
         }
         sw.WriteLine();  
      }
      sw.Close();
   }

   private static string ShowHexValue(string s)
   {
      string retval = null;
      foreach (var ch in s) {
         byte[] bytes = BitConverter.GetBytes(ch);
         retval += String.Format("{0:X2} {1:X2} ", bytes[1], bytes[0]);     
      }
      return retval;
   } 
}
// The example displays the following output:
//    file:
//        Invariant:    FILE               00 46 00 49 00 4C 00 45 
//            en-US:    FILE               00 46 00 49 00 4C 00 45 
//            tr-TR:    FİLE               00 46 01 30 00 4C 00 45 
//    
//    sıfır:
//        Invariant:   SıFıR         00 53 01 31 00 46 01 31 00 52 
//            en-US:   SIFIR         00 53 00 49 00 46 00 49 00 52 
//            tr-TR:   SIFIR         00 53 00 49 00 46 00 49 00 52 
//    
//    Dženana:
//        Invariant:  DžENANA   01 C5 00 45 00 4E 00 41 00 4E 00 41 
//            en-US:  DŽENANA   01 C4 00 45 00 4E 00 41 00 4E 00 41 
//            tr-TR:  DŽENANA   01 C4 00 45 00 4E 00 41 00 4E 00 41
open System
open System.Globalization
open System.IO

let showHexValue (s: string) =
    let mutable retval = ""
    for ch in s do
        let bytes = BitConverter.GetBytes ch
        retval <- retval + String.Format("{0:X2} {1:X2} ", bytes[1], bytes[0])
    retval

do
    use sw = new StreamWriter(@".\case.txt")
    let words = [| "file"; "sıfır"; "Dženana" |]
    let cultures = 
        [| CultureInfo.InvariantCulture 
           CultureInfo "en-US"
           CultureInfo "tr-TR" |]

    for word in words do
        sw.WriteLine("{0}:", word)
        for culture in cultures do
            let name =
                 if String.IsNullOrEmpty culture.Name then "Invariant" else culture.Name
            let upperWord = word.ToUpper culture
            sw.WriteLine("   {0,10}: {1,7} {2, 38}", name, upperWord, showHexValue upperWord)
        sw.WriteLine()
    sw.Close()

// The example displays the following output:
//    file:
//        Invariant:    FILE               00 46 00 49 00 4C 00 45
//            en-US:    FILE               00 46 00 49 00 4C 00 45
//            tr-TR:    FİLE               00 46 01 30 00 4C 00 45
//
//    sıfır:
//        Invariant:   SıFıR         00 53 01 31 00 46 01 31 00 52
//            en-US:   SIFIR         00 53 00 49 00 46 00 49 00 52
//            tr-TR:   SIFIR         00 53 00 49 00 46 00 49 00 52
//
//    Dženana:
//        Invariant:  DžENANA   01 C5 00 45 00 4E 00 41 00 4E 00 41
//            en-US:  DŽENANA   01 C4 00 45 00 4E 00 41 00 4E 00 41
//            tr-TR:  DŽENANA   01 C4 00 45 00 4E 00 41 00 4E 00 41
Imports System.Globalization
Imports System.IO

Module Example1
    Public Sub Main()
        Dim sw As New StreamWriter(".\case.txt")
        Dim words As String() = {"file", "sıfır", "Dženana"}
        Dim cultures() As CultureInfo = {CultureInfo.InvariantCulture,
                                        New CultureInfo("en-US"),
                                        New CultureInfo("tr-TR")}

        For Each word In words
            sw.WriteLine("{0}:", word)
            For Each culture In cultures
                Dim name As String = If(String.IsNullOrEmpty(culture.Name),
                                 "Invariant", culture.Name)
                Dim upperWord As String = word.ToUpper(culture)
                sw.WriteLine("   {0,10}: {1,7} {2, 38}", name,
                         upperWord, ShowHexValue(upperWord))

            Next
            sw.WriteLine()
        Next
        sw.Close()
    End Sub

    Private Function ShowHexValue(s As String) As String
        Dim retval As String = Nothing
        For Each ch In s
            Dim bytes() As Byte = BitConverter.GetBytes(ch)
            retval += String.Format("{0:X2} {1:X2} ", bytes(1), bytes(0))
        Next
        Return retval
    End Function
End Module
' The example displays the following output:
'    file:
'        Invariant:    FILE               00 46 00 49 00 4C 00 45 
'            en-US:    FILE               00 46 00 49 00 4C 00 45 
'            tr-TR:    FİLE               00 46 01 30 00 4C 00 45 
'    
'    sıfır:
'        Invariant:   SıFıR         00 53 01 31 00 46 01 31 00 52 
'            en-US:   SIFIR         00 53 00 49 00 46 00 49 00 52 
'            tr-TR:   SIFIR         00 53 00 49 00 46 00 49 00 52 
'    
'    Dženana:
'        Invariant:  DžENANA   01 C5 00 45 00 4E 00 41 00 4E 00 41 
'            en-US:  DŽENANA   01 C4 00 45 00 4E 00 41 00 4E 00 41 
'            tr-TR:  DŽENANA   01 C4 00 45 00 4E 00 41 00 4E 00 41

Formatowanie i analizowanie

Formatowanie i analizowanie są operacjami odwrotnymi. Reguły formatowania określają sposób konwertowania wartości, takiej jak data i godzina lub liczba, na reprezentację ciągu, podczas gdy reguły analizowania określają, jak przekonwertować reprezentację ciągu na wartość, taką jak data i godzina. Zarówno formatowanie, jak i reguły analizowania są zależne od konwencji kulturowych. Poniższy przykład ilustruje niejednoznaczność, która może wystąpić podczas interpretowania ciągu daty specyficznego dla kultury. Bez znajomości konwencji kultury, która została użyta do utworzenia ciągu daty, nie można wiedzieć, czy 03/01/2011, 3/1/2011 i 01/03/2011 reprezentują 3 stycznia 2011 r. lub 1 marca 2011 r.

using System;
using System.Globalization;

public class Example9
{
   public static void Main()
   {
      DateTime date = new DateTime(2011, 3, 1);
      CultureInfo[] cultures = { CultureInfo.InvariantCulture, 
                                 new CultureInfo("en-US"), 
                                 new CultureInfo("fr-FR") };

      foreach (var culture in cultures)
         Console.WriteLine("{0,-12} {1}", String.IsNullOrEmpty(culture.Name) ?
                           "Invariant" : culture.Name, 
                           date.ToString("d", culture));                                    
   }
}
// The example displays the following output:
//       Invariant    03/01/2011
//       en-US        3/1/2011
//       fr-FR        01/03/2011
open System
open System.Globalization

let date = DateTime(2011, 3, 1)
let cultures = 
      [| CultureInfo.InvariantCulture
         CultureInfo "en-US"
         CultureInfo "fr-FR" |]

for culture in cultures do
    printfn $"""{(if String.IsNullOrEmpty culture.Name then "Invariant" else culture.Name),-12} {date.ToString("d", culture)}"""
// The example displays the following output:
//       Invariant    03/01/2011
//       en-US        3/1/2011
//       fr-FR        01/03/2011
Imports System.Globalization

Module Example8
    Public Sub Main()
        Dim dat As Date = #3/1/2011#
        Dim cultures() As CultureInfo = {CultureInfo.InvariantCulture,
                                        New CultureInfo("en-US"),
                                        New CultureInfo("fr-FR")}

        For Each culture In cultures
            Console.WriteLine("{0,-12} {1}", If(String.IsNullOrEmpty(culture.Name),
                           "Invariant", culture.Name),
                           dat.ToString("d", culture))
        Next
    End Sub
End Module
' The example displays the following output:
'       Invariant    03/01/2011
'       en-US        3/1/2011
'       fr-FR        01/03/2011

Podobnie jak pokazano w poniższym przykładzie, pojedynczy ciąg może generować różne daty w zależności od kultury, której konwencje są używane w operacji analizowania.

using System;
using System.Globalization;

public class Example15
{
   public static void Main()
   {
      string dateString = "07/10/2011";
      CultureInfo[] cultures = { CultureInfo.InvariantCulture, 
                                 CultureInfo.CreateSpecificCulture("en-GB"), 
                                 CultureInfo.CreateSpecificCulture("en-US") };
      Console.WriteLine("{0,-12} {1,10} {2,8} {3,8}\n", "Date String", "Culture", 
                                                 "Month", "Day");
      foreach (var culture in cultures) {
         DateTime date = DateTime.Parse(dateString, culture);
         Console.WriteLine("{0,-12} {1,10} {2,8} {3,8}", dateString, 
                           String.IsNullOrEmpty(culture.Name) ?
                           "Invariant" : culture.Name, 
                           date.Month, date.Day);
      }                      
   }
}
// The example displays the following output:
//       Date String     Culture    Month      Day
//       
//       07/10/2011    Invariant        7       10
//       07/10/2011        en-GB       10        7
//       07/10/2011        en-US        7       10
open System
open System.Globalization

let dateString = "07/10/2011"
let cultures = 
    [| CultureInfo.InvariantCulture
       CultureInfo.CreateSpecificCulture "en-GB"
       CultureInfo.CreateSpecificCulture "en-US" |]
printfn $"""{"Date String",-12} {"Culture",10} {"Month",8} {"Day",8}\n"""
for culture in cultures do
    let date = DateTime.Parse(dateString, culture)
    printfn $"""{dateString,-12} {(if String.IsNullOrEmpty culture.Name then "Invariant" else culture.Name),10} {date.Month,8} {date.Day,8}"""
// The example displays the following output:
//       Date String     Culture    Month      Day
//
//       07/10/2011    Invariant        7       10
//       07/10/2011        en-GB       10        7
//       07/10/2011        en-US        7       10
Imports System.Globalization

Module Example18
    Public Sub Main()
        Dim dateString As String = "07/10/2011"
        Dim cultures() As CultureInfo = {CultureInfo.InvariantCulture,
                                        CultureInfo.CreateSpecificCulture("en-GB"),
                                        CultureInfo.CreateSpecificCulture("en-US")}
        Console.WriteLine("{0,-12} {1,10} {2,8} {3,8}", "Date String", "Culture",
                                                 "Month", "Day")
        Console.WriteLine()
        For Each culture In cultures
            Dim dat As Date = DateTime.Parse(dateString, culture)
            Console.WriteLine("{0,-12} {1,10} {2,8} {3,8}", dateString,
                           If(String.IsNullOrEmpty(culture.Name),
                           "Invariant", culture.Name),
                           dat.Month, dat.Day)
        Next
    End Sub
End Module
' The example displays the following output:
'       Date String     Culture    Month      Day
'       
'       07/10/2011    Invariant        7       10
'       07/10/2011        en-GB       10        7
'       07/10/2011        en-US        7       10

Porównanie ciągów i sortowanie

Konwencje porównywania i sortowania ciągów różnią się od kultury do kultury. Na przykład kolejność sortowania może być oparta na fonetyce lub na wizualnej reprezentacji znaków. W językach wschodnioazjatyckich znaki są sortowane według pociągnięcia i radykalności ideografów. Sortowanie zależy również od języków kolejności i kultur używanych dla alfabetu. Na przykład język duński ma znak "Æ", który sortuje po znaku "Z" w alfabecie. Ponadto porównania mogą być wrażliwe na wielkość liter lub bez uwzględniania wielkości liter, a reguły wielkości liter mogą różnić się od kultury. Z drugiej strony porównanie porządkowe używa punktów kodu Unicode poszczególnych znaków w ciągu podczas porównywania i sortowania ciągów.

Reguły sortowania określają kolejność alfabetyczną znaków Unicode i sposób porównania dwóch ciągów ze sobą. Na przykład String.Compare(String, String, StringComparison) metoda porównuje dwa ciągi na podstawie parametru StringComparison . Jeśli wartość parametru to StringComparison.CurrentCulture, metoda wykonuje porównanie językowe, które używa konwencji bieżącej kultury; jeśli wartość parametru to StringComparison.Ordinal, metoda wykonuje porównanie porządkowe. W związku z tym, jak pokazano w poniższym przykładzie, jeśli bieżąca kultura to U.S. English, pierwsze wywołanie String.Compare(String, String, StringComparison) metody (przy użyciu porównania wrażliwego na kulturę) uwzględnia wartość "a" mniejszą niż "A", ale drugie wywołanie tej samej metody (przy użyciu porównania porządkowego) uznaje wartość "a" większą niż "A".

using System;
using System.Globalization;
using System.Threading;

public class Example2
{
   public static void Main()
   {
      Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture("en-US");
      Console.WriteLine(String.Compare("A", "a", StringComparison.CurrentCulture));
      Console.WriteLine(String.Compare("A", "a", StringComparison.Ordinal));
   }
}
// The example displays the following output:
//       1
//       -32
open System
open System.Globalization
open System.Threading

Thread.CurrentThread.CurrentCulture <- CultureInfo.CreateSpecificCulture "en-US"
printfn $"""{String.Compare("A", "a", StringComparison.CurrentCulture)}"""
printfn $"""{String.Compare("A", "a", StringComparison.Ordinal)}"""
// The example displays the following output:
//       1
//       -32
Imports System.Globalization
Imports System.Threading

Module Example3
    Public Sub Main()
        Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture("en-US")
        Console.WriteLine(String.Compare("A", "a", StringComparison.CurrentCulture))
        Console.WriteLine(String.Compare("A", "a", StringComparison.Ordinal))
    End Sub
End Module
' The example displays the following output:
'       1                                                                                     
'       -32

Platforma .NET obsługuje reguły sortowania wyrazów, ciągów i porządkowych:

  • Sortowanie wyrazów wykonuje porównanie ciągów uwzględniających kulturę, w których niektóre znaki Unicode nieliczbowe mogą mieć przypisane specjalne wagi. Na przykład łącznik (-) może mieć przypisaną bardzo małą wagę, tak aby obok siebie na posortowanej liście pojawiały się "coop" i "co-op". Aby uzyskać listę String metod, które porównują dwa ciągi przy użyciu reguł sortowania wyrazów, zobacz sekcję Operacje ciągów według kategorii .

  • Sortowanie ciągów wykonuje również porównanie uwzględniające kulturę. Jest on podobny do sortowania wyrazów, z tą różnicą, że nie ma specjalnych przypadków, a wszystkie symbole niefanumeryczne pochodzą przed wszystkimi znakami alfanumerycznymi Unicode. Dwa ciągi można porównać przy użyciu reguł sortowania ciągów, wywołując CompareInfo.Compare przeciążenia metody, które mają options parametr, który jest dostarczany wartość CompareOptions.StringSort. Należy pamiętać, że jest to jedyna metoda udostępniana przez platformę .NET w celu porównania dwóch ciągów przy użyciu reguł sortowania ciągów.

  • Sortowanie porządkowe porównuje ciągi na podstawie wartości liczbowej każdego Char obiektu w ciągu. Porównywanie porządkowe jest automatycznie uwzględniane wielkość liter, ponieważ małe i wielkie wersje znaku mają różne punkty kodu. Jeśli jednak wielkość liter nie jest ważna, możesz określić porównanie porządkowe, które ignoruje wielkość liter. Jest to odpowiednik konwertowania ciągu na wielkie litery przy użyciu niezmiennej kultury, a następnie przeprowadzania porównania porządkowego w wyniku. Aby uzyskać listę String metod, które porównują dwa ciągi przy użyciu reguł sortowania porządkowego, zobacz sekcję Operacje ciągów według kategorii .

Porównanie wrażliwe na kulturę to każde porównanie, które jawnie lub niejawnie używa CultureInfo obiektu, w tym niezmiennej kultury określonej przez CultureInfo.InvariantCulture właściwość. Niejawna kultura jest bieżącą kulturą, która jest określana przez Thread.CurrentCulture właściwości i CultureInfo.CurrentCulture . Istnieje znaczna różnica w kolejności sortowania znaków alfabetycznych (czyli znaków, dla których Char.IsLetter właściwość zwraca truewartość ) w różnych kulturach. Można określić porównanie wrażliwe na kulturę, które używa konwencji określonej kultury, dostarczając CultureInfo obiekt do metody porównania ciągów, takiej jak Compare(String, String, CultureInfo, CompareOptions). Można określić porównanie wrażliwe na kulturę, które używa konwencji bieżącej kultury, podając StringComparison.CurrentCulture, StringComparison.CurrentCultureIgnoreCaselub dowolny element członkowski CompareOptions wyliczenia innego niż CompareOptions.Ordinal lub CompareOptions.OrdinalIgnoreCase do odpowiedniego Compare przeciążenia metody. Porównanie wrażliwe na kulturę jest ogólnie odpowiednie do sortowania, podczas gdy porównanie porządkowe nie jest. Porównanie porządkowe jest ogólnie odpowiednie do określania, czy dwa ciągi są równe (czyli do określania tożsamości), podczas gdy porównanie wrażliwe na kulturę nie jest.

W poniższym przykładzie przedstawiono różnicę między porównaniem uwzględniającym kulturę i porządkowym. Przykład ocenia trzy ciągi, "Apple", "Æble" i "AEble", używając porównania porządkowego oraz konwencji kultur da-DK i en-US (z których każda jest kulturą domyślną w czasie Compare wywoływanej metody). Ponieważ język duński traktuje znak "Æ" jako pojedynczą literę i sortuje go po znaku "Z" w alfabecie, ciąg "Æble" jest większy niż "Apple". Jednak "Æble" nie jest uważane za równoważne "AEble", więc "Æble" jest również większy niż "AEble". Kultura en-US nie zawiera litery "Æ", ale traktuje ją jako równoważną "AE", co wyjaśnia, dlaczego "Æble" jest mniejsze niż "Apple", ale równe "AEble". Z drugiej strony porównanie porządkowe uważa, że wartość "Apple" jest mniejsza niż "Æble", a "Æble" jest większa niż "AEble".

using System;
using System.Globalization;
using System.Threading;

public class CompareStringSample
{
   public static void Main()
   {
      string str1 = "Apple";
      string str2 = "Æble"; 
      string str3 = "AEble";
      
      // Set the current culture to Danish in Denmark.
      Thread.CurrentThread.CurrentCulture = new CultureInfo("da-DK");
      Console.WriteLine("Current culture: {0}", 
                        CultureInfo.CurrentCulture.Name);
      Console.WriteLine("Comparison of {0} with {1}: {2}", 
                        str1, str2, String.Compare(str1, str2));
      Console.WriteLine("Comparison of {0} with {1}: {2}\n", 
                        str2, str3, String.Compare(str2, str3));
      
      // Set the current culture to English in the U.S.
      Thread.CurrentThread.CurrentCulture = new CultureInfo("en-US");
      Console.WriteLine("Current culture: {0}", 
                        CultureInfo.CurrentCulture.Name);
      Console.WriteLine("Comparison of {0} with {1}: {2}", 
                        str1, str2, String.Compare(str1, str2));
      Console.WriteLine("Comparison of {0} with {1}: {2}\n", 
                        str2, str3, String.Compare(str2, str3));
      
      // Perform an ordinal comparison.
      Console.WriteLine("Ordinal comparison");
      Console.WriteLine("Comparison of {0} with {1}: {2}", 
                        str1, str2, 
                        String.Compare(str1, str2, StringComparison.Ordinal));
      Console.WriteLine("Comparison of {0} with {1}: {2}", 
                        str2, str3, 
                        String.Compare(str2, str3, StringComparison.Ordinal));
   }
}
// The example displays the following output:
//       Current culture: da-DK
//       Comparison of Apple with Æble: -1
//       Comparison of Æble with AEble: 1
//       
//       Current culture: en-US
//       Comparison of Apple with Æble: 1
//       Comparison of Æble with AEble: 0
//       
//       Ordinal comparison
//       Comparison of Apple with Æble: -133
//       Comparison of Æble with AEble: 133
open System
open System.Globalization
open System.Threading

let str1 = "Apple"
let str2 = "Æble"
let str3 = "AEble"

// Set the current culture to Danish in Denmark.
Thread.CurrentThread.CurrentCulture <- CultureInfo "da-DK"
printfn $"Current culture: {CultureInfo.CurrentCulture.Name}"
printfn $"Comparison of {str1} with {str2}: {String.Compare(str1, str2)}"
printfn $"Comparison of {str2} with {str3}: {String.Compare(str2, str3)}\n"

// Set the current culture to English in the U.S.
Thread.CurrentThread.CurrentCulture <- CultureInfo "en-US"
printfn $"Current culture: {CultureInfo.CurrentCulture.Name}"
printfn $"Comparison of {str1} with {str2}: {String.Compare(str1, str2)}"
printfn $"Comparison of {str2} with {str3}: {String.Compare(str2, str3)}\n"

// Perform an ordinal comparison.
printfn "Ordinal comparison"
printfn $"Comparison of {str1} with {str2}: {String.Compare(str1, str2, StringComparison.Ordinal)}"
printfn $"Comparison of {str2} with {str3}: {String.Compare(str2, str3, StringComparison.Ordinal)}"
// The example displays the following output:
//       Current culture: da-DK
//       Comparison of Apple with Æble: -1
//       Comparison of Æble with AEble: 1
//
//       Current culture: en-US
//       Comparison of Apple with Æble: 1
//       Comparison of Æble with AEble: 0
//
//       Ordinal comparison
//       Comparison of Apple with Æble: -133
//       Comparison of Æble with AEble: 133
Imports System.Globalization
Imports System.Threading

Public Module Example6
    Public Sub Main()
        Dim str1 As String = "Apple"
        Dim str2 As String = "Æble"
        Dim str3 As String = "AEble"

        ' Set the current culture to Danish in Denmark.
        Thread.CurrentThread.CurrentCulture = New CultureInfo("da-DK")
        Console.WriteLine("Current culture: {0}",
                        CultureInfo.CurrentCulture.Name)
        Console.WriteLine("Comparison of {0} with {1}: {2}",
                        str1, str2, String.Compare(str1, str2))
        Console.WriteLine("Comparison of {0} with {1}: {2}",
                        str2, str3, String.Compare(str2, str3))
        Console.WriteLine()

        ' Set the current culture to English in the U.S.
        Thread.CurrentThread.CurrentCulture = New CultureInfo("en-US")
        Console.WriteLine("Current culture: {0}",
                        CultureInfo.CurrentCulture.Name)
        Console.WriteLine("Comparison of {0} with {1}: {2}",
                        str1, str2, String.Compare(str1, str2))
        Console.WriteLine("Comparison of {0} with {1}: {2}",
                        str2, str3, String.Compare(str2, str3))
        Console.WriteLine()

        ' Perform an ordinal comparison.
        Console.WriteLine("Ordinal comparison")
        Console.WriteLine("Comparison of {0} with {1}: {2}",
                        str1, str2,
                        String.Compare(str1, str2, StringComparison.Ordinal))
        Console.WriteLine("Comparison of {0} with {1}: {2}",
                        str2, str3,
                        String.Compare(str2, str3, StringComparison.Ordinal))
    End Sub
End Module
' The example displays the following output:
'       Current culture: da-DK
'       Comparison of Apple with Æble: -1
'       Comparison of Æble with AEble: 1
'       
'       Current culture: en-US
'       Comparison of Apple with Æble: 1
'       Comparison of Æble with AEble: 0
'       
'       Ordinal comparison
'       Comparison of Apple with Æble: -133
'       Comparison of Æble with AEble: 133

Skorzystaj z poniższych ogólnych wskazówek, aby wybrać odpowiednią metodę sortowania lub porównania ciągów:

  • Jeśli chcesz, aby ciągi były uporządkowane na podstawie kultury użytkownika, należy je porządkować na podstawie konwencji bieżącej kultury. Jeśli kultura użytkownika ulegnie zmianie, kolejność posortowanych ciągów również zmieni się odpowiednio. Na przykład aplikacja tezaurusa powinna zawsze sortować wyrazy na podstawie kultury użytkownika.

  • Jeśli chcesz, aby ciągi były uporządkowane na podstawie konwencji określonej kultury, należy je uporządkować, podając CultureInfo obiekt reprezentujący tę kulturę metodzie porównania. Na przykład w aplikacji przeznaczonej do nauczania uczniów określonego języka, chcesz, aby ciągi zostały uporządkowane na podstawie konwencji jednej z kultur, które mówią o tym języku.

  • Jeśli chcesz, aby kolejność ciągów pozostała bez zmian w różnych kulturach, należy uporządkować je na podstawie konwencji niezmiennej kultury lub użyć porównania porządkowego. Na przykład można użyć sortowania porządkowego do organizowania nazw plików, procesów, mutexes lub nazwanych potoków.

  • W przypadku porównania, które obejmuje decyzję dotyczącą zabezpieczeń (na przykład, czy nazwa użytkownika jest prawidłowa), zawsze należy wykonać test porządkowy pod kątem równości, wywołując przeciążenie Equals metody.

Uwaga

Reguły sortowania i wielkości liter z uwzględnieniem kultury używane w porównywaniu ciągów zależą od wersji platformy .NET. Na platformie .NET Core porównanie ciągów zależy od wersji standardu Unicode obsługiwanej przez bazowy system operacyjny. W programie .NET Framework 4.5 lub nowszym w systemie Windows 8 lub nowszym informacje o znakach sortowania, wielkości liter, normalizacji i Unicode są zgodne ze standardem Unicode 6.0. W innych systemach operacyjnych Windows są one zgodne ze standardem Unicode 5.0.

Aby uzyskać więcej informacji na temat reguł sortowania wyrazów, ciągów i porządkowych, zobacz System.Globalization.CompareOptions temat. Aby uzyskać dodatkowe zalecenia dotyczące sposobu używania każdej reguły, zobacz Najlepsze rozwiązania dotyczące używania ciągów.

Zwykle nie wywołujesz metod porównywania ciągów, takich jak Compare bezpośrednio, aby określić kolejność sortowania ciągów. Zamiast tego metody porównania są wywoływane przez metody sortowania, takie jak Array.Sort lub List<T>.Sort. Poniższy przykład wykonuje cztery różne operacje sortowania (sortowanie wyrazów przy użyciu bieżącej kultury, sortowania wyrazów przy użyciu niezmiennej kultury, sortowania porządkowego i sortowania ciągów przy użyciu niezmiennej kultury) bez jawnego wywoływania metody porównania ciągów, chociaż określają typ porównania do użycia. Należy pamiętać, że każdy typ sortowania generuje unikatową kolejność ciągów w tablicy.

using System;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
 
public class Example3
{
   public static void Main()
   {
      string[] strings = { "coop", "co-op", "cooperative", 
                           "co\u00ADoperative", "cœur", "coeur" };

      // Perform a word sort using the current (en-US) culture.
      string[] current = new string[strings.Length]; 
      strings.CopyTo(current, 0); 
      Array.Sort(current, StringComparer.CurrentCulture);

      // Perform a word sort using the invariant culture.
      string[] invariant = new string[strings.Length];
      strings.CopyTo(invariant, 0); 
      Array.Sort(invariant, StringComparer.InvariantCulture);

      // Perform an ordinal sort.
      string[] ordinal = new string[strings.Length];
      strings.CopyTo(ordinal, 0); 
      Array.Sort(ordinal, StringComparer.Ordinal);

      // Perform a string sort using the current culture.
      string[] stringSort = new string[strings.Length];
      strings.CopyTo(stringSort, 0); 
      Array.Sort(stringSort, new SCompare());

      // Display array values
      Console.WriteLine("{0,13} {1,13} {2,15} {3,13} {4,13}\n", 
                        "Original", "Word Sort", "Invariant Word", 
                        "Ordinal Sort", "String Sort");
      for (int ctr = 0; ctr < strings.Length; ctr++)
         Console.WriteLine("{0,13} {1,13} {2,15} {3,13} {4,13}", 
                           strings[ctr], current[ctr], invariant[ctr], 
                           ordinal[ctr], stringSort[ctr] );          
   }
}

// IComparer<String> implementation to perform string sort.
internal class SCompare : IComparer<String>
{
   public int Compare(string x, string y)
   {
      return CultureInfo.CurrentCulture.CompareInfo.Compare(x, y, CompareOptions.StringSort);
   }
}
// The example displays the following output:
//         Original     Word Sort  Invariant Word  Ordinal Sort   String Sort
//    
//             coop          cœur            cœur         co-op         co-op
//            co-op         coeur           coeur         coeur          cœur
//      cooperative          coop            coop          coop         coeur
//     co­operative         co-op           co-op   cooperative          coop
//             cœur   cooperative     cooperative  co­operative   cooperative
//            coeur  co­operative    co­operative          cœur  co­operative
open System
open System.Collections.Generic
open System.Globalization

// IComparer<String> implementation to perform string sort using an F# object expression.
let scompare = 
    { new IComparer<String> with
        member _.Compare(x, y) =
            CultureInfo.CurrentCulture.CompareInfo.Compare(x, y, CompareOptions.StringSort) }

let strings = [| "coop"; "co-op"; "cooperative"; "co\u00ADoperative"; "cœur"; "coeur" |]

// Perform a word sort using the current (en-US) culture.
let current = Array.copy strings
Array.Sort(current, StringComparer.CurrentCulture)

// Perform a word sort using the invariant culture.
let invariant = Array.copy strings
Array.Sort(invariant, StringComparer.InvariantCulture)

// Perform an ordinal sort.
let ordinal = Array.copy strings
Array.Sort(ordinal, StringComparer.Ordinal)

// Perform a string sort using the current culture.
let stringSort = Array.copy strings
Array.Sort(stringSort, scompare)

// Display array values
printfn "%13s %13s %15s %13s %13s\n" "Original" "Word Sort" "Invariant Word" "Ordinal Sort" "String Sort"
for i = 0 to strings.Length - 1 do
    printfn "%13s %13s %15s %13s %13s\n" strings[i] current[i] invariant[i] ordinal[i] stringSort[i]

// The example displays the following output:
//         Original     Word Sort  Invariant Word  Ordinal Sort   String Sort
//
//             coop          cœur            cœur         co-op         co-op
//            co-op         coeur           coeur         coeur          cœur
//      cooperative          coop            coop          coop         coeur
//     co­operative         co-op           co-op   cooperative          coop
//             cœur   cooperative     cooperative  co­operative   cooperative
//            coeur  co­operative    co­operative          cœur  co­operative
Imports System.Collections
Imports System.Collections.Generic
Imports System.Globalization

Module Example4
    Public Sub Main()
        Dim strings() As String = {"coop", "co-op", "cooperative",
                                  "co" + ChrW(&HAD) + "operative",
                                  "cœur", "coeur"}

        ' Perform a word sort using the current (en-US) culture.
        Dim current(strings.Length - 1) As String
        strings.CopyTo(current, 0)
        Array.Sort(current, StringComparer.CurrentCulture)

        ' Perform a word sort using the invariant culture.
        Dim invariant(strings.Length - 1) As String
        strings.CopyTo(invariant, 0)
        Array.Sort(invariant, StringComparer.InvariantCulture)

        ' Perform an ordinal sort.
        Dim ordinal(strings.Length - 1) As String
        strings.CopyTo(ordinal, 0)
        Array.Sort(ordinal, StringComparer.Ordinal)

        ' Perform a string sort using the current culture.
        Dim stringSort(strings.Length - 1) As String
        strings.CopyTo(stringSort, 0)
        Array.Sort(stringSort, New SCompare())

        ' Display array values
        Console.WriteLine("{0,13} {1,13} {2,15} {3,13} {4,13}",
                        "Original", "Word Sort", "Invariant Word",
                        "Ordinal Sort", "String Sort")
        Console.WriteLine()

        For ctr As Integer = 0 To strings.Length - 1
            Console.WriteLine("{0,13} {1,13} {2,15} {3,13} {4,13}",
                           strings(ctr), current(ctr), invariant(ctr),
                           ordinal(ctr), stringSort(ctr))
        Next
    End Sub
End Module

' IComparer<String> implementation to perform string sort.
Friend Class SCompare : Implements IComparer(Of String)
   Public Function Compare(x As String, y As String) As Integer _
                   Implements IComparer(Of String).Compare
      Return CultureInfo.CurrentCulture.CompareInfo.Compare(x, y, CompareOptions.StringSort)
   End Function
End Class
' The example displays the following output:
'         Original     Word Sort  Invariant Word  Ordinal Sort   String Sort
'    
'             coop          cœur            cœur         co-op         co-op
'            co-op         coeur           coeur         coeur          cœur
'      cooperative          coop            coop          coop         coeur
'     co­operative         co-op           co-op   cooperative          coop
'             cœur   cooperative     cooperative  co­operative   cooperative
'            coeur  co­operative    co­operative          cœur  co­operative

Napiwek

Wewnętrznie platforma .NET używa kluczy sortowania do obsługi porównywania ciągów wrażliwych kulturowo. Każdy znak w ciągu ma kilka kategorii wag sortowania, w tym alfabetyczne, wielkość liter i znaki diakrytyczne. Klucz sortowania reprezentowany przez SortKey klasę zawiera repozytorium tych wag dla określonego ciągu. Jeśli aplikacja wykonuje dużą liczbę operacji wyszukiwania lub sortowania na tym samym zestawie ciągów, możesz zwiększyć jej wydajność, generując i przechowując klucze sortowania dla wszystkich używanych ciągów. Gdy wymagana jest operacja sortowania lub porównania, należy użyć kluczy sortowania zamiast ciągów. Aby uzyskać więcej informacji, zobacz klasę SortKey .

Jeśli nie określisz konwencji porównania ciągów, metody sortowania, takie jak Array.Sort(Array) sortowanie uwzględniające kulturę, sortowanie uwzględniające wielkość liter w ciągach. W poniższym przykładzie pokazano, jak zmiana bieżącej kultury wpływa na kolejność posortowanych ciągów w tablicy. Tworzy tablicę trzech ciągów. Najpierw ustawia System.Threading.Thread.CurrentThread.CurrentCulture właściwość na en-US i wywołuje metodę Array.Sort(Array) . Wynikowa kolejność sortowania jest oparta na konwencji sortowania dla kultury języka angielskiego (Stany Zjednoczone). Następnie przykład ustawia System.Threading.Thread.CurrentThread.CurrentCulture właściwość na da-DK i wywołuje metodę Array.Sort ponownie. Zwróć uwagę, że wynikowa kolejność sortowania różni się od wyników en-US, ponieważ używa konwencji sortowania dla języka duńskiego (Dania).

using System;
using System.Globalization;
using System.Threading;

public class ArraySort
{
   public static void Main(String[] args)
   {
      // Create and initialize a new array to store the strings.
      string[] stringArray = { "Apple", "Æble", "Zebra"};

      // Display the values of the array.
      Console.WriteLine( "The original string array:");
      PrintIndexAndValues(stringArray);

      // Set the CurrentCulture to "en-US".
      Thread.CurrentThread.CurrentCulture = new CultureInfo("en-US");
      // Sort the values of the array.
      Array.Sort(stringArray);

      // Display the values of the array.
      Console.WriteLine("After sorting for the culture \"en-US\":");
      PrintIndexAndValues(stringArray);

      // Set the CurrentCulture to "da-DK".
      Thread.CurrentThread.CurrentCulture = new CultureInfo("da-DK");
      // Sort the values of the Array.
      Array.Sort(stringArray);

      // Display the values of the array.
      Console.WriteLine("After sorting for the culture \"da-DK\":");
      PrintIndexAndValues(stringArray);
   }
   public static void PrintIndexAndValues(string[] myArray)
   {
      for (int i = myArray.GetLowerBound(0); i <=
            myArray.GetUpperBound(0); i++ )
         Console.WriteLine("[{0}]: {1}", i, myArray[i]);
      Console.WriteLine();
   }
}
// The example displays the following output:
//       The original string array:
//       [0]: Apple
//       [1]: Æble
//       [2]: Zebra
//
//       After sorting for the "en-US" culture:
//       [0]: Æble
//       [1]: Apple
//       [2]: Zebra
//
//       After sorting for the culture "da-DK":
//       [0]: Apple
//       [1]: Zebra
//       [2]: Æble
open System
open System.Globalization
open System.Threading

let printIndexAndValues (myArray: string[]) =
    for i = myArray.GetLowerBound 0 to myArray.GetUpperBound 0 do
        printfn $"[{i}]: {myArray[i]}" 
    printfn ""

// Create and initialize a new array to store the strings.
let stringArray = [| "Apple"; "Æble"; "Zebra" |]

// Display the values of the array.
printfn "The original string array:"
printIndexAndValues stringArray

// Set the CurrentCulture to "en-US".
Thread.CurrentThread.CurrentCulture <- CultureInfo "en-US"
// Sort the values of the array.
Array.Sort stringArray

// Display the values of the array.
printfn "After sorting for the culture \"en-US\":"
printIndexAndValues stringArray

// Set the CurrentCulture to "da-DK".
Thread.CurrentThread.CurrentCulture <- CultureInfo "da-DK"
// Sort the values of the Array.
Array.Sort stringArray

// Display the values of the array.
printfn "After sorting for the culture \"da-DK\":"
printIndexAndValues stringArray
// The example displays the following output:
//       The original string array:
//       [0]: Apple
//       [1]: Æble
//       [2]: Zebra
//
//       After sorting for the "en-US" culture:
//       [0]: Æble
//       [1]: Apple
//       [2]: Zebra
//
//       After sorting for the culture "da-DK":
//       [0]: Apple
//       [1]: Zebra
//       [2]: Æble
Imports System.Globalization
Imports System.IO
Imports System.Threading

Public Class TextToFile   
   Public Shared Sub Main()
      ' Creates and initializes a new array to store 
      ' these date/time objects.
      Dim stringArray() As String = { "Apple", "Æble", "Zebra"}
      
      ' Displays the values of the array.
      Console.WriteLine("The original string array:")
      PrintIndexAndValues(stringArray)
      
      ' Set the CurrentCulture to "en-US".
      Thread.CurrentThread.CurrentCulture = New CultureInfo("en-US")
      ' Sort the values of the Array.
      Array.Sort(stringArray)
      
      ' Display the values of the array.
      Console.WriteLine("After sorting for the ""en-US"" culture:")
      PrintIndexAndValues(stringArray)
      
      ' Set the CurrentCulture to "da-DK".
      Thread.CurrentThread.CurrentCulture = New CultureInfo("da-DK")
      ' Sort the values of the Array.
      Array.Sort(stringArray)
      
      ' Displays the values of the Array.
      Console.WriteLine("After sorting for the culture ""da-DK"":")
      PrintIndexAndValues(stringArray)
   End Sub

   Public Shared Sub PrintIndexAndValues(myArray() As String)
      For i As Integer = myArray.GetLowerBound(0) To myArray.GetUpperBound(0)
         Console.WriteLine("[{0}]: {1}", i, myArray(i))
      Next
      Console.WriteLine()
   End Sub 
End Class
' The example displays the following output:
'       The original string array:
'       [0]: Apple
'       [1]: Æble
'       [2]: Zebra
'       
'       After sorting for the "en-US" culture:
'       [0]: Æble
'       [1]: Apple
'       [2]: Zebra
'       
'       After sorting for the culture "da-DK":
'       [0]: Apple
'       [1]: Zebra
'       [2]: Æble

Ostrzeżenie

Jeśli podstawowym celem porównywania ciągów jest określenie, czy są one równe, należy wywołać metodę String.Equals . Zazwyczaj należy użyć Equals polecenia , aby wykonać porównanie porządkowe. Metoda jest przeznaczona String.Compare głównie do sortowania ciągów.

Metody wyszukiwania ciągów, takie jak String.StartsWith i String.IndexOf, mogą również wykonywać porównania ciągów porządkowych lub wrażliwych na kulturę. W poniższym przykładzie przedstawiono różnice między porównaniami porządkowymi i uwzględniającymi kulturę IndexOf przy użyciu metody . Wyszukiwanie wrażliwe na kulturę, w którym bieżąca kultura jest angielska (Stany Zjednoczone) uważa podciąg "oe", aby dopasować ligaturę "ś". Ponieważ łącznik miękki (U+00AD) jest znakiem o zerowej szerokości, wyszukiwanie traktuje łącznik miękki jako odpowiednik String.Empty i znajduje dopasowanie na początku ciągu. Z drugiej strony wyszukiwanie porządkowe nie znajduje dopasowania w obu przypadkach.

using System;

public class Example8
{
   public static void Main()
   {
      // Search for "oe" and "œu" in "œufs" and "oeufs".
      string s1 = "œufs";
      string s2 = "oeufs";
      FindInString(s1, "oe", StringComparison.CurrentCulture);
      FindInString(s1, "oe", StringComparison.Ordinal);
      FindInString(s2, "œu", StringComparison.CurrentCulture);
      FindInString(s2, "œu", StringComparison.Ordinal);
      Console.WriteLine();
      
      string s3 = "co\u00ADoperative";
      FindInString(s3, "\u00AD", StringComparison.CurrentCulture);
      FindInString(s3, "\u00AD", StringComparison.Ordinal);
   }

   private static void FindInString(string s, string substring, StringComparison options)
   {
      int result = s.IndexOf(substring, options);
      if (result != -1)
         Console.WriteLine("'{0}' found in {1} at position {2}", 
                           substring, s, result);
      else
         Console.WriteLine("'{0}' not found in {1}", 
                           substring, s);                                                  
   }
}
// The example displays the following output:
//       'oe' found in œufs at position 0
//       'oe' not found in œufs
//       'œu' found in oeufs at position 0
//       'œu' not found in oeufs
//       
//       '­' found in co­operative at position 0
//       '­' found in co­operative at position 2
open System

let findInString (s: string) (substring: string) (options: StringComparison) =
    let result = s.IndexOf(substring, options)
    if result <> -1 then
        printfn $"'{substring}' found in {s} at position {result}"
    else
        printfn $"'{substring}' not found in {s}"

// Search for "oe" and "œu" in "œufs" and "oeufs".
let s1 = "œufs"
let s2 = "oeufs"
findInString s1 "oe" StringComparison.CurrentCulture
findInString s1 "oe" StringComparison.Ordinal
findInString s2 "œu" StringComparison.CurrentCulture
findInString s2 "œu" StringComparison.Ordinal
printfn ""

let s3 = "co\u00ADoperative"
findInString s3 "\u00AD" StringComparison.CurrentCulture
findInString s3 "\u00AD" StringComparison.Ordinal

// The example displays the following output:
//       'oe' found in œufs at position 0
//       'oe' not found in œufs
//       'œu' found in oeufs at position 0
//       'œu' not found in oeufs
//
//       '­' found in co­operative at position 0
//       '­' found in co­operative at position 2
Module Example5
    Public Sub Main()
        ' Search for "oe" and "œu" in "œufs" and "oeufs".
        Dim s1 As String = "œufs"
        Dim s2 As String = "oeufs"
        FindInString(s1, "oe", StringComparison.CurrentCulture)
        FindInString(s1, "oe", StringComparison.Ordinal)
        FindInString(s2, "œu", StringComparison.CurrentCulture)
        FindInString(s2, "œu", StringComparison.Ordinal)
        Console.WriteLine()

        Dim softHyphen As String = ChrW(&HAD)
        Dim s3 As String = "co" + softHyphen + "operative"
        FindInString(s3, softHyphen, StringComparison.CurrentCulture)
        FindInString(s3, softHyphen, StringComparison.Ordinal)
    End Sub

    Private Sub FindInString(s As String, substring As String,
                            options As StringComparison)
        Dim result As Integer = s.IndexOf(substring, options)
        If result <> -1 Then
            Console.WriteLine("'{0}' found in {1} at position {2}",
                           substring, s, result)
        Else
            Console.WriteLine("'{0}' not found in {1}",
                           substring, s)
        End If
    End Sub
End Module
' The example displays the following output:
'       'oe' found in œufs at position 0
'       'oe' not found in œufs
'       'œu' found in oeufs at position 0
'       'œu' not found in oeufs
'       
'       '­' found in co­operative at position 0
'       '­' found in co­operative at position 2

Wyszukiwanie w ciągach

Metody wyszukiwania ciągów, takie jak String.StartsWith i String.IndexOf, mogą również wykonywać porównania ciągów porządkowych lub wrażliwych na kulturę, aby określić, czy znak lub podciąg znajduje się w określonym ciągu.

Metody wyszukiwania w String klasie, które wyszukują pojedynczy znak, taki jak metoda lub jeden z zestawów znaków, takich jak IndexOfIndexOfAny metoda, wszystkie wykonują wyszukiwanie porządkowe. Aby wyszukać znak uwzględniający kulturę, należy wywołać metodę CompareInfo , taką jak CompareInfo.IndexOf(String, Char) lub CompareInfo.LastIndexOf(String, Char). Należy pamiętać, że wyniki wyszukiwania znaku przy użyciu porównania porządkowego i wrażliwego na kulturę mogą być bardzo różne. Na przykład wyszukiwanie wstępnie skompilowanego znaku Unicode, takiego jak ligatura "Æ" (U+00C6) może odpowiadać dowolnemu wystąpieniu jego składników w prawidłowej sekwencji, takim jak "AE" (U+041U+0045), w zależności od kultury. Poniższy przykład ilustruje różnicę między metodami String.IndexOf(Char) i CompareInfo.IndexOf(String, Char) podczas wyszukiwania pojedynczego znaku. Ligature "æ" (U+00E6) znajduje się w ciągu "aerial" podczas korzystania z konwencji kultury en-US, ale nie w przypadku używania konwencji kultury da-DK lub podczas wykonywania porównania porządkowego.

using System;
using System.Globalization;

public class Example17
{
   public static void Main()
   {
      String[] cultureNames = { "da-DK", "en-US" };
      CompareInfo ci;
      String str = "aerial";
      Char ch = 'æ';  // U+00E6
      
      Console.Write("Ordinal comparison -- ");
      Console.WriteLine("Position of '{0}' in {1}: {2}", ch, str,
                        str.IndexOf(ch));
      
      foreach (var cultureName in cultureNames) {
         ci = CultureInfo.CreateSpecificCulture(cultureName).CompareInfo;
         Console.Write("{0} cultural comparison -- ", cultureName);
         Console.WriteLine("Position of '{0}' in {1}: {2}", ch, str,
                           ci.IndexOf(str, ch));
      }
   }
}
// The example displays the following output:
//       Ordinal comparison -- Position of 'æ' in aerial: -1
//       da-DK cultural comparison -- Position of 'æ' in aerial: -1
//       en-US cultural comparison -- Position of 'æ' in aerial: 0
open System.Globalization

let cultureNames = [| "da-DK"; "en-US" |]
let str = "aerial"
let ch = 'æ'  // U+00E6

printf "Ordinal comparison -- "
printfn $"Position of '{ch}' in {str}: {str.IndexOf ch}"
                  
for cultureName in cultureNames do
    let ci = CultureInfo.CreateSpecificCulture(cultureName).CompareInfo
    printf $"{cultureName} cultural comparison -- "
    printfn $"Position of '{ch}' in {str}: {ci.IndexOf(str, ch)}"
// The example displays the following output:
//       Ordinal comparison -- Position of 'æ' in aerial: -1
//       da-DK cultural comparison -- Position of 'æ' in aerial: -1
//       en-US cultural comparison -- Position of 'æ' in aerial: 0
Imports System.Globalization

Module Example19
    Public Sub Main()
        Dim cultureNames() As String = {"da-DK", "en-US"}
        Dim ci As CompareInfo
        Dim str As String = "aerial"
        Dim ch As Char = "æ"c  ' U+00E6

        Console.Write("Ordinal comparison -- ")
        Console.WriteLine("Position of '{0}' in {1}: {2}", ch, str,
                        str.IndexOf(ch))

        For Each cultureName In cultureNames
            ci = CultureInfo.CreateSpecificCulture(cultureName).CompareInfo
            Console.Write("{0} cultural comparison -- ", cultureName)
            Console.WriteLine("Position of '{0}' in {1}: {2}", ch, str,
                           ci.IndexOf(str, ch))
        Next
    End Sub
End Module
' The example displays the following output:
'       Ordinal comparison -- Position of 'æ' in aerial: -1
'       da-DK cultural comparison -- Position of 'æ' in aerial: -1
'       en-US cultural comparison -- Position of 'æ' in aerial: 0

Z drugiej strony metody klasy, które wyszukują ciąg, a nie znak wykonują wyszukiwanie wrażliwe na kulturę, String jeśli opcje wyszukiwania nie są jawnie określone przez parametr typu StringComparison. Jedynym wyjątkiem jest Contains, który wykonuje wyszukiwanie porządkowe.

Testowanie pod kątem równości

String.Compare Użyj metody , aby określić relację dwóch ciągów w kolejności sortowania. Zazwyczaj jest to operacja wrażliwa na kulturę. Z kolei wywołaj metodę String.Equals , aby przetestować równość. Ponieważ test równości zwykle porównuje dane wejściowe użytkownika z niektórymi znanymi ciągami, takimi jak prawidłowa nazwa użytkownika, hasło lub ścieżka systemu plików, zazwyczaj jest to operacja porządkowa.

Ostrzeżenie

Istnieje możliwość przetestowania równości przez wywołanie String.Compare metody i określenie, czy wartość zwracana jest zero. Jednak ta praktyka nie jest zalecana. Aby określić, czy dwa ciągi są równe, należy wywołać jedno z przeciążeń String.Equals metody. Preferowanym przeciążeniem do wywołania jest metoda wystąpienia Equals(String, StringComparison) lub metoda statyczna Equals(String, String, StringComparison) , ponieważ obie metody zawierają System.StringComparison parametr, który jawnie określa typ porównania.

Poniższy przykład ilustruje niebezpieczeństwo wykonania porównania wrażliwego na kulturę pod kątem równości, gdy zamiast tego należy użyć porządkowego. W takim przypadku celem kodu jest zakazanie dostępu do systemu plików z adresów URL rozpoczynających się od "FILE://" lub "file://", wykonując porównanie bez uwzględniania wielkości liter na początku adresu URL z ciągiem "FILE://". Jeśli jednak porównanie uwzględniające kulturę jest wykonywane przy użyciu kultury tureckiej (Turcji) na adresie URL rozpoczynającym się od "file://", porównanie równości nie powiedzie się, ponieważ tureckie wielkie litery "i" to "İ" zamiast "I". W związku z tym dostęp do systemu plików jest przypadkowo dozwolony. Z drugiej strony, jeśli wykonywane jest porównanie porządkowe, porównanie równości powiedzie się, a dostęp do systemu plików zostanie odrzucony.

using System;
using System.Globalization;
using System.Threading;

public class Example4
{
   public static void Main()
   {
      Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture("tr-TR");      

      string filePath = "file://c:/notes.txt";
      
      Console.WriteLine("Culture-sensitive test for equality:");
      if (! TestForEquality(filePath, StringComparison.CurrentCultureIgnoreCase))
         Console.WriteLine("Access to {0} is allowed.", filePath);
      else
         Console.WriteLine("Access to {0} is not allowed.", filePath);
      
      Console.WriteLine("\nOrdinal test for equality:");
      if (! TestForEquality(filePath, StringComparison.OrdinalIgnoreCase))
         Console.WriteLine("Access to {0} is allowed.", filePath);
      else
         Console.WriteLine("Access to {0} is not allowed.", filePath);
   }

   private static bool TestForEquality(string str, StringComparison cmp)
   {
      int position = str.IndexOf("://");
      if (position < 0) return false;

      string substring = str.Substring(0, position);  
      return substring.Equals("FILE", cmp);
   }
}
// The example displays the following output:
//       Culture-sensitive test for equality:
//       Access to file://c:/notes.txt is allowed.
//       
//       Ordinal test for equality:
//       Access to file://c:/notes.txt is not allowed.
open System
open System.Globalization
open System.Threading

let testForEquality (str: string) (cmp: StringComparison) =
    let position = str.IndexOf "://"
    if position < 0 then false
    else
        let substring = str.Substring(0, position)
        substring.Equals("FILE", cmp)

Thread.CurrentThread.CurrentCulture <- CultureInfo.CreateSpecificCulture "tr-TR"

let filePath = "file://c:/notes.txt"

printfn "Culture-sensitive test for equality:"
if not (testForEquality filePath StringComparison.CurrentCultureIgnoreCase) then
    printfn $"Access to {filePath} is allowed."
else
    printfn $"Access to {filePath} is not allowed."

printfn "\nOrdinal test for equality:"
if not (testForEquality filePath StringComparison.OrdinalIgnoreCase) then
    printfn $"Access to {filePath} is allowed."
else
    printfn $"Access to {filePath} is not allowed."

// The example displays the following output:
//       Culture-sensitive test for equality:
//       Access to file://c:/notes.txt is allowed.
//
//       Ordinal test for equality:
//       Access to file://c:/notes.txt is not allowed.
Imports System.Globalization
Imports System.Threading

Module Example7
    Public Sub Main()
        Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture("tr-TR")

        Dim filePath As String = "file://c:/notes.txt"

        Console.WriteLine("Culture-sensitive test for equality:")
        If Not TestForEquality(filePath, StringComparison.CurrentCultureIgnoreCase) Then
            Console.WriteLine("Access to {0} is allowed.", filePath)
        Else
            Console.WriteLine("Access to {0} is not allowed.", filePath)
        End If
        Console.WriteLine()

        Console.WriteLine("Ordinal test for equality:")
        If Not TestForEquality(filePath, StringComparison.OrdinalIgnoreCase) Then
            Console.WriteLine("Access to {0} is allowed.", filePath)
        Else
            Console.WriteLine("Access to {0} is not allowed.", filePath)
        End If
    End Sub

    Private Function TestForEquality(str As String, cmp As StringComparison) As Boolean
        Dim position As Integer = str.IndexOf("://")
        If position < 0 Then Return False

        Dim substring As String = str.Substring(0, position)
        Return substring.Equals("FILE", cmp)
    End Function
End Module
' The example displays the following output:
'       Culture-sensitive test for equality:
'       Access to file://c:/notes.txt is allowed.
'       
'       Ordinal test for equality:
'       Access to file://c:/notes.txt is not allowed.

Normalizacja

Niektóre znaki Unicode mają wiele reprezentacji. Na przykład dowolny z następujących punktów kodu może reprezentować literę "ắ":

  • U+1EAF
  • U+0103 U+0301
  • U+0061 U+0306 U+0301

Wiele reprezentacji pojedynczego znaku komplikuje wyszukiwanie, sortowanie, dopasowywanie i inne operacje ciągów.

Standard Unicode definiuje proces nazywany normalizacją, który zwraca jedną binarną reprezentację znaku Unicode dla dowolnej z jego równoważnych reprezentacji binarnych. Normalizacja może używać kilku algorytmów nazywanych formularzami normalizacji, które są zgodne z różnymi regułami. Platforma .NET obsługuje formularze normalizacji Unicode C, D, KC i KD. Gdy ciągi zostały znormalizowane do tego samego formularza normalizacji, można je porównać przy użyciu porównania porządkowego.

Porównanie porządkowe to binarne porównanie wartości skalarnej Unicode odpowiadających Char im obiektów w każdym ciągu. Klasa String zawiera szereg metod, które mogą wykonać porównanie porządkowe, w tym następujące:

Można określić, czy ciąg jest znormalizowany do normalizacji formularza C przez wywołanie String.IsNormalized() metody, lub wywołać String.IsNormalized(NormalizationForm) metodę, aby określić, czy ciąg jest znormalizowany do określonej formy normalizacji. Można również wywołać metodę String.Normalize() , aby przekonwertować ciąg na postać normalizacji C, lub wywołać String.Normalize(NormalizationForm) metodę w celu przekonwertowania ciągu na określoną formę normalizacji. Aby uzyskać szczegółowe informacje na temat normalizacji i porównywania ciągów, zobacz Normalize() metody i Normalize(NormalizationForm) .

Poniższy prosty przykład ilustruje normalizację ciągów. Definiuje literę "ố" na trzy różne sposoby w trzech różnych ciągach i używa porównania porządkowego równości, aby określić, że każdy ciąg różni się od pozostałych dwóch ciągów. Następnie konwertuje każdy ciąg na obsługiwane formularze normalizacji, a następnie ponownie wykonuje porządkowe porównanie każdego ciągu w określonej postaci normalizacji. W każdym przypadku drugi test równości pokazuje, że ciągi są równe.

using System;
using System.Globalization;
using System.IO;
using System.Text;

public class Example13
{
   private static StreamWriter sw;
   
   public static void Main()
   {
      sw = new StreamWriter(@".\TestNorm1.txt");

      // Define three versions of the same word. 
      string s1 = "sống";        // create word with U+1ED1
      string s2 = "s\u00F4\u0301ng";
      string s3 = "so\u0302\u0301ng";

      TestForEquality(s1, s2, s3);      
      sw.WriteLine();

      // Normalize and compare strings using each normalization form.
      foreach (string formName in Enum.GetNames(typeof(NormalizationForm)))
      {
         sw.WriteLine("Normalization {0}:\n", formName); 
         NormalizationForm nf = (NormalizationForm) Enum.Parse(typeof(NormalizationForm), formName);
         string[] sn = NormalizeStrings(nf, s1, s2, s3);
         TestForEquality(sn);           
         sw.WriteLine("\n");                                        
      }
      
      sw.Close();   
   }

   private static void TestForEquality(params string[] words)
   {
      for (int ctr = 0; ctr <= words.Length - 2; ctr++)
         for (int ctr2 = ctr + 1; ctr2 <= words.Length - 1; ctr2++) 
            sw.WriteLine("{0} ({1}) = {2} ({3}): {4}", 
                         words[ctr], ShowBytes(words[ctr]),
                         words[ctr2], ShowBytes(words[ctr2]),
                         words[ctr].Equals(words[ctr2], StringComparison.Ordinal));
   }

   private static string ShowBytes(string str)
   {
      string result = null;
      foreach (var ch in str)
         result += $"{(ushort)ch:X4} ";
      return result.Trim();            
   } 
   
   private static string[] NormalizeStrings(NormalizationForm nf, params string[] words)
   {
      for (int ctr = 0; ctr < words.Length; ctr++)
         if (! words[ctr].IsNormalized(nf))
            words[ctr] = words[ctr].Normalize(nf); 
      return words;   
   }
}
// The example displays the following output:
//       sống (0073 1ED1 006E 0067) = sống (0073 00F4 0301 006E 0067): False
//       sống (0073 1ED1 006E 0067) = sống (0073 006F 0302 0301 006E 0067): False
//       sống (0073 00F4 0301 006E 0067) = sống (0073 006F 0302 0301 006E 0067): False
//       
//       Normalization FormC:
//       
//       sống (0073 1ED1 006E 0067) = sống (0073 1ED1 006E 0067): True
//       sống (0073 1ED1 006E 0067) = sống (0073 1ED1 006E 0067): True
//       sống (0073 1ED1 006E 0067) = sống (0073 1ED1 006E 0067): True
//       
//       
//       Normalization FormD:
//       
//       sống (0073 006F 0302 0301 006E 0067) = sống (0073 006F 0302 0301 006E 0067): True
//       sống (0073 006F 0302 0301 006E 0067) = sống (0073 006F 0302 0301 006E 0067): True
//       sống (0073 006F 0302 0301 006E 0067) = sống (0073 006F 0302 0301 006E 0067): True
//       
//       
//       Normalization FormKC:
//       
//       sống (0073 1ED1 006E 0067) = sống (0073 1ED1 006E 0067): True
//       sống (0073 1ED1 006E 0067) = sống (0073 1ED1 006E 0067): True
//       sống (0073 1ED1 006E 0067) = sống (0073 1ED1 006E 0067): True
//       
//       
//       Normalization FormKD:
//       
//       sống (0073 006F 0302 0301 006E 0067) = sống (0073 006F 0302 0301 006E 0067): True
//       sống (0073 006F 0302 0301 006E 0067) = sống (0073 006F 0302 0301 006E 0067): True
//       sống (0073 006F 0302 0301 006E 0067) = sống (0073 006F 0302 0301 006E 0067): True
open System
open System.IO
open System.Text

do
    use sw = new StreamWriter(@".\TestNorm1.txt")

    let showBytes (str: string) =
        let mutable result = ""
        for ch in str do
            result <- result + $"{uint16 ch:X4} "
        result.Trim()
    
    let testForEquality (words: string[]) =
        for ctr = 0 to words.Length - 2 do
            for ctr2 = ctr + 1 to words.Length - 1 do
                sw.WriteLine("{0} ({1}) = {2} ({3}): {4}",
                            words[ctr], showBytes(words[ctr]),
                            words[ctr2], showBytes(words[ctr2]),
                            words[ctr].Equals(words[ctr2], StringComparison.Ordinal))

    let normalizeStrings nf (words: string[]) =
        for i = 0 to words.Length - 1 do
            if not (words[i].IsNormalized nf) then
                words[i] <- words[i].Normalize nf
        words

    // Define three versions of the same word.
    let s1 = "sống"        // create word with U+1ED1
    let s2 = "s\u00F4\u0301ng"
    let s3 = "so\u0302\u0301ng"

    testForEquality [| s1; s2; s3 |]
    sw.WriteLine()

    // Normalize and compare strings using each normalization form.
    for formName in Enum.GetNames typeof<NormalizationForm> do
        sw.WriteLine("Normalization {0}:\n", formName)
        let nf = Enum.Parse(typeof<NormalizationForm>, formName) :?> NormalizationForm
        let sn = normalizeStrings nf [| s1; s2; s3|]
        testForEquality sn
        sw.WriteLine "\n"

// The example displays the following output:
//       sống (0073 1ED1 006E 0067) = sống (0073 00F4 0301 006E 0067): False
//       sống (0073 1ED1 006E 0067) = sống (0073 006F 0302 0301 006E 0067): False
//       sống (0073 00F4 0301 006E 0067) = sống (0073 006F 0302 0301 006E 0067): False
//
//       Normalization FormC:
//
//       sống (0073 1ED1 006E 0067) = sống (0073 1ED1 006E 0067): True
//       sống (0073 1ED1 006E 0067) = sống (0073 1ED1 006E 0067): True
//       sống (0073 1ED1 006E 0067) = sống (0073 1ED1 006E 0067): True
//
//
//       Normalization FormD:
//
//       sống (0073 006F 0302 0301 006E 0067) = sống (0073 006F 0302 0301 006E 0067): True
//       sống (0073 006F 0302 0301 006E 0067) = sống (0073 006F 0302 0301 006E 0067): True
//       sống (0073 006F 0302 0301 006E 0067) = sống (0073 006F 0302 0301 006E 0067): True
//
//
//       Normalization FormKC:
//
//       sống (0073 1ED1 006E 0067) = sống (0073 1ED1 006E 0067): True
//       sống (0073 1ED1 006E 0067) = sống (0073 1ED1 006E 0067): True
//       sống (0073 1ED1 006E 0067) = sống (0073 1ED1 006E 0067): True
//
//
//       Normalization FormKD:
//
//       sống (0073 006F 0302 0301 006E 0067) = sống (0073 006F 0302 0301 006E 0067): True
//       sống (0073 006F 0302 0301 006E 0067) = sống (0073 006F 0302 0301 006E 0067): True
//       sống (0073 006F 0302 0301 006E 0067) = sống (0073 006F 0302 0301 006E 0067): True
Imports System.Globalization
Imports System.IO
Imports System.Text

Module Example16
    Private sw As StreamWriter

    Public Sub Main()
        sw = New StreamWriter(".\TestNorm1.txt")

        ' Define three versions of the same word. 
        Dim s1 As String = "sống"        ' create word with U+1ED1
        Dim s2 As String = "s" + ChrW(&HF4) + ChrW(&H301) + "ng"
        Dim s3 As String = "so" + ChrW(&H302) + ChrW(&H301) + "ng"

        TestForEquality(s1, s2, s3)
        sw.WriteLine()

        ' Normalize and compare strings using each normalization form.
        For Each formName In [Enum].GetNames(GetType(NormalizationForm))
            sw.WriteLine("Normalization {0}:", formName)
            Dim nf As NormalizationForm = CType([Enum].Parse(GetType(NormalizationForm), formName),
                                             NormalizationForm)
            Dim sn() As String = NormalizeStrings(nf, s1, s2, s3)
            TestForEquality(sn)
            sw.WriteLine(vbCrLf)
        Next

        sw.Close()
    End Sub

    Private Sub TestForEquality(ParamArray words As String())
        For ctr As Integer = 0 To words.Length - 2
            For ctr2 As Integer = ctr + 1 To words.Length - 1
                sw.WriteLine("{0} ({1}) = {2} ({3}): {4}",
                         words(ctr), ShowBytes(words(ctr)),
                         words(ctr2), ShowBytes(words(ctr2)),
                         words(ctr).Equals(words(ctr2), StringComparison.Ordinal))
            Next
        Next
    End Sub

    Private Function ShowBytes(str As String) As String
        Dim result As String = Nothing
        For Each ch In str
            result += String.Format("{0} ", Convert.ToUInt16(ch).ToString("X4"))
        Next
        Return result.Trim()
    End Function

    Private Function NormalizeStrings(nf As NormalizationForm, ParamArray words() As String) As String()
        For ctr As Integer = 0 To words.Length - 1
            If Not words(ctr).IsNormalized(nf) Then
                words(ctr) = words(ctr).Normalize(nf)
            End If
        Next
        Return words
    End Function
End Module
' The example displays the following output:
'       sống (0073 1ED1 006E 0067) = sống (0073 00F4 0301 006E 0067): False
'       sống (0073 1ED1 006E 0067) = sống (0073 006F 0302 0301 006E 0067): False
'       sống (0073 00F4 0301 006E 0067) = sống (0073 006F 0302 0301 006E 0067): False
'       
'       Normalization FormC:
'       
'       sống (0073 1ED1 006E 0067) = sống (0073 1ED1 006E 0067): True
'       sống (0073 1ED1 006E 0067) = sống (0073 1ED1 006E 0067): True
'       sống (0073 1ED1 006E 0067) = sống (0073 1ED1 006E 0067): True
'       
'       
'       Normalization FormD:
'       
'       sống (0073 006F 0302 0301 006E 0067) = sống (0073 006F 0302 0301 006E 0067): True
'       sống (0073 006F 0302 0301 006E 0067) = sống (0073 006F 0302 0301 006E 0067): True
'       sống (0073 006F 0302 0301 006E 0067) = sống (0073 006F 0302 0301 006E 0067): True
'       
'       
'       Normalization FormKC:
'       
'       sống (0073 1ED1 006E 0067) = sống (0073 1ED1 006E 0067): True
'       sống (0073 1ED1 006E 0067) = sống (0073 1ED1 006E 0067): True
'       sống (0073 1ED1 006E 0067) = sống (0073 1ED1 006E 0067): True
'       
'       
'       Normalization FormKD:
'       
'       sống (0073 006F 0302 0301 006E 0067) = sống (0073 006F 0302 0301 006E 0067): True
'       sống (0073 006F 0302 0301 006E 0067) = sống (0073 006F 0302 0301 006E 0067): True
'       sống (0073 006F 0302 0301 006E 0067) = sống (0073 006F 0302 0301 006E 0067): True

Aby uzyskać więcej informacji na temat formularzy normalizacji i normalizacji, zobacz System.Text.NormalizationForm, a także Unicode Standard Annex #15: Formularze normalizacji Unicode i często zadawane pytania dotyczące normalizacji w witrynie internetowej unicode.org.

Operacje na ciągach według kategorii

Klasa String udostępnia elementy członkowskie do porównywania ciągów, testowania ciągów pod kątem równości, znajdowania znaków lub podciągów w ciągu, modyfikowania ciągu, wyodrębniania podciągów z ciągu, łączenia ciągów, formatowania wartości, kopiowania ciągu i normalizacji ciągu.

Porównywanie ciągów

Ciągi można porównać, aby określić ich położenie względne w kolejności sortowania przy użyciu następujących String metod:

  • Compare Zwraca liczbę całkowitą, która wskazuje relację jednego ciągu z drugim ciągiem w kolejności sortowania.

  • CompareOrdinal Zwraca liczbę całkowitą, która wskazuje relację jednego ciągu z drugim ciągiem na podstawie porównania punktów kodu.

  • CompareTo Zwraca liczbę całkowitą, która wskazuje relację bieżącego wystąpienia ciągu z drugim ciągiem w kolejności sortowania. Metoda CompareTo(String) udostępnia IComparable implementacje i IComparable<T> dla String klasy .

Testowanie ciągów pod kątem równości

Wywołasz metodę , Equals aby określić, czy dwa ciągi są równe. Wystąpienie Equals(String, String, StringComparison) i przeciążenia statyczne Equals(String, StringComparison) umożliwiają określenie, czy porównanie jest wrażliwe na kulturę lub porządkowe, oraz czy wielkość liter jest uwzględniana, czy ignorowana. Większość testów równości jest porządkowa, a porównania równości określające dostęp do zasobu systemowego (takiego jak obiekt systemu plików) powinny zawsze być porządkowe.

Znajdowanie znaków w ciągu

Klasa String zawiera dwa rodzaje metod wyszukiwania:

Ostrzeżenie

Jeśli chcesz wyszukać ciąg dla określonego wzorca, a nie określonego podciągu, należy użyć wyrażeń regularnych. Aby uzyskać więcej informacji, zobacz Wyrażenia regularne platformy .NET.

Modyfikowanie ciągu

Klasa String zawiera następujące metody, które wydają się modyfikować wartość ciągu:

  • Insert Wstawia ciąg do bieżącego String wystąpienia.

  • PadLeft Wstawia jedno lub więcej wystąpień określonego znaku na początku ciągu.

  • PadRight Wstawia jedno lub więcej wystąpień określonego znaku na końcu ciągu.

  • Remove Usuwa podciąg z bieżącego String wystąpienia.

  • Replace Zamienia podciąg na inny podciąg w bieżącym String wystąpieniu.

  • ToLower i ToLowerInvariant przekonwertuj wszystkie znaki w ciągu na małe litery.

  • ToUpper i ToUpperInvariant przekonwertuj wszystkie znaki w ciągu na wielkie litery.

  • Trim Usuwa wszystkie wystąpienia znaku od początku i końca ciągu.

  • TrimEnd usuwa wszystkie wystąpienia znaku z końca ciągu.

  • TrimStart usuwa wszystkie wystąpienia znaku od początku ciągu.

Ważne

Wszystkie metody modyfikacji ciągu zwracają nowy String obiekt. Nie modyfikują one wartości bieżącego wystąpienia.

Wyodrębnianie podciągów z ciągu

Metoda String.Split oddziela jeden ciąg na wiele ciągów. Przeciążenia metody umożliwiają określenie wielu ograniczników, ograniczenie liczby podciągów wyodrębnionych przez metodę, przycinanie białych znaków z podciągów i określanie, czy puste ciągi (które występują, gdy ograniczniki są sąsiadujące) są uwzględniane wśród zwracanych ciągów.

Łączenie ciągów

Do łączenia ciągów można użyć następujących String metod:

  • Concat łączy jeden lub więcej podciągów w jeden ciąg.
  • Join Łączy co najmniej jeden podciąg w jeden element i dodaje separator między poszczególnymi podciągami.

Formatowanie wartości

Metoda String.Format używa funkcji formatowania złożonego, aby zastąpić jeden lub więcej symboli zastępczych w ciągu ciągiem reprezentującym jakiś obiekt lub wartość. Metoda Format jest często używana do wykonywania następujących czynności:

  • Aby osadzić reprezentację ciągu wartości liczbowej w ciągu.
  • Aby osadzić ciąg reprezentujący wartość daty i godziny w ciągu.
  • Aby osadzić reprezentację ciągu wartości wyliczenia w ciągu.
  • Aby osadzić reprezentację ciągu obiektu obsługującego IFormattable interfejs w ciągu.
  • Aby uzasadnić prawo lub w lewo, podciąg w polu w większym ciągu.

Aby uzyskać szczegółowe informacje na temat operacji formatowania i przykładów, zobacz Format podsumowanie przeciążenia.

Kopiowanie ciągu

Aby utworzyć kopię ciągu, możesz wywołać następujące String metody:

  • Clone Zwraca odwołanie do istniejącego String obiektu.
  • Copy tworzy kopię istniejącego ciągu.
  • CopyTo kopiuje część ciągu do tablicy znaków.

Normalizacja ciągu

W standardze Unicode pojedynczy znak może mieć wiele punktów kodu. Normalizacja konwertuje te równoważne znaki na tę samą reprezentację binarną. Metoda String.Normalize wykonuje normalizację, a String.IsNormalized metoda określa, czy ciąg jest znormalizowany.

Aby uzyskać więcej informacji i przykład, zobacz sekcję Normalizacja wcześniej w tym artykule.