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

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

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

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

注意

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

文字列を比較するときに、それらの間で順序を定義します。 比較は、文字列のシーケンスの並べ替えに使用されます。 シーケンスが既知の順序の場合、ソフトウェアと人間の両方が簡単に検索できます。 その他の比較では、文字列が等しいかどうかを確認する場合があります。 これらの類似性チェックは等値に似ていますが、大文字小文字の違いなど、いくつかの違いは無視される場合があります。

既定の序数の比較

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

大文字と小文字を区別した序数の比較が実行されます。 String.Equals の場合は、StringComparison 引数を指定することで、並べ替え規則を変更できます。 次に例を示します。

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");

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

言語的な比較

現在のカルチャの言語の規則を使用して文字列の順序を指定することもできます。 これは、"単語の並べ替え順序" と呼ばれることもあります。言語的な比較を実行するときには、一部の英数字以外の Unicode 文字に、特別な重みが割り当てられる場合があります。 たとえば、ハイフン ("-") に割り当てられる重みは小さいため、並べ替え順序で "coop" と "co-op" の出現位置が隣接します。 さらに、いくつかの 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 を指定する必要があります。

参照の等価性と文字列インターン

どの例でも ReferenceEquals を使用していません。 このメソッドによって、2 つの文字列が同じオブジェクトであるかどうかが判断されます。異なる場合、文字列比較で結果が一致しません。 次の例は、C# の文字列のインターン機能を示しています。 プログラムで 2 つ以上の同じ文字列変数を宣言すると、コンパイラはそれらをすべて同じ場所に保管します。 ReferenceEquals メソッドを呼び出すと、2 つの文字列がメモリ内の同じオブジェクトを実際に参照していることを確認できます。 インターン処理を回避するには、String.Copy メソッドを使用します。 コピーが行われた後、同じ値が含まれていても、2 つの文字列は別の記憶場所を使用します。 次の例を実行し、文字列 abインターン処理されることを示します。これは、同じ記憶域を共有することを意味します。 文字列 ac は異なります。

string a = "The computer ate my source code.";
string b = "The computer ate my source code.";

if (String.ReferenceEquals(a, b))
    Console.WriteLine("a and b are interned.");
else
    Console.WriteLine("a and b are not interned.");

string c = String.Copy(a);

if (String.ReferenceEquals(a, c))
    Console.WriteLine("a and c are interned.");
else
    Console.WriteLine("a and c are not interned.");

注意

文字列の等価をテストする場合、実行する比較の種類を明示的に指定するメソッドを使用する必要があります。 コードを保守しやすく、読みやすくすることができます。 StringComparison 列挙パラメーターを取る System.String および System.Array クラスのメソッドのオーバーロードを使用します。 実行する比較の種類を指定します。 等価をテストするときには、== および != 演算子を使用しないでください。 String.CompareTo インスタンス メソッドは常に、序数に基づいた大文字小文字を区別する比較を実行します。 これらは、文字列をアルファベット順に並べ替える場合に適しています。

String.Intern メソッドを呼び出すことで、文字列をインターンしたり、既存のインターンされた文字列への参照を取得したりできます。 文字列がインターンされているかどうかを確認するには、String.IsInterned メソッドを呼び出します。

関連項目