欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

.NET中字符串比較的最佳用法

 更新時(shí)間:2022年02月08日 10:39:25   作者:痕跡g  
本文詳細(xì)講解了.NET中字符串比較的最佳用法,文中通過(guò)示例代碼介紹的非常詳細(xì)。對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下

.NET 為開發(fā)本地化和全球化應(yīng)用程序提供廣泛支持,在執(zhí)行排序和顯示字符串等常見操作時(shí),輕松應(yīng)用當(dāng)前區(qū)域性或特定區(qū)域性的約定。 但排序或比較字符串并不總是區(qū)分區(qū)域性的操作。

例如,對(duì)于應(yīng)用程序內(nèi)部使用的字符串,通常應(yīng)該跨所有區(qū)域性以相同的方式對(duì)其進(jìn)行處理。 如果將 XML 標(biāo)記、HTML 標(biāo)記、用戶名、文件路徑和系統(tǒng)對(duì)象名稱等與區(qū)域性無(wú)關(guān)的字符串?dāng)?shù)據(jù)解釋為區(qū)分區(qū)域性,則應(yīng)用程序代碼會(huì)遭遇細(xì)微的錯(cuò)誤、不佳的性能,在某些情況下,還會(huì)遭遇安全性問(wèn)題。

本文介紹 .NET 中的字符串排序、比較和大小寫方法,針對(duì)如何選擇適當(dāng)?shù)淖址幚矸椒ㄌ岢鼋ㄗh,并提供有關(guān)字符串處理方法的其他信息。

對(duì)字符串用法的建議

使用 .NET 進(jìn)行開發(fā)時(shí),請(qǐng)遵循以下簡(jiǎn)要建議比較字符串:

  • 使用為字符串操作顯式指定字符串比較規(guī)則的重載。 通常情況下,這涉及調(diào)用具有 StringComparison類型的參數(shù)的方法重載。
  • 使用 StringComparison.Ordinal 或 StringComparison.OrdinalIgnoreCase 進(jìn)行比較,并以此作為匹配區(qū)域性不明確的字符串的安全默認(rèn)設(shè)置。
  • 將比較與 StringComparison.Ordinal 或 StringComparison.OrdinalIgnoreCase 配合使用,以獲得更好的性能。
  • 向用戶顯示輸出時(shí),使用基于 StringComparison.CurrentCulture 的字符串操作。
  • 當(dāng)進(jìn)行與語(yǔ)言(例如,符號(hào))無(wú)關(guān)的比較時(shí),使用非語(yǔ)言的 StringComparison.Ordinal 或 StringComparison.OrdinalIgnoreCase 值,而不使用基于 CultureInfo.InvariantCulture 的字符串操作。
  • 在規(guī)范化要比較的字符串時(shí),使用 String.ToUpperInvariant 方法而非 String.ToLowerInvariant 方法。
  • 使用 String.Equals 方法的重載來(lái)測(cè)試兩個(gè)字符串是否相等。
  • 使用 String.Compare 和 String.CompareTo 方法可對(duì)字符串進(jìn)行排序,而不是檢查字符串是否相等。
  • 在用戶界面,使用區(qū)分區(qū)域性的格式顯示非字符串?dāng)?shù)據(jù),如數(shù)字和日期。 使用格式以固定區(qū)域性使非字符串?dāng)?shù)據(jù)顯示為字符串形式。

比較字符串時(shí),請(qǐng)避免采用以下做法:

  • 不要使用未顯式或隱式為字符串操作指定字符串比較規(guī)則的重載。
  • 在大多數(shù)情況下,不要使用基于 StringComparison.InvariantCulture 的字符串操作。 其中的一個(gè)少數(shù)例外情況是,保存在語(yǔ)言上有意義但區(qū)域性不明確的數(shù)據(jù)。
  • 不要使用 String.Compare 或 CompareTo 方法的重載和用于確定兩個(gè)字符串是否相等的返回值為 0 的測(cè)試。

顯式指定字符串比較

