方法: C# で文字列を比較する

文字列を比較して、次の 2 つの質問のいずれかに回答します。"これら 2 つの文字列は等しいですか" または "これらを並べ替えるときにどのような順序でこれらの文字列を配置しますか"

これら 2 つの質問は、文字列比較に影響する要因によって複雑になります。

  • 序数に基づく比較または言語的な比較を選択することができます。
  • 大文字小文字が重要かどうかを選択できます。
  • カルチャに固有の比較を選択できます。
  • 言語的な比較は、カルチャおよびプラットフォームに依存します。

System.StringComparison 列挙フィールドは、これらの選択を表します。

  • CurrentCulture: カルチャを区別する並べ替え規則と現在のカルチャを使用して文字列を比較します。
  • CurrentCultureIgnoreCase: カルチャを区別する並べ替え規則と現在のカルチャを使用し、比較対象の文字列の大文字と小文字の区別を無視して文字列を比較します。
  • InvariantCulture: カルチャを区別する並べ替え規則とインバリアント カルチャを使用して文字列を比較します。
  • InvariantCultureIgnoreCase: カルチャを区別する並べ替え規則とインバリアント カルチャを使用し、比較対象の文字列の大文字と小文字の区別を無視して文字列を比較します。
  • Ordinal: 序数 (バイナリ) の並べ替え規則を使用して文字列を比較します。
  • OrdinalIgnoreCase: 序数 (バイナリ) の並べ替え規則を使用し、比較対象の文字列の大文字と小文字の区別を無視して文字列を比較します。

Note

この記事の C# 例は、Try.NET インライン コード ランナーとプレイグラウンドで実行されます。 [実行] ボタンを選択すると、対話型ウィンドウで例が実行されます。 コードを実行したら、コードを変更し、 [実行] をもう一度選択して変更後のコードを実行できます。 変更後のコードが対話型ウィンドウで実行されるか、コンパイルできなかった場合、対話型ウィンドウにすべての C# コンパイラ エラー メッセージが表示されます。

文字列を比較するときに、それらの間で順序を定義します。 比較は、文字列のシーケンスの並べ替えに使用されます。 シーケンスが既知の順序になると、ソフトウェアと人間のどちらにとっても検索が容易になります。 他にも、文字列が同じであるかどうかを確認するために比較を行う場合もあります。 この同一チェックは等価比較と似ていますが、大文字と小文字の違いなど、一部の違いは無視される場合があります。

既定の序数の比較

既定では、最も一般的な操作は次のとおりです。

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

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

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

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

既定の序数の比較では、文字列を比較するときに言語の規則を考慮しません。 2 つの文字列のそれぞれの Char オブジェクトのバイナリ値を比較します。 その結果、既定の序数の比較でも大文字と小文字が区別されます。

String.Equals== および != 演算子を使用した等価性のテストは、String.CompareToCompare(String, String) のメソッドを使用した文字列の比較とは異なります。 いずれの場合も、大文字と小文字を区別した比較が実行されます。 ただし、等価性のテストを使用すると、序数に基づく比較が実行されますが、CompareTo および Compareメソッドを使用すると、現在のカルチャを使用したカルチャ対応の言語比較が実行されます。 実行する比較の種類を明示的に指定するオーバーロードを呼び出して、コードの意図を明確にします。

大文字と小文字を区別しない、序数に基づく比較

String.Equals(String, StringComparison) メソッドでは、StringComparison.OrdinalIgnoreCaseStringComparison 値を指定して、大文字と小文字を区別しない序数に基づく比較を指定できます。 引数 StringComparison に値 StringComparison.OrdinalIgnoreCase を指定した場合、大文字と小文字を区別しない序数比較を実行する静的な String.Compare(String, String, StringComparison) メソッドもあります。 これらの比較を次のコードに示します。

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

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

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

これらのメソッドでは、大文字と小文字を区別しない比較を実行する場合、インバリアント カルチャの大文字と小文字の区別の規則が使用されます。

言語的な比較

