.NET中字符串比較的最佳用法
.NET 為開發(fā)本地化和全球化應用程序提供廣泛支持,在執(zhí)行排序和顯示字符串等常見操作時,輕松應用當前區(qū)域性或特定區(qū)域性的約定。 但排序或比較字符串并不總是區(qū)分區(qū)域性的操作。
例如,對于應用程序內部使用的字符串,通常應該跨所有區(qū)域性以相同的方式對其進行處理。 如果將 XML 標記、HTML 標記、用戶名、文件路徑和系統對象名稱等與區(qū)域性無關的字符串數據解釋為區(qū)分區(qū)域性,則應用程序代碼會遭遇細微的錯誤、不佳的性能,在某些情況下,還會遭遇安全性問題。
本文介紹 .NET 中的字符串排序、比較和大小寫方法,針對如何選擇適當的字符串處理方法提出建議,并提供有關字符串處理方法的其他信息。
對字符串用法的建議
使用 .NET 進行開發(fā)時,請遵循以下簡要建議比較字符串:
- 使用為字符串操作顯式指定字符串比較規(guī)則的重載。 通常情況下,這涉及調用具有 StringComparison類型的參數的方法重載。
- 使用 StringComparison.Ordinal 或 StringComparison.OrdinalIgnoreCase 進行比較,并以此作為匹配區(qū)域性不明確的字符串的安全默認設置。
- 將比較與 StringComparison.Ordinal 或 StringComparison.OrdinalIgnoreCase 配合使用,以獲得更好的性能。
- 向用戶顯示輸出時,使用基于 StringComparison.CurrentCulture 的字符串操作。
- 當進行與語言(例如,符號)無關的比較時,使用非語言的 StringComparison.Ordinal 或 StringComparison.OrdinalIgnoreCase 值,而不使用基于 CultureInfo.InvariantCulture 的字符串操作。
- 在規(guī)范化要比較的字符串時,使用 String.ToUpperInvariant 方法而非 String.ToLowerInvariant 方法。
- 使用 String.Equals 方法的重載來測試兩個字符串是否相等。
- 使用 String.Compare 和 String.CompareTo 方法可對字符串進行排序,而不是檢查字符串是否相等。
- 在用戶界面,使用區(qū)分區(qū)域性的格式顯示非字符串數據,如數字和日期。 使用格式以固定區(qū)域性使非字符串數據顯示為字符串形式。
比較字符串時,請避免采用以下做法:
- 不要使用未顯式或隱式為字符串操作指定字符串比較規(guī)則的重載。
- 在大多數情況下,不要使用基于 StringComparison.InvariantCulture 的字符串操作。 其中的一個少數例外情況是,保存在語言上有意義但區(qū)域性不明確的數據。
- 不要使用 String.Compare 或 CompareTo 方法的重載和用于確定兩個字符串是否相等的返回值為 0 的測試。
顯式指定字符串比較
重載 .NET 中大部分字符串操作方法。 通常,一個或多個重載會接受默認設置,然而其他重載則不接受默認設置,而是定義比較或操作字符串的精確方式。 大多數不依賴于默認設置的方法都包括 StringComparison類型的參數,該參數是按區(qū)域性和大小寫為字符串比較顯式指定規(guī)則的枚舉。 下表描述StringComparison 枚舉成員。
| StringComparison 成員 | 描述 |
|---|---|
| CurrentCulture | 使用當前區(qū)域性執(zhí)行區(qū)分大小寫的比較。 |
| CurrentCultureIgnoreCase | 使用當前區(qū)域性執(zhí)行不區(qū)分大小寫的比較。 |
| InvariantCulture | 使用固定區(qū)域性執(zhí)行區(qū)分大小寫的比較。 |
| InvariantCultureIgnoreCase | 使用固定區(qū)域性執(zhí)行不區(qū)分大小寫的比較。 |
| Ordinal | 執(zhí)行序號比較。 |
| OrdinalIgnoreCase | 執(zhí)行不區(qū)分大小寫的序號比較。 |
例如, IndexOf 方法(它返回 String 對象中與某字符或字符串匹配的子字符串的索引)具有九種重載:
- 默認情況下,IndexOf(Char), IndexOf(Char, Int32)和 IndexOf(Char, Int32, Int32)對字符串中的字符執(zhí)行序號(區(qū)分大小寫但不區(qū)分區(qū)域性的)搜索。
- 默認情況下,IndexOf(String), IndexOf(String, Int32)和 IndexOf(String, Int32, Int32)對字符串中的子字符串執(zhí)行區(qū)分大小寫且區(qū)分區(qū)域性的搜索。
- IndexOf(String, StringComparison)、 IndexOf(String, Int32, StringComparison)和 IndexOf(String, Int32, Int32, StringComparison),其中包括 StringComparison 類型的參數,該類型允許指定比較形式。
我們建議選擇不使用默認值的重載,原因如下:
- 具有默認參數的一些重載(在字符串實例中搜索 Char 的重載)執(zhí)行序號比較,而其他重載(在字符串實例中搜索字符串的重載)執(zhí)行的是區(qū)分區(qū)域性的比較。 要記住哪種方法使用哪個默認值并非易事,并很容易混淆重載。
- 依賴于方法調用默認值的代碼的意圖并不清楚。 在下面依賴于默認值的示例中,很難了解開發(fā)人員對兩個字符串的實際意圖是執(zhí)行序號比較還是語言比較,或者
protocol和“http”之間存在的大小寫差異是否會導致相等性測試返回false類型的參數的方法重載。
string protocol = GetProtocol(url);
if (String.Equals(protocol, "http")) {
// ...Code to handle HTTP protocol.
}
else {
throw new InvalidOperationException();
}一般情況下,我們建議調用不依賴于默認設置的方法,因為這會明確代碼的意圖。 這進而使代碼更具可讀性且更易于調試和維護。 下面的示例解決了前面示例中提出的問題。 使用序號比較并且忽略大小寫差異。
string protocol = GetProtocol(url);
if (String.Equals(protocol, "http", StringComparison.OrdinalIgnoreCase)) {
// ...Code to handle HTTP protocol.
}
else {
throw new InvalidOperationException();
}字符串比較的詳細信息
字符串比較是許多字符串相關操作的核心,特別是排序和相等性測試操作。 字符串以確定的順序進行排序:如果在排序的字符串列表中,“my”出現在“string”之前,則“my”必定小于或等于“string”。 此外,比較可隱式確定相等性。 對于認為是相等的字符串,比較操作將返回零。 對此很好的解釋是兩個字符串都不小于對方。 涉及到字符串的最有意義的操作包括這些步驟中的一個或兩個步驟:與另一個字符串進行比較和執(zhí)行明確的排序操作。
[!NOTE]
可以下載排序權重表,這是一組文本文件,其中包含有關 Windows 操作系統排序和比較操作中所使用的字符權重的信息,也可以下載默認 Unicode 排序元素表,這是適用于 Linux 和 macOS 的最新版排序權重表。 Linux 和 macOS 上的特定排序權重表版本取決于系統上安裝的 International Components for Unicode 庫的版本。 有關 ICU 版本及它們所實現的 Unicode 版本的信息,請參閱下載 ICU。
但是,評估兩個字符串的相等性或排序順序不會生成一個正確的結果;其結果取決于用于比較這兩個字符串的條件。 特別是,序號或基于當前區(qū)域性或固定區(qū)域性(基于英語語言的區(qū)域設置不明確的區(qū)域性)的大小寫和排序約定的字符串比較可能會產生不同的結果。
此外,使用不同 .NET 版本或在不同操作系統或不同的操作系統版本上使用 .NET 進行字符串比較時,返回的結果可能不同。 有關詳細信息,請參閱字符串和 Unicode 標準。
使用當前區(qū)域性的字符串比較
一個條件涉及在比較字符串時使用當前區(qū)域性的約定。 基于當前區(qū)域性的比較使用線程的當前區(qū)域性或區(qū)域設置。 如果用戶未設置該區(qū)域性,則默認為“控制面板”中“區(qū)域選項” 窗口中的設置。 當數據與語言相關并反映區(qū)分區(qū)域性的用戶交互時,應始終使用基于當前區(qū)域性的比較。
但是,當區(qū)域性發(fā)生更改時,.NET 中的比較和大小寫行為也發(fā)生更改。 如果執(zhí)行應用程序的計算機與用于開發(fā)該應用程序的計算機具有不同的區(qū)域性,或者執(zhí)行線程改變它的區(qū)域性,則會發(fā)生這種情況。 此行為是有意而為之的,但許多開發(fā)人員不易察覺此行為。 下面的示例說明了美國英語(“en-US”)與瑞典語(“sv-SE”)區(qū)域性在排序順序中的差異。 請注意,單詞“ångström”、“Windows”和“Visual Studio”將出現在已排序的字符串數組的不同位置。
using System;
using System.Globalization;
using System.Threading;
public class Example
{
public static void Main()
{
string[] values= { "able", "?ngstr?m", "apple", "?ble",
"Windows", "Visual Studio" };
Array.Sort(values);
DisplayArray(values);
// Change culture to Swedish (Sweden).
string originalCulture = CultureInfo.CurrentCulture.Name;
Thread.CurrentThread.CurrentCulture = new CultureInfo("sv-SE");
Array.Sort(values);
DisplayArray(values);
// Restore the original culture.
Thread.CurrentThread.CurrentCulture = new CultureInfo(originalCulture);
}
private static void DisplayArray(string[] values)
{
Console.WriteLine("Sorting using the {0} culture:",
CultureInfo.CurrentCulture.Name);
foreach (string value in values)
Console.WriteLine(" {0}", value);
Console.WriteLine();
}
}
// The example displays the following output:
// Sorting using the en-US culture:
// able
// ?ble
// ?ngstr?m
// apple
// Visual Studio
// Windows
//
// Sorting using the sv-SE culture:
// able
// ?ble
// apple
// Windows
// Visual Studio
// ?ngstr?m使用當前區(qū)域性的不區(qū)分大小寫比較和區(qū)分區(qū)域性的比較是相同的,只不過前者忽略由線程的當前區(qū)域性指示的大小寫。 這種情況也可表明它的排序順序。
以下方法默認利用使用當前區(qū)域性語義的比較:
- 不包括String.Compare 參數的 StringComparison 重載。
- String.CompareTo 重載。
- 默認 String.StartsWith(String) 方法和具有 String.StartsWith(String, Boolean, CultureInfo) null nullCultureInfo 重載。
- 默認 String.EndsWith(String) 方法和需要使用 nullCultureInfo 參數的 String.EndsWith(String, Boolean, CultureInfo) 方法。
- 接受String.IndexOf 作為搜索參數且不包含 String 參數的 StringComparison 重載。
- 接受String.LastIndexOf 作為搜索參數且不包含 String 參數的 StringComparison 重載。
總之,我們建議調用具有 <xref:System.StringComparison> 參數的重載,以便明確方法調用的意圖。
當從語言角度解釋非語言的字符串數據,或利用其他區(qū)域性的約定解釋某個特定區(qū)域性中的字符串時,則會發(fā)生或大或小的錯誤。 土耳其語 I 問題便是一個規(guī)范示例。
對于幾乎所有拉丁字母來講(包括美國英語),字符“i”(\u0069) 是字符“I”(\u0049) 的小寫形式。 此大小寫規(guī)則快速成為在此類區(qū)域性中編程的人員的默認設置。 但是,土耳其語(“tr-TR”)字母表中包含一個“帶有點的 I”的字符“?”(\u0130),該字符是“i”的大寫形式。 土耳其語還包括一個小寫“不帶點的 i”字符,即為“?”(\u0131),該字符的大寫形式為“I”。 阿塞拜疆語(“az”)區(qū)域也會出現這種情況。
因此,關于將“i”變?yōu)榇髮懟驅?ldquo;I”變?yōu)樾懙募僭O并非在所有區(qū)域性中都是有效的。 如果為字符串比較例程使用默認重載,則它們可能會因區(qū)域性不同而異。 如果對非語言的數據進行比較,使用默認重載會產生不良后果,如以下對字符串“file”和“FILE”執(zhí)行不區(qū)分大小寫的比較嘗試所示。
using System;
using System.Globalization;
using System.Threading;
public class Example
{
public static void Main()
{
string fileUrl = "file";
Thread.CurrentThread.CurrentCulture = new CultureInfo("en-US");
Console.WriteLine("Culture = {0}",
Thread.CurrentThread.CurrentCulture.DisplayName);
Console.WriteLine("(file == FILE) = {0}",
fileUrl.StartsWith("FILE", true, null));
Console.WriteLine();
Thread.CurrentThread.CurrentCulture = new CultureInfo("tr-TR");
Console.WriteLine("Culture = {0}",
Thread.CurrentThread.CurrentCulture.DisplayName);
Console.WriteLine("(file == FILE) = {0}",
fileUrl.StartsWith("FILE", true, null));
}
}
// The example displays the following output:
// Culture = English (United States)
// (file == FILE) = True
//
// Culture = Turkish (Turkey)
// (file == FILE) = False如果無意中在安全敏感設置中使用了區(qū)域性,則此比較會導致發(fā)生重大問題,如以下示例所示。 如果當前區(qū)域性為美國英語,則 IsFileURI("file:") 等方法調用將返回 true;但如果當前區(qū)域性為土耳其語,則將返回 false。 因此,在土耳其語系統中,有人可能會避開阻止訪問以“FILE:”開頭的不區(qū)分大小寫的安全措施。
public static bool IsFileURI(String path)
{
return path.StartsWith("FILE:", true, null);
}在這種情況下,由于“file:”會被解釋為非語言的、不區(qū)分區(qū)域性的標識符,因此,應按照下面的示例所示編寫代碼:
public static bool IsFileURI(string path)
{
return path.StartsWith("FILE:", StringComparison.OrdinalIgnoreCase);
}序號字符串操作
在方法調用中指定 StringComparison.Ordinal 或 StringComparison.OrdinalIgnoreCase 值表示非語言比較,這種比較忽略了自然語言的特性。 利用 StringComparison 值調用的方法將字符串操作決策建立在簡單的字節(jié)比較的基礎之上,而不是按區(qū)域性參數化的大小寫或相等表。 在大多數情況下,這種方法最符合字符串的預期解釋,并使代碼更快更可靠。
序號比較就是字符串比較,在這種比較中,將比較每個字符串中的每個字節(jié)且不進行語言解釋;例如,“windows”不匹配“Windows”。 實質上,這是對 C 運行時 strcmp 函數的調用。 當上下文指示應完全匹配字符串或要求保守匹配策略時,請使用這種比較。 此外,序號比較是最快的比較操作,因為它在確定結果時不應用任何語言規(guī)則。
.NET 中的字符串可以包括嵌入的空字符。 序號比較與區(qū)分區(qū)域性的比較(包括使用固定區(qū)域性的比較)之間最明顯的區(qū)別之一是對字符串中嵌入的空字符的處理方式。 當使用 String.Compare 和 String.Equals 方法執(zhí)行區(qū)分區(qū)域性的比較(包括使用固定區(qū)域性的比較)時,將忽略這些字符。 因此,在區(qū)分區(qū)域性的比較中,包含嵌入的空字符的字符串可視為等于不包含空字符的字符串。
[!IMPORTANT]
盡管字符串比較方法忽略嵌入的空字符,但是 String.Contains、 String.EndsWith、 String.IndexOf、 String.LastIndexOf和 String.StartsWith 等字符串搜索方法并不會忽略這些字符。
下面的示例對字符串“Aa”與在“A”和“a”之間嵌入了多個空字符的相似字符串進行區(qū)分區(qū)域性的比較,并顯示如何將這兩個字符串視為相等的字符串:
using System;
public class Example
{
public static void Main()
{
string str1 = "Aa";
string str2 = "A" + new String('\u0000', 3) + "a";
Console.WriteLine("Comparing '{0}' ({1}) and '{2}' ({3}):",
str1, ShowBytes(str1), str2, ShowBytes(str2));
Console.WriteLine(" With String.Compare:");
Console.WriteLine(" Current Culture: {0}",
String.Compare(str1, str2, StringComparison.CurrentCulture));
Console.WriteLine(" Invariant Culture: {0}",
String.Compare(str1, str2, StringComparison.InvariantCulture));
Console.WriteLine(" With String.Equals:");
Console.WriteLine(" Current Culture: {0}",
String.Equals(str1, str2, StringComparison.CurrentCulture));
Console.WriteLine(" Invariant Culture: {0}",
String.Equals(str1, str2, StringComparison.InvariantCulture));
}
private static string ShowBytes(string str)
{
string hexString = String.Empty;
for (int ctr = 0; ctr < str.Length; ctr++)
{
string result = String.Empty;
result = Convert.ToInt32(str[ctr]).ToString("X4");
result = " " + result.Substring(0,2) + " " + result.Substring(2, 2);
hexString += result;
}
return hexString.Trim();
}
}
// The example displays the following output:
// Comparing 'Aa' (00 41 00 61) and 'A a' (00 41 00 00 00 00 00 00 00 61):
// With String.Compare:
// Current Culture: 0
// Invariant Culture: 0
// With String.Equals:
// Current Culture: True
// Invariant Culture: True但是,當使用序號比較時,這兩個字符串不會視為相等,如下面的示例所示:
Console.WriteLine("Comparing '{0}' ({1}) and '{2}' ({3}):",
str1, ShowBytes(str1), str2, ShowBytes(str2));
Console.WriteLine(" With String.Compare:");
Console.WriteLine(" Ordinal: {0}",
String.Compare(str1, str2, StringComparison.Ordinal));
Console.WriteLine(" With String.Equals:");
Console.WriteLine(" Ordinal: {0}",
String.Equals(str1, str2, StringComparison.Ordinal));
// The example displays the following output:
// Comparing 'Aa' (00 41 00 61) and 'A a' (00 41 00 00 00 00 00 00 00 61):
// With String.Compare:
// Ordinal: 97
// With String.Equals:
// Ordinal: False不區(qū)分大小寫的序號比較是第二種最保守的方法。 這些比較會忽略大多數的大小寫;例如,“windows”會匹配“Windows”。 在處理 ASCII 字符時,此策略等同于 StringComparison.Ordinal,只不過它會忽略常用的 ASCII 大小寫。 因此,[A, Z] (\u0041-\u005A) 中的任何字符都會匹配 [a,z] (\u0061-\007A) 中的相應字符。 超出 ASCII 范圍的大小寫使用固定區(qū)域性的表。 因此,下面的比較:
String.Compare(strA, strB, StringComparison.OrdinalIgnoreCase);
等效于(但會更快)這種比較:
String.Compare(strA.ToUpperInvariant(), strB.ToUpperInvariant(),
StringComparison.Ordinal);這些比較仍非???。
StringComparison.Ordinal 和 StringComparison.OrdinalIgnoreCase 均直接使用二進制值并最適合匹配。 當不確定比較設置時,請使用這兩個值中的其中一個。 不過,由于它們執(zhí)行逐字節(jié)比較,因此不會按照語言排序順序(如英語詞典)進行排序,而是按照二進制排序順序。 如果向用戶顯示結果,則在大多數上下文中結果都看上去不正常。
序號語義是不包括 String.Equals 參數(包括相等運算符)的 StringComparison 重載的默認項。 總之,我們建議調用具有 StringComparison 參數的重載。
使用固定區(qū)域性的字符串操作
具有固定區(qū)域性的比較使用由靜態(tài) CompareInfo 屬性返回的 CultureInfo.InvariantCulture 屬性。 此行為在所有系統中都相同;它會將其范圍外的任何字符轉換為其認為等效的固定字符。 此策略對于在各個區(qū)域性中維護一組字符串行為很有用,但經常產生意外的結果。
具有固定區(qū)域性的不區(qū)分大小寫的比較也使用由靜態(tài) CompareInfo 屬性返回的靜態(tài) CultureInfo.InvariantCulture 屬性以獲取比較信息。 所轉換字符中的任何大小寫差異都將被忽略。
使用 StringComparison.InvariantCulture 和 StringComparison.Ordinal 的比較對 ASCII 字符串產生相同的作用。 但是, StringComparison.InvariantCulture 會做出可能不適用于解釋為一組字節(jié)的字符串的語言性決策。 還可以使用 CultureInfo.InvariantCulture.CompareInfo 對象使 Compare 方法將一組特定的字符解釋為等效字符。 例如,下面的等效字符在固定區(qū)域性中是有效的:
InvariantCulture: a + ? = å
如果 A 字符的小寫拉丁字母“a”(\u0061) 旁邊有上方組合圓圈字符“+ " ?”(\u030a),A 字符就會被解釋為,上方帶有圓圈的小寫拉丁字母“å”(\u00e5)。 如下面的示例所示,此行為不同于序號比較。
string separated = "\u0061\u030a";
string combined = "\u00e5";
Console.WriteLine("Equal sort weight of {0} and {1} using InvariantCulture: {2}",
separated, combined,
String.Compare(separated, combined,
StringComparison.InvariantCulture) == 0);
Console.WriteLine("Equal sort weight of {0} and {1} using Ordinal: {2}",
separated, combined,
String.Compare(separated, combined,
StringComparison.Ordinal) == 0);
// The example displays the following output:
// Equal sort weight of a° and ? using InvariantCulture: True
// Equal sort weight of a° and ? using Ordinal: False當解釋其中出現如“å”組合的文件名稱、cookie 或其他內容時,序號比較仍會提供最透明和最合適的行為。
總的來說,固定區(qū)域性具有極少的對比較有用的屬性。 它會以與語言相關的方式執(zhí)行比較,使其無法保證完整的符號等效性,但它并不是任何區(qū)域性中顯示的選擇。 使用 StringComparison.InvariantCulture 進行比較的其中一個原因是為多個區(qū)域性相同的顯示保留已排序的數據。 例如,如果應用程序附帶包含用于顯示的已排序標識符列表的大型數據文件,則添加到此列表將需要使用固定條件樣式排序插入。
為方法調用選擇 StringComparison 成員
下表概述了從語義字符串上下文到 StringComparison 枚舉成員的映射:
| 數據 | 行為 | 相應 System.StringComparison value |
|---|---|---|
| 區(qū)分大小寫的內部標識符。 區(qū)分大小寫的標準標識符(例如 XML 和 HTTP)。 區(qū)分大小寫的安全相關設置。 | 字節(jié)完全匹配的非語言標識符。 | Ordinal |
| 不區(qū)分大小寫的內部標識符。 不區(qū)分大小寫的標準標識符(例如 XML 和 HTTP)。 文件路徑。 注冊表項和值。 環(huán)境變量。 資源標識符(例如,句柄名稱)。 不區(qū)分大小寫的安全相關設置。 | 無關大小寫的非語言標識符;尤其是存儲在大多數 Windows 系統服務中的數據。 | OrdinalIgnoreCase |
| 某些保留的、與語言相關的數據。 需要固定排序順序的語言數據的顯示。 | 仍與語言相關的區(qū)域性不明確數據。 | InvariantCulture - 或 - InvariantCultureIgnoreCase |
| 向用戶顯示的數據。 大多數用戶輸入。 | 需要本地語言自定義的數據。 | CurrentCulture - 或 - CurrentCultureIgnoreCase |
.NET 中的常見字符串比較方法
以下各節(jié)介紹最常用于執(zhí)行字符串比較的方法。
String.Compare
默認解釋: StringComparison.CurrentCulture。
作為字符串解釋最核心的操作,應根據當前區(qū)域性檢查這些方法調用的所有實例來確定是否應該從區(qū)域性(符號)解釋或分離字符串。 通常情況下,采用后者,并且應改用 StringComparison.Ordinal 比較。
System.Globalization.CompareInfo 屬性返回的 CultureInfo.CompareInfo 類也包括利用 Compare 標記枚舉的方式提供大量匹配選項(序號、忽略空白、忽略假名類型等)的 CompareOptions 方法。
String.CompareTo
默認解釋: StringComparison.CurrentCulture。
此方法當前不提供指定 StringComparison 類型的重載。 通常可以將此方法轉換為建議的 String.Compare(String, String, StringComparison) 形式。
實現 IComparable 和 IComparable 接口的類型實現此方法。 由于它不提供 StringComparison 參數選項,因此實現類型經常使用戶在其構造函數中指定 StringComparer。 下面的示例定義 FileName 類,其類構造函數包括 StringComparer 參數。 然后此 StringComparer 對象將用于 FileName.CompareTo 方法。
using System;
public class FileName : IComparable
{
string fname;
StringComparer comparer;
public FileName(string name, StringComparer comparer)
{
if (String.IsNullOrEmpty(name))
throw new ArgumentNullException("name");
this.fname = name;
if (comparer != null)
this.comparer = comparer;
else
this.comparer = StringComparer.OrdinalIgnoreCase;
}
public string Name
{
get { return fname; }
}
public int CompareTo(object obj)
{
if (obj == null) return 1;
if (! (obj is FileName))
return comparer.Compare(this.fname, obj.ToString());
else
return comparer.Compare(this.fname, ((FileName) obj).Name);
}
}String.Equals
默認解釋: StringComparison.Ordinal。
String 類可通過調用靜態(tài)或實例 Equals 方法重載或使用靜態(tài)相等運算符,測試是否相等。 默認情況下,重載和運算符使用序號比較。 但是,我們仍然建議調用顯式指定 StringComparison 類型的重載,即使想要執(zhí)行序號比較;這將更輕松地搜索特定字符串解釋的代碼。
String.ToUpper 和 String.ToLower
默認解釋: StringComparison.CurrentCulture。
應謹慎使用這些方法,因為將字符串強制為大寫或小寫經常用作在不考慮大小寫的情況下比較字符串的較小規(guī)范化。 如果是這樣,請考慮使用不區(qū)分大小寫的比較。
還可以使用 String.ToUpperInvariant 和 String.ToLowerInvariant 方法。 ToUpperInvariant 是規(guī)范化大小寫的標準方式。 使用 StringComparison.OrdinalIgnoreCase 進行的比較在行為上是兩個調用的組合:對兩個字符串參數調用 ToUpperInvariant ,并使用 StringComparison.Ordinal執(zhí)行比較。
通過向方法傳遞表示區(qū)域性的 CultureInfo 對象,重載也已可用于轉換該特性區(qū)域性中的大寫和小寫字母。
Char.ToUpper 和 Char.ToLower
默認解釋: StringComparison.CurrentCulture。
這些方法的工作原理類似于上一節(jié)中所述的 String.ToUpper 和 String.ToLower 方法。
String.StartsWith 和 String.EndsWith
默認解釋: StringComparison.CurrentCulture。
默認情況下,這兩種方法執(zhí)行區(qū)分區(qū)域性的比較。
String.IndexOf 和 String.LastIndexOf
默認解釋: StringComparison.CurrentCulture。
這些方法的默認重載如何執(zhí)行比較方面缺乏一致性。 包含 String.IndexOf 參數的所有 String.LastIndexOf 和 Char 方法都執(zhí)行序號比較,但是包含 String.IndexOf 參數的默認 String.LastIndexOf 和 String 方法都執(zhí)行區(qū)分區(qū)域性的比較。
如果調用 String.IndexOf(String) 或 String.LastIndexOf(String) 方法并向其傳遞一個字符串以在當前實例中查找,那么我們建議調用顯式指定 StringComparison 類型的重載。 包括 Char 參數的重載不允許指定 StringComparison 類型。
間接執(zhí)行字符串比較的方法
將字符串比較作為核心操作的一些非字符串方法使用 StringComparer 類型。 StringComparer 類型包含六個返回 StringComparer 實例的靜態(tài)屬性,這些實例的 StringComparer.Compare 方法可執(zhí)行以下類型的字符串比較:
- 使用當前區(qū)域性的區(qū)分區(qū)域性的字符串比較。 此 StringComparer 對象由 StringComparer.CurrentCulture 屬性返回。
- 使用當前區(qū)域性的不區(qū)分區(qū)域性的比較。 此 StringComparer 對象由 StringComparer.CurrentCultureIgnoreCase 屬性返回。
- 使用固定區(qū)域性的單詞比較規(guī)則的不區(qū)分區(qū)域性的比較。 此 StringComparer 對象由 StringComparer.InvariantCulture 屬性返回。
- 使用固定區(qū)域性的單詞比較規(guī)則的不區(qū)分大小寫和不區(qū)分區(qū)域性的比較。 此 StringComparer 對象由 StringComparer.InvariantCultureIgnoreCase 屬性返回。
- 序號比較。 此 StringComparer 對象由 StringComparer.Ordinal 屬性返回。
- 不區(qū)分大小寫的序號比較。 此 StringComparer 對象由 StringComparer.OrdinalIgnoreCase 屬性返回。
Array.Sort 和 Array.BinarySearch
默認解釋: StringComparison.CurrentCulture。
當在集合中存儲任何數據,或將持久數據從文件或數據庫中讀取到集合中時,切換當前區(qū)域性可能會使集合中的固定條件無效。 Array.BinarySearch 方法假定已對數組中要搜索的元素排序。 若要對數組中的任何字符串元素進行排序, Array.Sort 方法會調用 String.Compare 方法以對各個元素進行排序。 如果對數組進行排序和搜索其內容的時間范圍內區(qū)域性發(fā)生變化,那么使用區(qū)分區(qū)域性的比較器會很危險。 例如在下面的代碼中,是在由 Thread.CurrentThread.CurrentCulture 屬性。 如果在調用 StoreNames 和 DoesNameExist之間更改了區(qū)域性(尤其是數組內容保存在兩個方法調用之間的某個位置),那么二進制搜索可能會失敗。
// Incorrect.
string []storedNames;
public void StoreNames(string [] names)
{
int index = 0;
storedNames = new string[names.Length];
foreach (string name in names)
{
this.storedNames[index++] = name;
}
Array.Sort(names); // Line A.
}
public bool DoesNameExist(string name)
{
return (Array.BinarySearch(this.storedNames, name) >= 0); // Line B.
}建議的變體將顯示在下面使用相同序號(不區(qū)分區(qū)域性)比較方法進行排序并搜索數組的示例中。 在這兩個示例中,更改代碼會反映在標記 Line A 和 Line B 的代碼行中。
// Correct.
string []storedNames;
public void StoreNames(string [] names)
{
int index = 0;
storedNames = new string[names.Length];
foreach (string name in names)
{
this.storedNames[index++] = name;
}
Array.Sort(names, StringComparer.Ordinal); // Line A.
}
public bool DoesNameExist(string name)
{
return (Array.BinarySearch(this.storedNames, name, StringComparer.Ordinal) >= 0); // Line B.
}如果此數據永久保留并跨區(qū)域性移動,并且使用排序來向用戶顯示此數據,則可以考慮使用 StringComparison.InvariantCulture,其語言操作可獲得更好的用戶輸出且不受區(qū)域性更改的影響。 下面的示例修改了前面兩個示例,使用固定區(qū)域性對數組進行排序和搜索。
// Correct.
string []storedNames;
public void StoreNames(string [] names)
{
int index = 0;
storedNames = new string[names.Length];
foreach (string name in names)
{
this.storedNames[index++] = name;
}
Array.Sort(names, StringComparer.InvariantCulture); // Line A.
}
public bool DoesNameExist(string name)
{
return (Array.BinarySearch(this.storedNames, name, StringComparer.InvariantCulture) >= 0); // Line B.
}集合示例:哈希表構造函數
哈希字符串提供了第二個運算示例,該運算受比較字符串的方式影響。
下面的示例實例化 Hashtable 對象,方法是向其傳遞由 StringComparer 屬性返回的 StringComparer.OrdinalIgnoreCase 對象。 由于派生自 StringComparer 的類 StringComparer 實現 IEqualityComparer 接口,其 GetHashCode 方法用于計算哈希表中的字符串的哈希代碼。
const int initialTableCapacity = 100;
Hashtable h;
public void PopulateFileTable(string directory)
{
h = new Hashtable(initialTableCapacity,
StringComparer.OrdinalIgnoreCase);
foreach (string file in Directory.GetFiles(directory))
h.Add(file, File.GetCreationTime(file));
}
public void PrintCreationTime(string targetFile)
{
Object dt = h[targetFile];
if (dt != null)
{
Console.WriteLine("File {0} was created at time {1}.",
targetFile,
(DateTime) dt);
}
else
{
Console.WriteLine("File {0} does not exist.", targetFile);
}
}請參閱
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持腳本之家。
相關文章
詳解在ASP.NET Core中使用Angular2以及與Angular2的Token base身份認證
這篇文章主要介紹了詳解在ASP.NET Core中使用Angular2以及與Angular2的Token base身份認證,有興趣的可以了解一下。2016-12-12