重載 .NET 中大部分字符串操作方法。 通常,一個(gè)或多個(gè)重載會(huì)接受默認(rèn)設(shè)置,然而其他重載則不接受默認(rèn)設(shè)置,而是定義比較或操作字符串的精確方式。 大多數(shù)不依賴于默認(rèn)設(shè)置的方法都包括 StringComparison類型的參數(shù),該參數(shù)是按區(qū)域性和大小寫為字符串比較顯式指定規(guī)則的枚舉。 下表描述StringComparison 枚舉成員。

StringComparison 成員描述
CurrentCulture使用當(dāng)前區(qū)域性執(zhí)行區(qū)分大小寫的比較。
CurrentCultureIgnoreCase使用當(dāng)前區(qū)域性執(zhí)行不區(qū)分大小寫的比較。
InvariantCulture使用固定區(qū)域性執(zhí)行區(qū)分大小寫的比較。
InvariantCultureIgnoreCase使用固定區(qū)域性執(zhí)行不區(qū)分大小寫的比較。
Ordinal執(zhí)行序號(hào)比較。
OrdinalIgnoreCase執(zhí)行不區(qū)分大小寫的序號(hào)比較。

例如, IndexOf 方法(它返回 String 對(duì)象中與某字符或字符串匹配的子字符串的索引)具有九種重載:

  • 默認(rèn)情況下,IndexOf(Char), IndexOf(Char, Int32)和 IndexOf(Char, Int32, Int32)對(duì)字符串中的字符執(zhí)行序號(hào)(區(qū)分大小寫但不區(qū)分區(qū)域性的)搜索。
  • 默認(rèn)情況下,IndexOf(String), IndexOf(String, Int32)和 IndexOf(String, Int32, Int32)對(duì)字符串中的子字符串執(zhí)行區(qū)分大小寫且區(qū)分區(qū)域性的搜索。
  • IndexOf(String, StringComparison)、 IndexOf(String, Int32, StringComparison)和 IndexOf(String, Int32, Int32, StringComparison),其中包括 StringComparison 類型的參數(shù),該類型允許指定比較形式。

我們建議選擇不使用默認(rèn)值的重載,原因如下:

  • 具有默認(rèn)參數(shù)的一些重載(在字符串實(shí)例中搜索 Char 的重載)執(zhí)行序號(hào)比較,而其他重載(在字符串實(shí)例中搜索字符串的重載)執(zhí)行的是區(qū)分區(qū)域性的比較。 要記住哪種方法使用哪個(gè)默認(rèn)值并非易事,并很容易混淆重載。
  • 依賴于方法調(diào)用默認(rèn)值的代碼的意圖并不清楚。 在下面依賴于默認(rèn)值的示例中,很難了解開發(fā)人員對(duì)兩個(gè)字符串的實(shí)際意圖是執(zhí)行序號(hào)比較還是語(yǔ)言比較,或者 protocol 和“http”之間存在的大小寫差異是否會(huì)導(dǎo)致相等性測(cè)試返回 false類型的參數(shù)的方法重載。
string protocol = GetProtocol(url);
if (String.Equals(protocol, "http")) {
   // ...Code to handle HTTP protocol.
}
else {
   throw new InvalidOperationException();
}

一般情況下,我們建議調(diào)用不依賴于默認(rèn)設(shè)置的方法,因?yàn)檫@會(huì)明確代碼的意圖。 這進(jìn)而使代碼更具可讀性且更易于調(diào)試和維護(hù)。 下面的示例解決了前面示例中提出的問(wèn)題。 使用序號(hào)比較并且忽略大小寫差異。

string protocol = GetProtocol(url);
if (String.Equals(protocol, "http", StringComparison.OrdinalIgnoreCase)) {
   // ...Code to handle HTTP protocol.
}
else {
   throw new InvalidOperationException();
}

字符串比較的詳細(xì)信息

字符串比較是許多字符串相關(guān)操作的核心,特別是排序和相等性測(cè)試操作。 字符串以確定的順序進(jìn)行排序:如果在排序的字符串列表中,“my”出現(xiàn)在“string”之前,則“my”必定小于或等于“string”。 此外,比較可隱式確定相等性。 對(duì)于認(rèn)為是相等的字符串,比較操作將返回零。 對(duì)此很好的解釋是兩個(gè)字符串都不小于對(duì)方。 涉及到字符串的最有意義的操作包括這些步驟中的一個(gè)或兩個(gè)步驟:與另一個(gè)字符串進(jìn)行比較和執(zhí)行明確的排序操作。

