次の方法で共有


System.String クラス

この記事では、この API のリファレンス ドキュメントへの補足的な解説を提供します。

文字列は、テキストを表すために使用される文字のシーケンシャル コレクションです。 String オブジェクトは、文字列を表すSystem.Char オブジェクトのシーケンシャル コレクションです。System.Char オブジェクトは UTF-16 コード単位に対応します。 String オブジェクトの値は、System.Char オブジェクトのシーケンシャル コレクションの内容であり、その値は不変です (つまり、読み取り専用です)。 文字列の不変性の詳細については、「 Immutability と StringBuilder クラス 」セクションを参照してください。 メモリ内の String オブジェクトの最大サイズは、2 GB または約 10 億文字です。

Unicode、UTF-16、コード単位、コード ポイント、 Char 型と Rune 型の詳細については、「 .NET での文字エンコードの概要を参照してください。

String オブジェクトをインスタンス化する

String オブジェクトは、次の方法でインスタンス化できます。

  • 文字列リテラルを String 変数に割り当てること。 これは、文字列を作成するために最も一般的に使用されるメソッドです。 次の例では、代入を使用して複数の文字列を作成します。 C# および F# では、円記号 (\) がエスケープ文字であるため、文字列内のリテラル円記号をエスケープするか、文字列全体を @-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
    
  • String クラス コンストラクターを呼び出すこと。 次の例では、複数のクラス コンストラクターを呼び出して文字列をインスタンス化します。 一部のコンストラクターには、パラメーターとして文字配列または符号付きバイト配列へのポインターが含まれています。 Visual Basic では、これらのコンストラクターの呼び出しはサポートされていません。 Stringコンストラクターの詳細については、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
    
  • 文字列連結演算子 (C# および F# では +、Visual Basic では +) を使用して、 String インスタンスと文字列リテラルの任意の組み合わせから 1 つの文字列を作成します。 文字列連結演算子の使用例を次に示します。

    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.
    
  • プロパティを取得するか、文字列を返すメソッドを呼び出します。 次の例では、 String クラスのメソッドを使用して、大きな文字列から部分文字列を抽出します。

    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
    
  • 書式設定メソッドを呼び出して、値またはオブジェクトを文字列形式に変換します。 次の例では、 composite の書式設定 機能を使用して、2 つのオブジェクトの文字列表現を文字列に埋め込みます。

    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.
    

Char オブジェクトと Unicode 文字

文字列内の各文字は、Unicode スカラー値 (Unicode コード ポイントまたは Unicode 文字の序数 (数値) 値とも呼ばれます) によって定義されます。 各コード ポイントは UTF-16 エンコードを使用してエンコードされ、エンコードの各要素の数値は Char オブジェクトによって表されます。

Note

String インスタンスは UTF-16 コード単位のシーケンシャル コレクションで構成されるため、整形式の Unicode 文字列ではないString オブジェクトを作成できます。 たとえば、対応する高サロゲートを持たない低いサロゲートを持つ文字列を作成できます。 System.Text名前空間内のオブジェクトをエンコードおよびデコードするメソッドなど、一部のメソッドでは、文字列が整形式であることを確認するチェックが実行される場合がありますが、Stringクラス メンバーは文字列が整形式であることを確認しません。

通常、1 つの Char オブジェクトは 1 つのコード ポイントを表します。つまり、 Char の数値はコード ポイントと等しくなります。 たとえば、文字 "a" のコード ポイントは U+0061 です。 ただし、コード ポイントには、複数のエンコードされた要素 (複数の Char オブジェクト) が必要な場合があります。 Unicode 標準では、複数の Char オブジェクトに対応する 2 種類の文字が定義されています。graphemes と Unicode 補助プレーンの文字に対応する Unicode 補助コード ポイントです。

  • グラフは、基本文字の後に 1 つ以上の結合文字が続いて表されます。 たとえば、文字 ä は、コード ポイントが U+0061 の Char オブジェクトの後に、コード ポイントが U+0308 である Char オブジェクトによって表されます。 この文字は、U+00E4 のコード ポイントを持つ 1 つの Char オブジェクトでも定義できます。 次の例に示すように、等しいカルチャに依存する比較は、通常の序数比較では等しくないが、これら 2 つの表現が等しいことを示しています。 ただし、2 つの文字列が正規化されている場合、序数比較は等しいことを示します。 (文字列の正規化の詳細については、 を参照してください。正規化 セクション)。)

    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
    
  • Unicode 補助コード ポイント (サロゲート ペア) は、コード ポイントが上位サロゲートである Char オブジェクトの後に、コード ポイントが低サロゲートである Char オブジェクトによって表されます。 上位サロゲートのコード単位は、U+D800 から U+DBFF の範囲です。 低サロゲートのコード単位は、U+DC00 から U+DFFF の範囲です。 サロゲート ペアは、16 Unicode 補助プレーンの文字を表すために使用されます。 次の例では、サロゲート文字を作成し、それを Char.IsSurrogatePair(Char, Char) メソッドに渡して、サロゲート ペアであるかどうかを判断します。

    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
    

Unicode 標準

文字列内の文字は UTF-16 でエンコードされたコード単位で表され、 Char 値に対応します。

文字列内の各文字には、 UnicodeCategory 列挙型によって .NET で表される Unicode 文字カテゴリが関連付けられています。 文字またはサロゲート ペアのカテゴリは、 CharUnicodeInfo.GetUnicodeCategory メソッドを呼び出すことによって決定できます。

.NET は独自の文字のテーブルとそれに対応するカテゴリを保持するため、さまざまなプラットフォームで実行されている特定のバージョンの .NET 実装で同一の文字カテゴリの情報が返されるようになります。 すべての .NET バージョンおよびすべての OS プラットフォームで、文字カテゴリ情報は Unicode Character Database によって提供されます。