多くの文字列比較メソッド (String.StartsWith など) では、既定で "現在のカルチャ" の言語ルールを使用し、入力に順序を付けます。 この言語的な比較は、"単語の並べ替え順序" と呼ばれる場合もあります。言語的な比較を実行するときには、一部の英数字以外の Unicode 文字に、特別な重みが割り当てられる場合があります。 たとえば、"co-op" と "coop" が並べ替え順序で隣接して出現するように、ハイフン "-" に小さな重みが割り当てられる場合があります。 一部の非印刷制御文字は無視される場合があります。 さらに、一部の Unicode 文字は、Char インスタンスのシーケンスと等しくなる場合があります。 次の例では、ドイツ語で "They dance in the street" という語句を一方の文字列では "ss" (U+0073 U+0073)、もう一方の文字列では 'ß' (U+00DF) を使って表しています。 言語的に (Windows では)、"en-US" と "de-DE" の両方のカルチャで、"ss" はドイツ語の Essetz: 'ß' 文字と同じです。

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

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

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

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

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

Windows 上の.NET 5 より前のバージョンにおいては、言語的な比較から序数に基づく比較に変更した場合に、"cop"、"coop"、および "co-op" の並べ替え順序が変更されます。 ドイツ語の 2 つの文も、異なる比較の種類を使用して異なる方法で比較されます。 .NET 5 より前は、.NET グローバリゼーション API で各国語サポート (NLS) ライブラリが使用されていました。 .NET 5 以降のバージョンでは、.NET グローバル API で International Components for Unicode (ICU) が使用されます。これにより、サポートされているすべてのオペレーティング システムで .NET のグローバリゼーション動作が統合されます。

特定のカルチャを使用した比較

次の例では、en-US と de-DE のカルチャの CultureInfo オブジェクトを格納します。 比較は、カルチャ固有の比較を確保するために CultureInfo オブジェクトを使用して実行されます。 使用されるカルチャは言語的な比較に影響します。 次の例は、"en-US" カルチャと "de-DE" カルチャを使用する 2 つのドイツ語の文の比較の結果を示しています。

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

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

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

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

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

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

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

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

カルチャに依存した比較は、一般的にユーザーによる文字列入力とユーザーによる他の文字列入力の比較および並べ替えに使用されます。 これらの文字列の文字および並べ替え規則は、ユーザーのコンピューターのロケールによって異なる場合があります。 同一の文字を含む文字列でも、現在のスレッドのカルチャに応じて、異なる方法で並べ替えられます。

配列の言語的な並べ替えと文字列の検索

次の例は、現在のカルチャに依存する言語的な比較を使用して、配列内の文字列を並べ替えおよび検索する方法を示しています。 System.StringComparer パラメーターを取得する静的な Array メソッドを使用します。

次の例は、現在のカルチャを使用して文字列の配列を並べ替える方法を示しています。

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

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

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

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

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

配列が並べ替えられた後は、バイナリ検索を使用してエントリを検索できます。 バイナリ検索は、コレクションの中央で開始され、要求された文字列がコレクションのどちらの半分に含まれているかを判断します。 各後続の比較は、半分にコレクションの残りの部分を半分に細分化します。 配列は StringComparer.CurrentCulture を使用して並べ替えられます。 ローカル関数 ShowWhere は、文字列が見つかった場所に関する情報を表示します。 文字列が見つからなかった場合、返された値は、見つからなかった場合にどこにあるべきかを示します。

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

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

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

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

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

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

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

コレクションの序数に基づく並べ替えと検索

次のコードでは、System.Collections.Generic.List<T> コレクション クラスを使用して文字列を格納します。 文字列は、List<T>.Sort メソッドを使用して並べ替えられます。 このメソッドでは、2 つの文字列の順序を比較するデリゲートが必要です。 String.CompareTo メソッドは、その比較関数を提供します。 このサンプルを実行し、順序を確認します。 この並べ替え操作では、大文字小文字を区別する序数の並べ替えを使用します。 静的な String.Compare メソッドを使用し、別の比較規則を指定します。

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

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

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

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

並べ替えられたら、バイナリ検索を使用して文字列の一覧を検索できます。 次の例では、同じ比較関数を使用して、並べ替えられた一覧を検索する方法を示します。 ローカル関数 ShowWhere は、要求されたテキストの場所、またはあるべき場所を示します。

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

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

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

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

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

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

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

並べ替えと検索に常に同じ種類の比較を使用してください。 並べ替えと検索に異なる種類の比較を使用すると、予期しない結果になります。

System.Collections.HashtableSystem.Collections.Generic.Dictionary<TKey,TValue>、および System.Collections.Generic.List<T> などのコレクション クラスには、要素またはキーの種類が string の場合、System.StringComparer パラメーターを取るコンストラクターが用意されています。 通常は、これらのコンストラクターをできるだけ使用し、StringComparer.Ordinal または StringComparer.OrdinalIgnoreCase を指定する必要があります。

関連項目