[!NOTE]

可以下載排序權(quán)重表,這是一組文本文件,其中包含有關(guān) Windows 操作系統(tǒng)排序和比較操作中所使用的字符權(quán)重的信息,也可以下載默認(rèn) Unicode 排序元素表,這是適用于 Linux 和 macOS 的最新版排序權(quán)重表。 Linux 和 macOS 上的特定排序權(quán)重表版本取決于系統(tǒng)上安裝的 International Components for Unicode 庫(kù)的版本。 有關(guān) ICU 版本及它們所實(shí)現(xiàn)的 Unicode 版本的信息,請(qǐng)參閱下載 ICU。

但是,評(píng)估兩個(gè)字符串的相等性或排序順序不會(huì)生成一個(gè)正確的結(jié)果;其結(jié)果取決于用于比較這兩個(gè)字符串的條件。 特別是,序號(hào)或基于當(dāng)前區(qū)域性或固定區(qū)域性(基于英語(yǔ)語(yǔ)言的區(qū)域設(shè)置不明確的區(qū)域性)的大小寫和排序約定的字符串比較可能會(huì)產(chǎn)生不同的結(jié)果。

此外,使用不同 .NET 版本或在不同操作系統(tǒng)或不同的操作系統(tǒng)版本上使用 .NET 進(jìn)行字符串比較時(shí),返回的結(jié)果可能不同。 有關(guān)詳細(xì)信息,請(qǐng)參閱字符串和 Unicode 標(biāo)準(zhǔn)。

使用當(dāng)前區(qū)域性的字符串比較

一個(gè)條件涉及在比較字符串時(shí)使用當(dāng)前區(qū)域性的約定。 基于當(dāng)前區(qū)域性的比較使用線程的當(dāng)前區(qū)域性或區(qū)域設(shè)置。 如果用戶未設(shè)置該區(qū)域性,則默認(rèn)為“控制面板”中“區(qū)域選項(xiàng)” 窗口中的設(shè)置。 當(dāng)數(shù)據(jù)與語(yǔ)言相關(guān)并反映區(qū)分區(qū)域性的用戶交互時(shí),應(yīng)始終使用基于當(dāng)前區(qū)域性的比較。

但是,當(dāng)區(qū)域性發(fā)生更改時(shí),.NET 中的比較和大小寫行為也發(fā)生更改。 如果執(zhí)行應(yīng)用程序的計(jì)算機(jī)與用于開發(fā)該應(yīng)用程序的計(jì)算機(jī)具有不同的區(qū)域性,或者執(zhí)行線程改變它的區(qū)域性,則會(huì)發(fā)生這種情況。 此行為是有意而為之的,但許多開發(fā)人員不易察覺此行為。 下面的示例說(shuō)明了美國(guó)英語(yǔ)(“en-US”)與瑞典語(yǔ)(“sv-SE”)區(qū)域性在排序順序中的差異。 請(qǐng)注意,單詞“ångström”、“Windows”和“Visual Studio”將出現(xiàn)在已排序的字符串?dāng)?shù)組的不同位置。

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

使用當(dāng)前區(qū)域性的不區(qū)分大小寫比較和區(qū)分區(qū)域性的比較是相同的,只不過(guò)前者忽略由線程的當(dāng)前區(qū)域性指示的大小寫。 這種情況也可表明它的排序順序。

以下方法默認(rèn)利用使用當(dāng)前區(qū)域性語(yǔ)義的比較:

  • 不包括String.Compare 參數(shù)的 StringComparison 重載。
  • String.CompareTo 重載。
  • 默認(rèn) String.StartsWith(String) 方法和具有 String.StartsWith(String, Boolean, CultureInfo) null nullCultureInfo 重載。
  • 默認(rèn) String.EndsWith(String) 方法和需要使用 nullCultureInfo 參數(shù)的 String.EndsWith(String, Boolean, CultureInfo) 方法。
  • 接受String.IndexOf 作為搜索參數(shù)且不包含 String 參數(shù)的 StringComparison 重載。
  • 接受String.LastIndexOf 作為搜索參數(shù)且不包含 String 參數(shù)的 StringComparison 重載。