次の表は、.NET のバージョンとその文字カテゴリが基準としている Unicode 標準のバージョンを示しています。

.NET のバージョン Unicode 標準のバージョン
.NET Framework 1.1 Unicode 標準、バージョン 4.0.0
.NET Framework 2.0 Unicode 標準、バージョン 5.0.0
.NET Framework 3.5 Unicode 標準、バージョン 5.0.0
.NET Framework 4 Unicode 標準、バージョン 5.0.0
.NET Framework 4.5 Unicode 標準、バージョン 6.3.0
.NET Framework 4.5.1 Unicode 標準、バージョン 6.3.0
.NET Framework 4.5.2 Unicode 標準、バージョン 6.3.0
.NET Framework 4.6 Unicode 標準、バージョン 6.3.0
.NET Framework 4.6.1 Unicode 標準、バージョン 6.3.0
.NET Framework 4.6.2 およびそれ以降のバージョン Unicode 標準、バージョン 8.0.0
.NET Core 2.1 Unicode 標準、バージョン 8.0.0
.NET Core 3.1 Unicode 標準バージョン 11.0.0
.NET 5 Unicode 標準バージョン 13.0.0

さらに、.NET では、Unicode 標準に基づく文字列の比較と並べ替えがサポートされています。 Windows 8 以降のバージョンの Windows オペレーティング システムで実行されている .NET Framework 4.5 以降、ランタイムは文字列の比較操作と並べ替え操作をオペレーティング システムに委任します。 .NET Core および .NET 5 以降では、文字列の比較と並べ替えの情報は、 Unicode 用のインターナショナル コンポーネント ライブラリによって提供されます (Windows 10 May 2019 Update より前の Windows バージョンを除く)。 次の表に、.NET のバージョンと、文字の比較と並べ替えの基になっている Unicode 標準のバージョンを示します。

.NET のバージョン Unicode 標準のバージョン
Windows 7 の .NET Framework 4.5 以降 Unicode 標準、バージョン 5.0.0
Windows 8 以降の Windows オペレーティング システム上の .NET Framework 4.5 以降 Unicode 標準、バージョン 6.3.0
.NET Core および .NET 5+ 基になるオペレーティング システムでサポートされている Unicode 標準のバージョンによって異なります。

埋め込み null 文字

.NET では、 String オブジェクトには、文字列の長さの一部としてカウントされる埋め込み null 文字を含めることができます。 ただし、C や C++ などの一部の言語では、null 文字は文字列の末尾を示します。文字列の一部とは見なされず、文字列の長さの一部としてカウントされません。 つまり、C または C++ で記述された C および C++ のプログラマまたはライブラリが文字列に関して行う可能性がある次の一般的な前提は、 String オブジェクトに適用されるときに必ずしも有効であるとは限りません。

  • strlenまたはwcslen関数によって返される値は、必ずしもString.Length等しいとは限りません。

  • strcpy_s関数またはwcscpy_s関数によって作成された文字列は、コピーされる文字列と必ずしも同じではありません。

Stringオブジェクトをインスタンス化するネイティブ C および C++ コードと、プラットフォーム呼び出しを介してオブジェクトString渡されるコードは、埋め込み null 文字が文字列の末尾をマークすることを前提としないようにする必要があります。

文字列に埋め込まれた null 文字は、文字列が並べ替えられる (または比較される) 場合と、文字列が検索されるときにも、異なる方法で処理されます。 インバリアント カルチャを使用した比較など、2 つの文字列間でカルチャに依存する比較を実行する場合、Null 文字は無視されます。 これらは、序数または大文字と小文字を区別しない序数の比較に対してのみ考慮されます。 一方、埋め込み null 文字は、 ContainsStartsWithIndexOfなどのメソッドを使用して文字列を検索するときに常に考慮されます。

文字列とインデックス

インデックスは、String内の (Unicode 文字ではなく) Char オブジェクトの位置です。 インデックスは、文字列内の最初の位置 (インデックス位置 0) から始まる 0 から始まる負でない 0 から始まる数値です。 IndexOfLastIndexOfなど、多くの検索メソッドは、文字列インスタンス内の文字または部分文字列のインデックスを返します。

