如何:在 C# 比較字串

您可以比較字串來回答以下其中一個問題:「這兩個字串相等嗎?」或「這些字串在排序時應該以何種順序放置?」

這兩個問題會因為影響字串比較的因素而變複雜:

  • 您可以選擇序數或語言比較。
  • 您可以選擇大小寫是否重要。
  • 您可以選擇文化特性 (Culture) 特定的比較。
  • 語言比較視文化特性 (Culture) 與平台而異。

System.StringComparison列舉欄位代表下列選項:

  • CurrentCulture:使用區分文化特性的排序規則和目前文化特性來比較字串。
  • CurrentCultureIgnoreCase:使用區分文化特性的排序規則比較字串、目前的文化特性,以及忽略所比較字串串的案例。
  • InvariantCulture:使用區分文化特性的排序規則和不因文化特性而異的文化特性來比較字串。
  • InvariantCultureIgnoreCase:使用區分文化特性的排序規則、不因文化特性而異的文化特性比較字串,以及忽略所比較字元串的案例。
  • 序數:使用序數(二進位)排序規則比較字串。
  • OrdinalIgnoreCase:使用序數 (binary) 排序規則比較字串,並忽略所比較字串的案例。

注意

本文中的 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")}");

比較字串時,預設序數比較不會考慮語言規則。 其會在兩個字串中比較各 Char 物件的二進位值。 如此一來,預設序數比較也會區分大小寫。

使用 String.Equals==!= 運算子測試是否相等與使用 String.CompareToCompare(String, String) 方法比較字串不同。 其全都會執行區分大小寫的比較。 不過,雖然相等測試會執行序數比較,但 CompareToCompare 方法會使用目前的文化特性執行文化特性感知語言比較。 藉由呼叫明確指定要執行的比較類型的多載,讓程式代碼的意圖清楚。

不區分大小寫的序數比較

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 字元可能會指派特殊權數。 例如,連字元 「-」 可能會指派一個小權數,讓 「共同作業」和「coop」依排序順序出現在彼此旁邊。 可能會忽略某些非列印控制字元。 此外,某些 Unicode 字元可能相當於實例序列 Char 。 以下使用德文「他們在街道上跳舞」為例,並在其中一個字串使用 "ss" (U+0073 U+0073),而另一個則使用 'ß' (U+00DF)。 在語言方面 (在 Windows 中),"ss" 等於 "en-US" 和 "de-DE" 文化特性中的德文 Esszet: 'ß' 字元。

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" 的排序次序會在您從語言比較變更為序數比較時進行變更。 兩個德文句子使用不同的比較型別時,比較起來也會不同。 在 .NET 5 之前,.NET 全球化 API 使用 國家語言支援 (NLS) 連結庫。 在 .NET 5 與更新版本中,.NET 全球化 API 使用 Unicode 國際元件 (ICU) 程式庫,這會統一所有受支援作業系統上的 .NET 全球化行為。

使用特定文化特性的比較

下列範例會 CultureInfo 儲存 en-US 和 de-DE 文化特性的物件。 比較是使用 CultureInfo 物件執行,以確保文化特性 (Culture) 特定的比較。 使用的文化特性會影響語言比較。 下列範例會顯示使用 "en-US" 文化特性和 "de-DE" 文化特性比較兩個德文句子的結果:

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 方法排序。 這個方法需要可比較和排序兩個字串的委派。 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}.");
    }
}

請務必使用相同型別的比較來排序和搜尋。 使用不同的比較型別來排序和搜尋,會產生非預期的結果。

項目或索引鍵的類型為 string 時,System.Collections.HashtableSystem.Collections.Generic.Dictionary<TKey,TValue>System.Collections.Generic.List<T> 這類集合類別具有接受 System.StringComparer 參數的建構函式。 通常應該盡可能使用這些建構函式,並指定 StringComparer.OrdinalStringComparer.OrdinalIgnoreCase

另請參閱