總之,我們建議調(diào)用具有 <xref:System.StringComparison> 參數(shù)的重載,以便明確方法調(diào)用的意圖。

當(dāng)從語(yǔ)言角度解釋非語(yǔ)言的字符串?dāng)?shù)據(jù),或利用其他區(qū)域性的約定解釋某個(gè)特定區(qū)域性中的字符串時(shí),則會(huì)發(fā)生或大或小的錯(cuò)誤。 土耳其語(yǔ) I 問(wèn)題便是一個(gè)規(guī)范示例。

對(duì)于幾乎所有拉丁字母來(lái)講(包括美國(guó)英語(yǔ)),字符“i”(\u0069) 是字符“I”(\u0049) 的小寫形式。 此大小寫規(guī)則快速成為在此類區(qū)域性中編程的人員的默認(rèn)設(shè)置。 但是,土耳其語(yǔ)(“tr-TR”)字母表中包含一個(gè)“帶有點(diǎn)的 I”的字符“?”(\u0130),該字符是“i”的大寫形式。 土耳其語(yǔ)還包括一個(gè)小寫“不帶點(diǎn)的 i”字符,即為“?”(\u0131),該字符的大寫形式為“I”。 阿塞拜疆語(yǔ)(“az”)區(qū)域也會(huì)出現(xiàn)這種情況。

因此,關(guān)于將“i”變?yōu)榇髮懟驅(qū)?ldquo;I”變?yōu)樾懙募僭O(shè)并非在所有區(qū)域性中都是有效的。 如果為字符串比較例程使用默認(rèn)重載,則它們可能會(huì)因區(qū)域性不同而異。 如果對(duì)非語(yǔ)言的數(shù)據(jù)進(jìn)行比較,使用默認(rèn)重載會(huì)產(chǎn)生不良后果,如以下對(duì)字符串“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

如果無(wú)意中在安全敏感設(shè)置中使用了區(qū)域性,則此比較會(huì)導(dǎo)致發(fā)生重大問(wèn)題,如以下示例所示。 如果當(dāng)前區(qū)域性為美國(guó)英語(yǔ),則 IsFileURI("file:") 等方法調(diào)用將返回 true;但如果當(dāng)前區(qū)域性為土耳其語(yǔ),則將返回 false。 因此,在土耳其語(yǔ)系統(tǒng)中,有人可能會(huì)避開阻止訪問(wèn)以“FILE:”開頭的不區(qū)分大小寫的安全措施。

public static bool IsFileURI(String path)
{
   return path.StartsWith("FILE:", true, null);
}

在這種情況下,由于“file:”會(huì)被解釋為非語(yǔ)言的、不區(qū)分區(qū)域性的標(biāo)識(shí)符,因此,應(yīng)按照下面的示例所示編寫代碼:

public static bool IsFileURI(string path)
{
   return path.StartsWith("FILE:", StringComparison.OrdinalIgnoreCase);
}

序號(hào)字符串操作

在方法調(diào)用中指定 StringComparison.Ordinal 或 StringComparison.OrdinalIgnoreCase 值表示非語(yǔ)言比較,這種比較忽略了自然語(yǔ)言的特性。 利用 StringComparison 值調(diào)用的方法將字符串操作決策建立在簡(jiǎn)單的字節(jié)比較的基礎(chǔ)之上,而不是按區(qū)域性參數(shù)化的大小寫或相等表。 在大多數(shù)情況下,這種方法最符合字符串的預(yù)期解釋,并使代碼更快更可靠。

序號(hào)比較就是字符串比較,在這種比較中,將比較每個(gè)字符串中的每個(gè)字節(jié)且不進(jìn)行語(yǔ)言解釋;例如,“windows”不匹配“Windows”。 實(shí)質(zhì)上,這是對(duì) C 運(yùn)行時(shí) strcmp 函數(shù)的調(diào)用。 當(dāng)上下文指示應(yīng)完全匹配字符串或要求保守匹配策略時(shí),請(qǐng)使用這種比較。 此外,序號(hào)比較是最快的比較操作,因?yàn)樗诖_定結(jié)果時(shí)不應(yīng)用任何語(yǔ)言規(guī)則。