Chars[] プロパティを使用すると、文字列内のインデックス位置によって個々のChar オブジェクトにアクセスできます。 Chars[] プロパティは既定のプロパティ (Visual Basic の場合) またはインデクサー (C# および F# の場合) であるため、次のようなコードを使用して、文字列内の個々のChar オブジェクトにアクセスできます。 このコードは、文字列内の空白文字または句読点文字を検索して、文字列に含まれる単語の数を決定します。

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 クラスはIEnumerable インターフェイスを実装するため、次の例に示すように、foreachコンストラクトを使用して文字列内のChar オブジェクトを反復処理することもできます。

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.

Unicode 文字は複数の Char オブジェクトとしてエンコードされる可能性があるため、連続するインデックス値が連続する Unicode 文字に対応していない可能性があります。 特に、文字列には、基本文字の後に 1 つ以上の結合文字またはサロゲート ペアが続く複数文字のテキスト単位を含めることができます。 Char オブジェクトではなく Unicode 文字を使用するには、System.Globalization.StringInfoクラスとTextElementEnumerator クラス、またはString.EnumerateRunes メソッドとRune構造体を使用します。 次の例は、 Char オブジェクトで動作するコードと Unicode 文字で動作するコードの違いを示しています。 これは、文の各単語の文字数またはテキスト要素の数を比較します。 文字列には、2 つのシーケンスの基本文字の後に結合文字が続きます。

// 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

この例では、 StringInfo.GetTextElementEnumerator メソッドと TextElementEnumerator クラスを使用して文字列内のすべてのテキスト要素を列挙することで、テキスト要素を操作します。 StringInfo.ParseCombiningCharacters メソッドを呼び出すことによって、各テキスト要素の開始インデックスを含む配列を取得することもできます。

個々の Char 値ではなくテキストの単位を操作する方法の詳細については、「 .NET での文字エンコードの概要を参照してください。

null 文字列と空の文字列

宣言されているが、値が割り当てられていない文字列は null。 その文字列でメソッドを呼び出そうとすると、 NullReferenceExceptionがスローされます。 null 文字列は、値が "" または String.Empty の文字列である空の文字列とは異なります。 場合によっては、メソッド呼び出しで null 文字列または空の文字列を引数として渡すと、例外がスローされます。 たとえば、 Int32.Parse メソッドに null 文字列を渡すと ArgumentNullExceptionがスローされ、空の文字列を渡すと FormatExceptionがスローされます。 それ以外の場合は、メソッド引数に null 文字列または空の文字列を指定できます。 たとえば、クラスに IFormattable 実装を提供する場合は、null 文字列と空の文字列の両方を一般 ("G") 書式指定子と同じにする必要があります。

String クラスには、文字列がnullか空かをテストできる、次の 2 つの便利なメソッドが含まれています。

  • IsNullOrEmpty: 文字列が null されているか、 String.Emptyと等しいかを示します。 このメソッドを使用すると、次のようなコードを使用する必要がなくなります。

    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は、文字列が nullされているか、 String.Empty等しいか、空白文字のみで構成されているかを示します。 このメソッドを使用すると、次のようなコードを使用する必要がなくなります。

    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
    

次の例では、カスタム Temperature クラスのIFormattable.ToString実装でIsNullOrEmpty メソッドを使用します。 このメソッドは、"G"、"C"、"F"、および "K" の書式指定文字列をサポートしています。 空の書式指定文字列または値が null の書式指定文字列がメソッドに渡された場合、その値は "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

不変性と StringBuilder クラス

String オブジェクトは、作成後に値を変更できないため、不変 (読み取り専用) と呼ばれます。 String オブジェクトを変更するように見えるメソッドは、変更を含む新しいString オブジェクトを実際に返します。

文字列は不変であるため、1 つの文字列と思われるものに対して繰り返し追加または削除を実行する文字列操作ルーチンでは、パフォーマンスが大幅に低下する可能性があります。 たとえば、次のコードでは、乱数ジェネレーターを使用して、0x052Fする範囲に 1000 文字の文字列0x0001作成します。 コードは文字列連結を使用して、 strという名前の既存の文字列に新しい文字を追加するように見えますが、実際には連結操作ごとに新しい String オブジェクトを作成します。

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

文字列の値に複数の変更を加える操作には、String クラスの代わりに StringBuilder クラスを使用できます。 String クラスのインスタンスとは異なり、StringBuilder オブジェクトは変更可能です。文字列から部分文字列を連結、追加、または削除すると、操作は 1 つの文字列に対して実行されます。 StringBuilder オブジェクトの値の変更が完了したら、StringBuilder.ToString メソッドを呼び出して文字列に変換できます。 次の例では、前の例で使用した String を置き換えて、範囲内の 1,000 文字のランダムな文字を連結して、 StringBuilder オブジェクトに0x052F 0x0001します。

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

序数とカルチャに依存する操作

String クラスのメンバーは、String オブジェクトに対して序数またはカルチャに依存する (言語) 操作を実行します。 序数演算は、各 Char オブジェクトの数値に作用します。 カルチャに依存する操作は、 String オブジェクトの値に対して機能し、カルチャ固有の大文字と小文字の区別、並べ替え、書式設定、および解析ルールを考慮します。 カルチャに依存する操作は、明示的に宣言されたカルチャまたは暗黙的な現在のカルチャのコンテキストで実行されます。 2 種類の操作は、同じ文字列に対して実行されると、非常に異なる結果を生成できます。

.NET では、インバリアント カルチャ (CultureInfo.InvariantCulture) を使用してカルチャを認識しない言語文字列操作もサポートしています。これは、地域に依存しない英語のカルチャ設定に大まかに基づいています。 他の System.Globalization.CultureInfo 設定とは異なり、インバリアント カルチャの設定は、1 台のコンピューター、システム間、および .NET のバージョン間で一貫性を維持することが保証されます。 インバリアント カルチャは、すべてのカルチャで文字列比較と順序付けの安定性を確保するブラック ボックスの一種と見なすことができます。

重要

アプリケーションで、ファイル名や名前付きパイプなどのシンボリック識別子、または XML ファイル内のテキスト ベースのデータなどの永続化されたデータに関するセキュリティ上の決定を行う場合、操作ではカルチャに依存する比較ではなく序数比較を使用する必要があります。 これは、カルチャに依存する比較では、有効なカルチャに応じて異なる結果が得られるのに対し、序数比較は比較される文字のバイナリ値のみに依存するためです。

重要

文字列操作を実行するほとんどのメソッドには、 StringComparison型のパラメーターを持つオーバーロードが含まれています。これにより、メソッドが序数またはカルチャに依存する操作を実行するかどうかを指定できます。 一般に、このオーバーロードを呼び出して、メソッド呼び出しの意図を明確にする必要があります。 文字列に対して序数およびカルチャに依存する操作を使用するためのベスト プラクティスとガイダンスについては、「 文字列を使用するためのベスト プラクティスを参照してください。

大文字と小文字の区別、解析と書式設定、比較と並べ替え、および等価性のテストの操作は、序数またはカルチャに依存する場合があります。 次のセクションでは、操作の各カテゴリについて説明します。

ヒント

メソッド呼び出しの意図を明確にするメソッド オーバーロードを常に呼び出す必要があります。 たとえば、Compare(String, String) メソッドを呼び出して、現在のカルチャの規則を使用して 2 つの文字列のカルチャに依存する比較を実行する代わりに、comparisonType引数の値が StringComparison.CurrentCultureCompare(String, String, StringComparison) メソッドを呼び出す必要があります。 詳細については、「文字列を使用するためのベスト プラクティス」を参照してください。

並べ替えおよび比較操作で使用される文字の重みに関する情報を含む一連のテキスト ファイルである並べ替え重みテーブルは、次のリンクからダウンロードできます。

大文字小文字の区別

大文字と小文字の規則は、Unicode 文字の大文字と小文字を変更する方法を決定します。たとえば、小文字から大文字までです。 多くの場合、大文字と小文字の区別操作は文字列比較の前に実行されます。 たとえば、文字列を別の大文字の文字列と比較できるように、大文字に変換できます。 ToLowerメソッドまたは ToLowerInvariant メソッドを呼び出すことによって、文字列内の文字を小文字に変換できます。また、ToUpper または ToUpperInvariant メソッドを呼び出すことで大文字に変換できます。 さらに、 TextInfo.ToTitleCase メソッドを使用して、文字列をタイトル ケースに変換することもできます。

Note

Linux および macOS システムでのみ実行されている .NET Core: C および Posix のカルチャでは、想定されている Unicode 照合順序が使用されないため、照合順序の動作で常に大文字と小文字が区別されます。 カルチャに依存する、大文字と小文字を区別しない並べ替え操作を実行する場合は、C または Posix 以外のカルチャを使うことをお勧めします。

大文字と小文字の区別の操作は、現在のカルチャ、指定されたカルチャ、またはインバリアント カルチャのルールに基づいて行うことができます。 ケース マッピングは使用されるカルチャによって異なる場合があるため、大文字と小文字の区別操作の結果はカルチャによって異なる場合があります。 大文字と小文字の実際の違いは、次の 3 種類です。

  • ラテン大文字 I (U+0049)、ラテン小文字 I (U+0069)、英大文字 I (上のドット付き) (U+0130)、ラテン小文字ドットレス I (U+0131) の大文字マッピングの違い。 tr-TR (トルコ語 (トルコ語 (トルコ)) と az-Latn-AZ (アゼルバイジャン、ラテン) カルチャ、および tr、az、az-Latn ニュートラル カルチャでは、ラテン大文字 I はラテン小文字 DOTLESS I で、大文字の小文字 I はラテン小文字 I に相当し、大文字の小文字 I は DOT ABOVE です。 インバリアント カルチャを含む他のすべてのカルチャでは、LATIN SMALL LETTER I と LATIN CAPITAL LETTER I は小文字と大文字に相当します。

    次の例は、カルチャに依存する大文字と小文字の比較に依存している場合に、ファイル システムアクセスを防ぐために設計された文字列比較が失敗する方法を示しています。 (インバリアント カルチャの大文字と小文字の表記規則を使用する必要があります)。

    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
    
  • インバリアント カルチャと他のすべてのカルチャ間のケース マッピングの違い。 このような場合、インバリアント カルチャの大文字と小文字の規則を使用して文字を大文字または小文字に変更すると、同じ文字が返されます。 他のすべてのカルチャでは、別の文字が返されます。 影響を受ける文字の一部を次の表に示します。

    文字 に変更した場合 返品
    ミクロン記号 (U+00B5) 大文字 ギリシャ大文字 MU (U+-39C)
    上にドットがあるラテン大文字 I (U+0130) 小文字 英小文字 I (U+0069)
    英小文字ドットレス I (U+0131) 大文字 英大文字 I (U+0049)
    英小文字 LONG S (U+017F) 大文字 英大文字 S (U+0053)
    英大文字 D と小文字 Z CARON (U+01C5) 小文字 英小文字 DZ と CARON (U+01C6)
    ギリシャ語の YPOGEGRAMMENI の組み合わせ (U+0345) 大文字 ギリシャ大文字 IOTA (U+0399)
  • ASCII 文字範囲での 2 文字の大文字と小文字が混在するペアのケース マッピングの違い。 ほとんどのカルチャでは、2 文字の大文字と小文字のペアは、等価の 2 文字の大文字または小文字のペアと等しくなります。 これは、次のカルチャの次の 2 文字のペアには当てはまりません。これは、それぞれのケースでディグラフと比較されるためです。

    • hr-HR (クロアチア語 (クロアチア)) 文化における "lJ" と "nJ"。
    • cs-CZ (チェコ共和国)、sk-SK (スロバキア (スロバキア)) 文化の "cH" です。
    • da-DK (デンマーク)文化の"aA"。
    • hu-HU (ハンガリー) カルチャの "cS"、"dZ"、"dZS"、"nY"、"sZ"、"tY"、および "zS"。
    • es-ES_tradnl (スペイン語 (スペイン、伝統的な並べ替え)) カルチャの "cH" と "lL"。
    • vi-VN (ベトナム (ベトナム)) カルチャの "cH"、"gI"、"kH"、"nG"nH"、"pH"、"qU"、"tH"、および "tR" 。

    ただし、固定文字列または識別子ではこれらのペアが一般的でないため、これらのペアのカルチャに依存した比較によって問題が発生する状況は通常とは異なります。

次の例は、文字列を大文字に変換するときのカルチャ間の大文字と小文字の規則の違いをいくつか示しています。

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

解析と書式設定

書式設定と解析は逆操作です。 書式設定ルールは、日付と時刻や数値などの値を文字列形式に変換する方法を決定します。一方、解析規則では、文字列形式を日付や時刻などの値に変換する方法が決まります。 書式設定と解析の両方の規則は、カルチャ規則に依存します。 次の例は、カルチャ固有の日付文字列を解釈するときに発生する可能性があるあいまいさを示しています。 日付文字列の生成に使用されたカルチャの規則を知らなければ、2011 年 3 月 1 日、2011 年 3 月 1 日、および 2011 年 1 月 3 日または 2011 年 3 月 1 日を表すかどうかを把握することはできません。

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

同様に、次の例に示すように、1 つの文字列で、解析操作で規則が使用されるカルチャに応じて異なる日付を生成できます。

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

文字列の比較と並べ替え

文字列の比較と並べ替えの規則は、カルチャによって異なります。 たとえば、並べ替え順序は、ふりがなや文字の視覚的表現に基づく場合があります。 東アジア圏の言語では、文字が表意文字の画数と部首によって並べ替えられます。 また、並べ替えは、言語やカルチャで使用されているアルファベットの順序によっても異なります。 たとえば、デンマーク語の文字 "Æ" は、アルファベットでは "Z" の後に位置します。 さらに、比較では大文字と小文字が区別されるか、大文字と小文字が区別されない場合があり、大文字と小文字の区別規則はカルチャによって異なる場合があります。 一方、序数比較では、文字列を比較および並べ替えるときに、文字列内の個々の文字の Unicode コード ポイントを使用します。

並べ替えルールは、Unicode 文字のアルファベット順と、2 つの文字列が相互にどのように比較されるかを決定します。 たとえば、 String.Compare(String, String, StringComparison) メソッドは、 StringComparison パラメーターに基づいて 2 つの文字列を比較します。 パラメーター値が StringComparison.CurrentCultureの場合、メソッドは現在のカルチャの規則を使用する言語比較を実行します。パラメーター値が StringComparison.Ordinal場合、メソッドは序数比較を実行します。 したがって、次の例に示すように、現在のカルチャが米国英語の場合、(カルチャに依存する比較を使用して) String.Compare(String, String, StringComparison) メソッドの最初の呼び出しでは "a" が "A" より小さいと見なされますが、同じメソッドの 2 番目の呼び出し (序数比較を使用) では "a" が "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

.NET では、単語、文字列、および序数の並べ替え規則がサポートされています。

  • 単語での並べ替えでは、英数字以外の特定の Unicode 文字には特別な重みが割り当てられる、カルチャに依存した文字列の比較が行われます。 たとえば、ハイフン (-) には非常に小さな重みが割り当てられ、並べ替えられたリスト内で "coop" と "co-op" が隣り合って表示される場合があります。 単語の並べ替えルールを使用して 2 つの文字列を比較する String メソッドの一覧については、「カテゴリ別の 文字列操作 」セクションを参照してください。

  • 文字列の並べ替えでは、カルチャに依存する比較も実行されます。 これは単語の並べ替えに似ていますが、特殊なケースがなく、英数字以外のすべての記号がすべての英数字 Unicode 文字の前に来る点が異なります。 CompareOptions.StringSortの値が指定されたoptions パラメーターを持つCompareInfo.Compare メソッドオーバーロードを呼び出すことで、文字列並べ替えルールを使用して 2 つの文字列を比較できます。 これは、文字列の並べ替え規則を使用して 2 つの文字列を比較するために .NET が提供する唯一のメソッドであることに注意してください。

  • 序数の並べ替えでは、文字列内の各 Char オブジェクトの数値に基づいて文字列を比較します。 文字の小文字と大文字のバージョンのコード ポイントが異なるため、序数比較では自動的に大文字と小文字が区別されます。 ただし、大文字と小文字が重要でない場合は、大文字と小文字を無視する序数比較を指定できます。 これは、インバリアント カルチャを使用して文字列を大文字に変換し、結果に対して序数比較を実行することと同じです。 序数並べ替え規則を使用して 2 つの文字列を比較する String メソッドの一覧については、「カテゴリ別の String 操作 」セクションを参照してください。

カルチャに依存する比較とは、CultureInfo.InvariantCulture プロパティで指定されたインバリアント カルチャを含め、CultureInfo オブジェクトを明示的または暗黙的に使用する比較です。 暗黙的なカルチャは現在のカルチャであり、 Thread.CurrentCulture プロパティと CultureInfo.CurrentCulture プロパティで指定されます。 カルチャ間では、アルファベット文字 (つまり、 Char.IsLetter プロパティが trueを返す文字) の並べ替え順序に大きな違いがあります。 特定のカルチャの規則を使用するカルチャに依存する比較を指定するには、Compare(String, String, CultureInfo, CompareOptions)などの文字列比較メソッドにCultureInfo オブジェクトを指定します。 Compare メソッドの適切なオーバーロードにCompareOptions.OrdinalまたはCompareOptions.OrdinalIgnoreCase以外のCompareOptions列挙体のStringComparison.CurrentCultureStringComparison.CurrentCultureIgnoreCase、または任意のメンバーを指定することで、現在のカルチャの規則を使用するカルチャに依存する比較を指定できます。 通常、カルチャに依存する比較は並べ替えに適しているのに対し、序数比較は適していません。 序数比較は通常、2 つの文字列が等しい (つまり、ID を判断する) かどうかを判断するのに適しているのに対し、カルチャに依存する比較は適していません。

次の例は、カルチャに依存する比較と序数の比較の違いを示しています。 この例では、序数比較と da-DK カルチャと en-US カルチャの規則 ( Compare メソッドが呼び出された時点での既定のカルチャ) を使用して、3 つの文字列 "Apple"、"Æble"、および "AEble" を評価します。 デンマーク語では文字 "Æ" が個々の文字として扱われ、アルファベットの "Z" の後に並べ替えられるため、文字列 "Æble" は "Apple" より大きくなります。 ただし、"Æble" は "AEble" と同等とは見なされないため、"Æble" も "AEble" より大きくなります。 en-US カルチャには文字 "Æ" は含まれませんが、"AE" と同等として扱います。これは、"Æble" が "Apple" より小さいが "AEble" と等しい理由を説明します。 一方、序数比較では、"Apple" は "Æble" 未満、"Æble" は "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

適切な並べ替え方法または文字列比較方法を選択するには、次の一般的なガイドラインを使用します。

  • ユーザーのカルチャに基づいて文字列を並べ替える場合は、現在のカルチャの規則に基づいて並べ替える必要があります。 ユーザーのカルチャが変更されると、並べ替えられた文字列の順序もそれに応じて変更されます。 たとえば、類義語辞典アプリケーションでは、常にユーザーのカルチャに基づいて単語を並べ替える必要があります。

  • 特定のカルチャの規則に基づいて文字列を並べ替える場合は、そのカルチャを表す CultureInfo オブジェクトを比較メソッドに指定して、文字列を並べ替える必要があります。 たとえば、学生に特定の言語を教えるために設計されたアプリケーションでは、その言語を話すカルチャの 1 つの規則に基づいて文字列を並べ替える必要があります。

  • カルチャ間で文字列の順序を変更しない場合は、インバリアント カルチャの規則に基づいて順序を付けるか、序数比較を使用する必要があります。 たとえば、序数並べ替えを使用して、ファイル、プロセス、ミューテックス、または名前付きパイプの名前を整理します。

  • セキュリティ上の決定を伴う比較 (ユーザー名が有効かどうかなど) の場合は、常に、 Equals メソッドのオーバーロードを呼び出して、等価性の序数テストを実行する必要があります。

Note

文字列比較で使用されるカルチャに依存する並べ替えと大文字と小文字の区別の規則は、.NET のバージョンによって異なります。 .NET Core では、文字列比較は、基になるオペレーティング システムでサポートされている Unicode 標準のバージョンによって異なります。 Windows 8 以降で実行されている .NET Framework 4.5 以降のバージョンでは、並べ替え、大文字と小文字の区別、正規化、Unicode 文字情報は Unicode 6.0 標準に準拠しています。 他の Windows オペレーティング システムでは、Unicode 5.0 標準に準拠しています。

単語、文字列、序数の並べ替え規則の詳細については、 System.Globalization.CompareOptions トピックを参照してください。 各ルールを使用するタイミングに関するその他の推奨事項については、「文字列を使用するためのベスト プラクティスを参照してください。

通常、文字列の並べ替え順序を決定するために、 Compare などの文字列比較メソッドを直接呼び出すことはありません。 代わりに、比較メソッドは、 Array.SortList<T>.Sortなどの並べ替えメソッドによって呼び出されます。 次の例では、文字列比較メソッドを明示的に呼び出さずに、4 つの異なる並べ替え操作 (現在のカルチャを使用した単語の並べ替え、インバリアント カルチャを使用した単語の並べ替え、インバリアント カルチャを使用した序数の並べ替え、文字列の並べ替え) を実行します。ただし、使用する比較の種類は指定します。 並べ替えの種類ごとに、配列内の文字列の一意の順序が生成されることに注意してください。

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

ヒント

内部的には、.NET は並べ替えキーを使用して、カルチャに依存する文字列比較をサポートします。 文字列内の各文字には、アルファベット順、大文字と小文字の区別、発音の区別など、さまざまなカテゴリの並べ替えウェイトが指定されます。 SortKey クラスで表される並べ替えキーは、特定の文字列に対してこれらの重みのリポジトリを提供します。 アプリが同じ文字列セットに対して多数の検索操作または並べ替え操作を実行する場合は、使用するすべての文字列の並べ替えキーを生成して格納することで、パフォーマンスを向上させることができます。 並べ替え操作または比較操作が必要な場合は、文字列の代わりに並べ替えキーを使用します。 詳細については、SortKey クラスを参照してください。

文字列比較規則を指定しない場合、 Array.Sort(Array) などの並べ替えメソッドは、カルチャに依存する大文字と小文字を区別して文字列に対して並べ替えを実行します。 次の例は、配列内の並べ替えられた文字列の順序に現在のカルチャを変更する方法を示しています。 3 つの文字列の配列が作成されます。 最初に、System.Threading.Thread.CurrentThread.CurrentCulture プロパティを en-US に設定し、Array.Sort(Array) メソッドを呼び出します。 これよって、英語 (米国) カルチャの並べ替え規則に基づく並べ替え順序が適用されます。 次に、System.Threading.Thread.CurrentThread.CurrentCulture プロパティを da-DK に設定し、再度 Array.Sort メソッドを呼び出します。 適用される並べ替え順序が en-US の並べ替え順序と異なる点に注意してください。これは、デンマーク語 (デンマーク) の並べ替え規則が使用されるためです。

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

警告

文字列を比較する主な目的が、文字列が等しいかどうかを判断する場合は、 String.Equals メソッドを呼び出す必要があります。 通常は、 Equals を使用して序数比較を実行する必要があります。 String.Compare メソッドは、主に文字列を並べ替えることを目的としています。

String.StartsWithString.IndexOfなどの文字列検索メソッドでは、カルチャに依存する文字列比較や序数文字列比較を実行することもできます。 次の例は、 IndexOf メソッドを使用した序数とカルチャに依存する比較の違いを示しています。 現在のカルチャが英語 (米国) であるカルチャに依存する検索では、部分文字列 "oe" が合字 "œ" と一致すると見なされます。 ソフト ハイフン (U+00AD) は幅が 0 の文字であるため、検索ではソフト ハイフンが String.Empty と同等として扱われ、文字列の先頭に一致するものが検索されます。 一方、序数検索では、どちらの場合も一致が見つかりません。

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

文字列で検索する

String.StartsWithString.IndexOfなどの文字列検索メソッドでは、カルチャに依存する文字列または序数の文字列比較を実行して、指定した文字列に文字または部分文字列があるかどうかを判断することもできます。

IndexOf メソッドなどの個々の文字を検索するString クラス内の検索メソッド、または IndexOfAny メソッドなどの文字セットの 1 つはすべて、序数検索を実行します。 カルチャに依存する文字の検索を実行するには、CompareInfo.IndexOf(String, Char)CompareInfo.LastIndexOf(String, Char)などのCompareInfo メソッドを呼び出す必要があります。 序数とカルチャに依存する比較を使用して文字を検索した結果は、大きく異なる場合があることに注意してください。 たとえば、合字 "Æ" (U+00C6) などの事前計算済みの Unicode 文字を検索すると、カルチャに応じて、正しいシーケンス ("AE" (U+041U+0045) など) 内のコンポーネントの出現と一致する場合があります。 次の例は、個々の文字を検索するときの String.IndexOf(Char) メソッドと CompareInfo.IndexOf(String, Char) メソッドの違いを示しています。 合字 "æ" (U+00E6) は、en-US カルチャの規則を使用する場合は文字列 "aerial" にありますが、da-DK カルチャの規則を使用する場合や序数比較を実行する場合には見つかりません。

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

一方、 String 文字ではなく文字列を検索するクラス メソッドは、 StringComparison型のパラメーターで検索オプションが明示的に指定されていない場合、カルチャに依存する検索を実行します。 唯一の例外は、序数検索を実行する Containsです。

等しいかどうかをテストする

String.Compare メソッドを使用して、並べ替え順序で 2 つの文字列のリレーションシップを決定します。 通常、これはカルチャに依存する操作です。 これに対し、 String.Equals メソッドを呼び出して等価性をテストします。 等価性のテストでは、通常、ユーザー入力と、有効なユーザー名、パスワード、ファイル システム パスなどの既知の文字列が比較されるため、通常は序数演算です。

警告

String.Compare メソッドを呼び出し、戻り値が 0 であるかどうかを判断することで、等価性をテストできます。 ただし、この方法はお勧めしません。 2 つの文字列が等しいかどうかを判断するには、 String.Equals メソッドのいずれかのオーバーロードを呼び出す必要があります。 どちらのメソッドにも比較の型を明示的に指定するSystem.StringComparison パラメーターが含まれているため、呼び出すオーバーロードはインスタンス Equals(String, StringComparison) メソッドまたは静的Equals(String, String, StringComparison) メソッドです。

次の例は、序数を代わりに使用する必要がある場合に、カルチャに依存する比較を実行する危険性を示しています。 この場合、コードの目的は、URL の先頭と文字列 "FILE://" の大文字と小文字を区別しない比較を実行することで、"FILE://" または "file://" で始まる URL からのファイル システムアクセスを禁止することです。 ただし、"file://" で始まる URL でトルコ語 (トルコ) カルチャを使用してカルチャに依存する比較を実行すると、トルコの大文字の小文字 "i" が "I" ではなく "İ" であるため、等価性の比較は失敗します。 その結果、ファイル システムへのアクセスが誤って許可されます。 一方、序数比較を実行すると、等価性の比較は成功し、ファイル システムのアクセスは拒否されます。

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.

正規化

一部の Unicode 文字には複数の表現があります。 たとえば、次のコード ポイントは、文字 "ắ" を表すことができます。

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

1 つの文字に対して複数の表現を使用すると、検索、並べ替え、照合、およびその他の文字列操作が複雑になります。

Unicode 標準では、同等のバイナリ表現のいずれかに対して Unicode 文字の 1 つのバイナリ表現を返す正規化と呼ばれるプロセスを定義します。 正規化では、さまざまなルールに従う複数のアルゴリズム (正規化フォームと呼ばれます) を使用できます。 .NET では、Unicode 正規化形式 C、D、KC、KD がサポートされています。 文字列が同じ正規化形式に正規化されている場合は、序数比較を使用して比較できます。

序数比較は、各文字列内の対応する Char オブジェクトの Unicode スカラー値のバイナリ比較です。 String クラスには、次のような序数比較を実行できるメソッドが多数含まれています。

String.IsNormalized() メソッドを呼び出して、文字列が正規化形式 C に正規化されているかどうかを判断するか、String.IsNormalized(NormalizationForm) メソッドを呼び出して、文字列が指定された正規化フォームに正規化されているかどうかを判断できます。 String.Normalize() メソッドを呼び出して文字列を正規化形式 C に変換することも、String.Normalize(NormalizationForm) メソッドを呼び出して文字列を指定された正規化形式に変換することもできます。 文字列の正規化と比較の詳細については、 Normalize() メソッドと Normalize(NormalizationForm) メソッドを参照してください。

次の簡単な例は、文字列の正規化を示しています。 3 つの異なる文字列で 3 つの異なる方法で文字 "ố" を定義し、等しいかどうかを序数比較を使用して、各文字列が他の 2 つの文字列と異なることを判断します。 次に、各文字列をサポートされている正規化形式に変換し、指定された正規化形式で各文字列の序数比較を再度実行します。 いずれの場合も、2 番目の等値テストは、文字列が等しいことを示します。

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

正規化フォームと正規化フォームの詳細については、 System.Text.NormalizationForm、および Unicode Standard Annex #15: Unicode 正規化フォーム および unicode.org Web サイトの 正規化に関する FAQ を参照してください。

カテゴリ別の文字列操作

String クラスは、文字列の比較、文字列の等価性のテスト、文字列内の文字または部分文字列の検索、文字列の変更、文字列からの部分文字列の抽出、文字列の結合、値の書式設定、文字列のコピー、および文字列の正規化を行うメンバーを提供します。

文字列の比較

次の String メソッドを使用して、並べ替え順序で文字列を比較して相対位置を決定できます。

  • Compare は、並べ替え順序で 1 つの文字列と 2 番目の文字列の関係を示す整数を返します。

  • CompareOrdinal は、コード ポイントの比較に基づいて、1 つの文字列と 2 番目の文字列の関係を示す整数を返します。

  • CompareTo は、現在の文字列インスタンスと 2 番目の文字列の並べ替え順序の関係を示す整数を返します。 CompareTo(String) メソッドは、String クラスのIComparableおよびIComparable<T>実装を提供します。

文字列の等価性をテストする

Equals メソッドを呼び出して、2 つの文字列が等しいかどうかを判断します。 インスタンス Equals(String, String, StringComparison) と静的 Equals(String, StringComparison) オーバーロードを使用すると、比較がカルチャに依存するか序数であるか、大文字と小文字を区別するか無視するかを指定できます。 等価性に関するほとんどのテストは序数であり、システム リソース (ファイル システム オブジェクトなど) へのアクセスを決定する等価性の比較は常に序数である必要があります。

文字列内の文字を検索する

String クラスには、次の 2 種類の検索メソッドが含まれています。

  • 特定の部分文字列が文字列インスタンスに存在するかどうかを示す Boolean 値を返すメソッド。 これには、 ContainsEndsWith、および StartsWith メソッドが含まれます。

  • 文字列インスタンス内の部分文字列の開始位置を示すメソッド。 これには、 IndexOfIndexOfAnyLastIndexOf、および LastIndexOfAny メソッドが含まれます。

警告

特定の部分文字列ではなく特定のパターンの文字列を検索する場合は、正規表現を使用する必要があります。 詳細については、「 .NET 正規表現」を参照してください。

文字列を変更する

String クラスには、文字列の値を変更するように見える次のメソッドが含まれています。

  • Insert は、現在の String インスタンスに文字列を挿入します。

  • PadLeft は、文字列の先頭に指定した文字の 1 つ以上の出現箇所を挿入します。

  • PadRight は、指定した文字の 1 つ以上の出現箇所を文字列の末尾に挿入します。

  • Remove は、現在の String インスタンスから部分文字列を削除します。

  • Replace は、部分文字列を現在の String インスタンス内の別の部分文字列に置き換えます。

  • ToLowerToLowerInvariant文字列内のすべての文字を小文字に変換します。

  • ToUpperToUpperInvariant文字列内のすべての文字を大文字に変換します。

  • Trim は、文字列の先頭と末尾から文字のすべての出現箇所を削除します。

  • TrimEnd は、文字列の末尾から文字のすべての出現箇所を削除します。

  • TrimStart は、文字列の先頭から文字のすべての出現箇所を削除します。

重要

すべての文字列変更メソッドは、新しい String オブジェクトを返します。 現在のインスタンスの値は変更されません。

文字列から部分文字列を抽出する

String.Split メソッドは、1 つの文字列を複数の文字列に分割します。 メソッドのオーバーロードを使用すると、複数の区切り記号を指定して、メソッドが抽出する部分文字列の数を制限したり、部分文字列から空白をトリミングしたり、返される文字列に空の文字列 (区切り記号が隣接する場合に発生する文字列) を含めるかどうかを指定したりできます。

文字列を結合する

文字列連結には、次の String メソッドを使用できます。

  • Concat は、1 つ以上の部分文字列を 1 つの文字列に結合します。
  • Join は、1 つ以上の部分文字列を 1 つの要素に連結し、各部分文字列の間に区切り記号を追加します。

値の書式設定

String.Formatメソッドは、複合書式設定機能を使用して、文字列内の 1 つ以上のプレースホルダーを、オブジェクトまたは値の文字列表現に置き換えます。 Formatメソッドは、多くの場合、次の操作を行うために使用されます。

  • 数値の文字列形式を文字列に埋め込む。
  • 日付と時刻の値の文字列形式を文字列に埋め込む。
  • 列挙値の文字列表現を文字列に埋め込む。
  • IFormattable インターフェイスをサポートするオブジェクトの文字列形式を文字列に埋め込む。
  • 大きな文字列内のフィールド内の部分文字列を右揃えまたは左揃えする。

書式設定操作と例の詳細については、 Format オーバーロードの概要を参照してください。

文字列をコピーします。

次の String メソッドを呼び出して、文字列のコピーを作成できます。

  • Clone は、既存の String オブジェクトへの参照を返します。
  • CopyTo は文字列の一部を文字配列にコピーします。

文字列を正規化する

Unicode では、1 つの文字に複数のコード ポイントを含めることができます。 正規化により、これらの同等の文字が同じバイナリ表現に変換されます。 String.Normalizeメソッドは正規化を実行し、String.IsNormalizedメソッドは文字列が正規化されているかどうかを判断します。

詳細と例については、この記事の前の「 正規化 」セクションを参照してください。