.NET 中的字符串可以包括嵌入的空字符。 序號(hào)比較與區(qū)分區(qū)域性的比較(包括使用固定區(qū)域性的比較)之間最明顯的區(qū)別之一是對(duì)字符串中嵌入的空字符的處理方式。 當(dāng)使用 String.Compare 和 String.Equals 方法執(zhí)行區(qū)分區(qū)域性的比較(包括使用固定區(qū)域性的比較)時(shí),將忽略這些字符。 因此,在區(qū)分區(qū)域性的比較中,包含嵌入的空字符的字符串可視為等于不包含空字符的字符串。

[!IMPORTANT]

盡管字符串比較方法忽略嵌入的空字符,但是 String.Contains、 String.EndsWith、 String.IndexOf、 String.LastIndexOf和 String.StartsWith 等字符串搜索方法并不會(huì)忽略這些字符。

下面的示例對(duì)字符串“Aa”與在“A”和“a”之間嵌入了多個(gè)空字符的相似字符串進(jìn)行區(qū)分區(qū)域性的比較,并顯示如何將這兩個(gè)字符串視為相等的字符串:

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

但是,當(dāng)使用序號(hào)比較時(shí),這兩個(gè)字符串不會(huì)視為相等,如下面的示例所示:

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ū)分大小寫的序號(hào)比較是第二種最保守的方法。 這些比較會(huì)忽略大多數(shù)的大小寫;例如,“windows”會(huì)匹配“Windows”。 在處理 ASCII 字符時(shí),此策略等同于 StringComparison.Ordinal,只不過(guò)它會(huì)忽略常用的 ASCII 大小寫。 因此,[A, Z] (\u0041-\u005A) 中的任何字符都會(huì)匹配 [a,z] (\u0061-\007A) 中的相應(yīng)字符。 超出 ASCII 范圍的大小寫使用固定區(qū)域性的表。 因此,下面的比較:

String.Compare(strA, strB, StringComparison.OrdinalIgnoreCase);

等效于(但會(huì)更快)這種比較:

String.Compare(strA.ToUpperInvariant(), strB.ToUpperInvariant(),
               StringComparison.Ordinal);

這些比較仍非???。

StringComparison.Ordinal 和 StringComparison.OrdinalIgnoreCase 均直接使用二進(jìn)制值并最適合匹配。 當(dāng)不確定比較設(shè)置時(shí),請(qǐng)使用這兩個(gè)值中的其中一個(gè)。 不過(guò),由于它們執(zhí)行逐字節(jié)比較,因此不會(huì)按照語(yǔ)言排序順序(如英語(yǔ)詞典)進(jìn)行排序,而是按照二進(jìn)制排序順序。 如果向用戶顯示結(jié)果,則在大多數(shù)上下文中結(jié)果都看上去不正常。

序號(hào)語(yǔ)義是不包括 String.Equals 參數(shù)(包括相等運(yùn)算符)的 StringComparison 重載的默認(rèn)項(xiàng)。 總之,我們建議調(diào)用具有 StringComparison 參數(shù)的重載。

使用固定區(qū)域性的字符串操作

具有固定區(qū)域性的比較使用由靜態(tài) CompareInfo 屬性返回的 CultureInfo.InvariantCulture 屬性。 此行為在所有系統(tǒng)中都相同;它會(huì)將其范圍外的任何字符轉(zhuǎn)換為其認(rèn)為等效的固定字符。 此策略對(duì)于在各個(gè)區(qū)域性中維護(hù)一組字符串行為很有用,但經(jīng)常產(chǎn)生意外的結(jié)果。

具有固定區(qū)域性的不區(qū)分大小寫的比較也使用由靜態(tài) CompareInfo 屬性返回的靜態(tài) CultureInfo.InvariantCulture 屬性以獲取比較信息。 所轉(zhuǎn)換字符中的任何大小寫差異都將被忽略。

使用 StringComparison.InvariantCulture 和 StringComparison.Ordinal 的比較對(duì) ASCII 字符串產(chǎn)生相同的作用。 但是, StringComparison.InvariantCulture 會(huì)做出可能不適用于解釋為一組字節(jié)的字符串的語(yǔ)言性決策。 還可以使用 CultureInfo.InvariantCulture.CompareInfo 對(duì)象使 Compare 方法將一組特定的字符解釋為等效字符。 例如,下面的等效字符在固定區(qū)域性中是有效的:

InvariantCulture: a + ? = å

如果 A 字符的小寫拉丁字母“a”(\u0061) 旁邊有上方組合圓圈字符“+ " ?”(\u030a),A 字符就會(huì)被解釋為,上方帶有圓圈的小寫拉丁字母“å”(\u00e5)。 如下面的示例所示,此行為不同于序號(hào)比較。

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

當(dāng)解釋其中出現(xiàn)如“å”組合的文件名稱、cookie 或其他內(nèi)容時(shí),序號(hào)比較仍會(huì)提供最透明和最合適的行為。

總的來(lái)說(shuō),固定區(qū)域性具有極少的對(duì)比較有用的屬性。 它會(huì)以與語(yǔ)言相關(guān)的方式執(zhí)行比較,使其無(wú)法保證完整的符號(hào)等效性,但它并不是任何區(qū)域性中顯示的選擇。 使用 StringComparison.InvariantCulture 進(jìn)行比較的其中一個(gè)原因是為多個(gè)區(qū)域性相同的顯示保留已排序的數(shù)據(jù)。 例如,如果應(yīng)用程序附帶包含用于顯示的已排序標(biāo)識(shí)符列表的大型數(shù)據(jù)文件,則添加到此列表將需要使用固定條件樣式排序插入。

為方法調(diào)用選擇 StringComparison 成員

下表概述了從語(yǔ)義字符串上下文到 StringComparison 枚舉成員的映射:

數(shù)據(jù)行為相應(yīng) System.StringComparison

value

區(qū)分大小寫的內(nèi)部標(biāo)識(shí)符。

區(qū)分大小寫的標(biāo)準(zhǔn)標(biāo)識(shí)符(例如 XML 和 HTTP)。

區(qū)分大小寫的安全相關(guān)設(shè)置。

字節(jié)完全匹配的非語(yǔ)言標(biāo)識(shí)符。Ordinal
不區(qū)分大小寫的內(nèi)部標(biāo)識(shí)符。

不區(qū)分大小寫的標(biāo)準(zhǔn)標(biāo)識(shí)符(例如 XML 和 HTTP)。

文件路徑。

注冊(cè)表項(xiàng)和值。

環(huán)境變量。

資源標(biāo)識(shí)符(例如,句柄名稱)。

不區(qū)分大小寫的安全相關(guān)設(shè)置。

無(wú)關(guān)大小寫的非語(yǔ)言標(biāo)識(shí)符;尤其是存儲(chǔ)在大多數(shù) Windows 系統(tǒng)服務(wù)中的數(shù)據(jù)。OrdinalIgnoreCase
某些保留的、與語(yǔ)言相關(guān)的數(shù)據(jù)。

需要固定排序順序的語(yǔ)言數(shù)據(jù)的顯示。

仍與語(yǔ)言相關(guān)的區(qū)域性不明確數(shù)據(jù)。InvariantCulture

- 或 -

InvariantCultureIgnoreCase

向用戶顯示的數(shù)據(jù)。

大多數(shù)用戶輸入。

需要本地語(yǔ)言自定義的數(shù)據(jù)。CurrentCulture

- 或 -

CurrentCultureIgnoreCase

.NET 中的常見字符串比較方法

以下各節(jié)介紹最常用于執(zhí)行字符串比較的方法。

String.Compare

默認(rèn)解釋: StringComparison.CurrentCulture。

作為字符串解釋最核心的操作,應(yīng)根據(jù)當(dāng)前區(qū)域性檢查這些方法調(diào)用的所有實(shí)例來(lái)確定是否應(yīng)該從區(qū)域性(符號(hào))解釋或分離字符串。 通常情況下,采用后者,并且應(yīng)改用 StringComparison.Ordinal 比較。

System.Globalization.CompareInfo 屬性返回的 CultureInfo.CompareInfo 類也包括利用 Compare 標(biāo)記枚舉的方式提供大量匹配選項(xiàng)(序號(hào)、忽略空白、忽略假名類型等)的 CompareOptions 方法。

String.CompareTo

默認(rèn)解釋: StringComparison.CurrentCulture。

此方法當(dāng)前不提供指定 StringComparison 類型的重載。 通常可以將此方法轉(zhuǎn)換為建議的 String.Compare(String, String, StringComparison) 形式。

實(shí)現(xiàn) IComparable 和 IComparable 接口的類型實(shí)現(xiàn)此方法。 由于它不提供 StringComparison 參數(shù)選項(xiàng),因此實(shí)現(xiàn)類型經(jīng)常使用戶在其構(gòu)造函數(shù)中指定 StringComparer。 下面的示例定義 FileName 類,其類構(gòu)造函數(shù)包括 StringComparer 參數(shù)。 然后此 StringComparer 對(duì)象將用于 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

默認(rèn)解釋: StringComparison.Ordinal。

String 類可通過(guò)調(diào)用靜態(tài)或?qū)嵗?Equals 方法重載或使用靜態(tài)相等運(yùn)算符,測(cè)試是否相等。 默認(rèn)情況下,重載和運(yùn)算符使用序號(hào)比較。 但是,我們?nèi)匀唤ㄗh調(diào)用顯式指定 StringComparison 類型的重載,即使想要執(zhí)行序號(hào)比較;這將更輕松地搜索特定字符串解釋的代碼。

String.ToUpper 和 String.ToLower

默認(rèn)解釋: StringComparison.CurrentCulture。

應(yīng)謹(jǐn)慎使用這些方法,因?yàn)閷⒆址畯?qiáng)制為大寫或小寫經(jīng)常用作在不考慮大小寫的情況下比較字符串的較小規(guī)范化。 如果是這樣,請(qǐng)考慮使用不區(qū)分大小寫的比較。

還可以使用 String.ToUpperInvariant 和 String.ToLowerInvariant 方法。 ToUpperInvariant 是規(guī)范化大小寫的標(biāo)準(zhǔn)方式。 使用 StringComparison.OrdinalIgnoreCase 進(jìn)行的比較在行為上是兩個(gè)調(diào)用的組合:對(duì)兩個(gè)字符串參數(shù)調(diào)用 ToUpperInvariant ,并使用 StringComparison.Ordinal執(zhí)行比較。

通過(guò)向方法傳遞表示區(qū)域性的 CultureInfo 對(duì)象,重載也已可用于轉(zhuǎn)換該特性區(qū)域性中的大寫和小寫字母。

Char.ToUpper 和 Char.ToLower

默認(rèn)解釋: StringComparison.CurrentCulture。

這些方法的工作原理類似于上一節(jié)中所述的 String.ToUpper 和 String.ToLower 方法。

String.StartsWith 和 String.EndsWith

默認(rèn)解釋: StringComparison.CurrentCulture。

默認(rèn)情況下,這兩種方法執(zhí)行區(qū)分區(qū)域性的比較。

String.IndexOf 和 String.LastIndexOf

默認(rèn)解釋: StringComparison.CurrentCulture。

這些方法的默認(rèn)重載如何執(zhí)行比較方面缺乏一致性。 包含 String.IndexOf 參數(shù)的所有 String.LastIndexOf 和 Char 方法都執(zhí)行序號(hào)比較,但是包含 String.IndexOf 參數(shù)的默認(rèn) String.LastIndexOf 和 String 方法都執(zhí)行區(qū)分區(qū)域性的比較。

如果調(diào)用 String.IndexOf(String) 或 String.LastIndexOf(String) 方法并向其傳遞一個(gè)字符串以在當(dāng)前實(shí)例中查找,那么我們建議調(diào)用顯式指定 StringComparison 類型的重載。 包括 Char 參數(shù)的重載不允許指定 StringComparison 類型。

間接執(zhí)行字符串比較的方法

將字符串比較作為核心操作的一些非字符串方法使用 StringComparer 類型。 StringComparer 類型包含六個(gè)返回 StringComparer 實(shí)例的靜態(tài)屬性,這些實(shí)例的 StringComparer.Compare 方法可執(zhí)行以下類型的字符串比較:

  • 使用當(dāng)前區(qū)域性的區(qū)分區(qū)域性的字符串比較。 此 StringComparer 對(duì)象由 StringComparer.CurrentCulture 屬性返回。
  • 使用當(dāng)前區(qū)域性的不區(qū)分區(qū)域性的比較。 此 StringComparer 對(duì)象由 StringComparer.CurrentCultureIgnoreCase 屬性返回。
  • 使用固定區(qū)域性的單詞比較規(guī)則的不區(qū)分區(qū)域性的比較。 此 StringComparer 對(duì)象由 StringComparer.InvariantCulture 屬性返回。
  • 使用固定區(qū)域性的單詞比較規(guī)則的不區(qū)分大小寫和不區(qū)分區(qū)域性的比較。 此 StringComparer 對(duì)象由 StringComparer.InvariantCultureIgnoreCase 屬性返回。
  • 序號(hào)比較。 此 StringComparer 對(duì)象由 StringComparer.Ordinal 屬性返回。
  • 不區(qū)分大小寫的序號(hào)比較。 此 StringComparer 對(duì)象由 StringComparer.OrdinalIgnoreCase 屬性返回。

Array.Sort 和 Array.BinarySearch

默認(rèn)解釋: StringComparison.CurrentCulture。

當(dāng)在集合中存儲(chǔ)任何數(shù)據(jù),或?qū)⒊志脭?shù)據(jù)從文件或數(shù)據(jù)庫(kù)中讀取到集合中時(shí),切換當(dāng)前區(qū)域性可能會(huì)使集合中的固定條件無(wú)效。 Array.BinarySearch 方法假定已對(duì)數(shù)組中要搜索的元素排序。 若要對(duì)數(shù)組中的任何字符串元素進(jìn)行排序, Array.Sort 方法會(huì)調(diào)用 String.Compare 方法以對(duì)各個(gè)元素進(jìn)行排序。 如果對(duì)數(shù)組進(jìn)行排序和搜索其內(nèi)容的時(shí)間范圍內(nèi)區(qū)域性發(fā)生變化,那么使用區(qū)分區(qū)域性的比較器會(huì)很危險(xiǎn)。 例如在下面的代碼中,是在由 Thread.CurrentThread.CurrentCulture 屬性。 如果在調(diào)用 StoreNames 和 DoesNameExist之間更改了區(qū)域性(尤其是數(shù)組內(nèi)容保存在兩個(gè)方法調(diào)用之間的某個(gè)位置),那么二進(jìn)制搜索可能會(huì)失敗。

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

建議的變體將顯示在下面使用相同序號(hào)(不區(qū)分區(qū)域性)比較方法進(jìn)行排序并搜索數(shù)組的示例中。 在這兩個(gè)示例中,更改代碼會(huì)反映在標(biāo)記 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.
}

如果此數(shù)據(jù)永久保留并跨區(qū)域性移動(dòng),并且使用排序來(lái)向用戶顯示此數(shù)據(jù),則可以考慮使用 StringComparison.InvariantCulture,其語(yǔ)言操作可獲得更好的用戶輸出且不受區(qū)域性更改的影響。 下面的示例修改了前面兩個(gè)示例,使用固定區(qū)域性對(duì)數(shù)組進(jìn)行排序和搜索。

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

集合示例:哈希表構(gòu)造函數(shù)

哈希字符串提供了第二個(gè)運(yùn)算示例,該運(yùn)算受比較字符串的方式影響。

下面的示例實(shí)例化 Hashtable 對(duì)象,方法是向其傳遞由 StringComparer 屬性返回的 StringComparer.OrdinalIgnoreCase 對(duì)象。 由于派生自 StringComparer 的類 StringComparer 實(shí)現(xiàn) IEqualityComparer 接口,其 GetHashCode 方法用于計(jì)算哈希表中的字符串的哈希代碼。

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

請(qǐng)參閱

 以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

最新評(píng)論