在 .NET 中使用字串的最佳做法Best Practices for Using Strings in .NET

.NET 可廣泛支援當地語系化和全球化應用程式的開發作業,使您在執行一般作業 (例如排序和顯示字串) 時,可輕鬆套用目前的文化特性或文化特性特定的慣例。.NET provides extensive support for developing localized and globalized applications, and makes it easy to apply the conventions of either the current culture or a specific culture when performing common operations such as sorting and displaying strings. 但是,排序或比較字串並不一定是區分文化特性的作業。But sorting or comparing strings is not always a culture-sensitive operation. 例如,應用程式內部使用的字串,通常應該跨所有文化特性皆進行相同處理。For example, strings that are used internally by an application typically should be handled identically across all cultures. 若將與文化特性無關的字串資料 (例如 XML 標記、HTML 標記、使用者名稱、檔案路徑和系統物件的名稱) 進行區分文化特性的解譯時,應用程式程式碼可能會出現細微的 Bug、效能不佳,甚至在某些情況下,會產生安全性問題。When culturally independent string data, such as XML tags, HTML tags, user names, file paths, and the names of system objects, are interpreted as if they were culture-sensitive, application code can be subject to subtle bugs, poor performance, and, in some cases, security issues.

本主題詳述 .NET 中的字串排序、比較和大小寫方法,並提供選取適當字串處理方法的建議,以及有關字串處理方法的其他資訊。This topic examines the string sorting, comparison, and casing methods in .NET, presents recommendations for selecting an appropriate string-handling method, and provides additional information about string-handling methods. 它也會說明如何處理數值資料和日期與時間資料之類的格式化資料,以供顯示及儲存。It also examines how formatted data, such as numeric data and date and time data, is handled for display and for storage.

此主題包括下列章節:This topic contains the following sections:

字串的使用方式建議Recommendations for String Usage

當您使用 .NET 進行開發時,請遵循下列簡單的字串使用建議:When you develop with .NET, follow these simple recommendations when you use strings:

當您使用字串時,請避免下列作法:Avoid the following practices when you use strings:

  • 不要使用未明確或未隱含指定字串比較規則的多載來進行字串作業。Do not use overloads that do not explicitly or implicitly specify the string comparison rules for string operations.

  • 在大部分情況下,請不要使用依據 StringComparison.InvariantCulture 的字串作業。Do not use string operations based on StringComparison.InvariantCulture in most cases. 若您要保存具語言意義但無從驗證文化特性的資料,就是少數的例外狀況之一。One of the few exceptions is when you are persisting linguistically meaningful but culturally agnostic data.

  • 不要使用 String.CompareCompareTo 方法的多載以及傳回零值的測試,來判斷兩個字串是否相等。Do not use an overload of the String.Compare or CompareTo method and test for a return value of zero to determine whether two strings are equal.

  • 不要使用區分文化特性的格式來保存數值資料或字串形式的日期和時間資料。Do not use culture-sensitive formatting to persist numeric data or date and time data in string form.

回到頁首Back to top

明確指定字串比較Specifying String Comparisons Explicitly

在 .NET 中的字串操作方法大多都是多載。Most of the string manipulation methods in .NET are overloaded. 一般而言,您可讓一或多個多載接受預設值,而其他不接受預設值的多載,則定義用來比較或操作字串的精確方式。Typically, one or more overloads accept default settings, whereas others accept no defaults and instead define the precise way in which strings are to be compared or manipulated. 大部分不依賴預設值的方法都會包含 StringComparison類型的參數,其為一種列舉類型,可明確指定依據文化特性和大小寫進行字串比較的規則。Most of the methods that do not rely on defaults include a parameter of type StringComparison, which is an enumeration that explicitly specifies rules for string comparison by culture and case. 下表說明 StringComparison 列舉類型成員。The following table describes the StringComparison enumeration members.

StringComparison 成員StringComparison member 說明Description
CurrentCulture 使用目前文化特性執行區分大小寫的比較。Performs a case-sensitive comparison using the current culture.
CurrentCultureIgnoreCase 使用目前文化特性執行不區分大小寫的比較。Performs a case-insensitive comparison using the current culture.
InvariantCulture 使用不因國別而異的文化特性執行區分大小寫的比較。Performs a case-sensitive comparison using the invariant culture.
InvariantCultureIgnoreCase 使用不因國別而異的文化特性執行不區分大小寫的比較。Performs a case-insensitive comparison using the invariant culture.
Ordinal 執行序數比較。Performs an ordinal comparison.
OrdinalIgnoreCase 執行不區分大小寫的序數比較。Performs a case-insensitive ordinal comparison.

例如, IndexOf 方法有下列九個多載,可傳回 String 物件中符合某字元或某字串之子字串的索引:For example, the IndexOf method, which returns the index of a substring in a String object that matches either a character or a string, has nine overloads:

基於下列原因,建議您選取不使用預設值的多載:We recommend that you select an overload that does not use default values, for the following reasons:

  • 有些使用預設參數的多載 (其會在字串執行個體中搜尋 Char ) 會執行序數比較,而其他多載 (其會搜尋字串執行個體中的字串) 有區分文化特性。Some overloads with default parameters (those that search for a Char in the string instance) perform an ordinal comparison, whereas others (those that search for a string in the string instance) are culture-sensitive. 使用者很難記住哪一種方法使用預設值,也很容易混淆多載。It is difficult to remember which method uses which default value, and easy to confuse the overloads.

  • 依賴預設值來執行方法呼叫的程式碼意圖並不清楚。The intent of the code that relies on default values for method calls is not clear. 在下列使用預設值的範例中,我們難以知道開發人員要進行兩個字串的序數或語言比較,也很難判斷 protocol 和 "http" 之間的大小寫差異是否會造成相等測試傳回 false類別參數的方法多載。In the following example, which relies on defaults, it is difficult to know whether the developer actually intended an ordinal or a linguistic comparison of two strings, or whether a case difference between protocol and "http" might cause the test for equality to return false.

    string protocol = GetProtocol(url);       
    if (String.Equals(protocol, "http")) {
       // ...Code to handle HTTP protocol.
    }
    else {
       throw new InvalidOperationException();
    }
    
    Dim protocol As String = GetProtocol(url)       
    If String.Equals(protocol, "http") Then
       ' ...Code to handle HTTP protocol.
    Else
       Throw New InvalidOperationException()
    End If   
    

一般而言,我們建議您呼叫不依賴預設值的方法,因為它會讓程式碼的意圖模稜兩可。In general, we recommend that you call a method that does not rely on defaults, because it makes the intent of the code unambiguous. 不過,這可讓程式碼更容易閱讀也更容易偵錯與維護。This, in turn, makes the code more readable and easier to debug and maintain. 下列範例將解決先前範例所引發的問題。The following example addresses the questions raised about the previous example. 它會清楚指出使用序數比較,並且忽略大小寫差異。It makes it clear that ordinal comparison is used and that differences in case are ignored.

string protocol = GetProtocol(url);       
if (String.Equals(protocol, "http", StringComparison.OrdinalIgnoreCase)) {
   // ...Code to handle HTTP protocol.
}
else {
   throw new InvalidOperationException();
}
Dim protocol As String = GetProtocol(url)       
If String.Equals(protocol, "http", StringComparison.OrdinalIgnoreCase) Then
   ' ...Code to handle HTTP protocol.
Else
   Throw New InvalidOperationException()
End If   

回到頁首Back to top

字串比較的詳細資料The Details of String Comparison

字串比較是許多字串相關作業的核心,尤其是排序和測試是否相等這類作業。String comparison is the heart of many string-related operations, particularly sorting and testing for equality. 字串依固定順序排序:在已排序的字串清單中,如果 "my" 在 "string" 之前出現,則 "my" 比較起來一定小於或等於 "string"。Strings sort in a determined order: If "my" appears before "string" in a sorted list of strings, "my" must compare less than or equal to "string". 此外,比較也隱含定義相等性。Additionally, comparison implicitly defines equality. 比較作業會針對它認為相等的字串傳回零。The comparison operation returns zero for strings it deems equal. 明言之,就是沒有任一字串比另一個字串更小。A good interpretation is that neither string is less than the other. 最有意義的字串作業包含下列一或兩個程序:和其他字串進行比較,並執行定義完善的排序作業。Most meaningful operations involving strings include one or both of these procedures: comparing with another string, and executing a well-defined sort operation.

注意

您可以下載排序權數資料表,該文字檔集合包含在 Windows 作業系統排序及比較作業中使用的字元權數資訊,以及下載預設 Unicode 定序元素資料表 (適用於 Linux 和 macOS 的最新版本排序權數資料表)。You can download the Sorting Weight Tables, a set of text files that contain information on the character weights used in sorting and comparison operations for Windows operating systems, and the Default Unicode Collation Element Table, the latest version of the sort weight table for Linux and macOS. Linux 和 macOS 上的特定版本排序權數資料表,取決於在系統上安裝的 International Components for Unicode 程式庫。The specific version of the sort weight table on Linux and macOS depends on the version of the International Components for Unicode libraries installed on the system. 如需其實作的 ICU 版本及 Unicode 版本詳細資訊,請參閱下載 ICUFor information on ICU versions and the Unicode versions that they implement, see Downloading ICU.

不過,評估兩個字串是否相等或決定排序順序不會產生單一的正確結果,而要取決於用來比較字串的準則而定。However, evaluating two strings for equality or sort order does not yield a single, correct result; the outcome depends on the criteria used to compare the strings. 特別是,如果字串比較是序數或根據目前文化特性或不因文化特性而異的大小寫與排序慣例 (根據英文語言的無從驗證地區設定文化特性),則可能會產生不同的結果。In particular, string comparisons that are ordinal or that are based on the casing and sorting conventions of the current culture or the invariant culture (a locale-agnostic culture based on the English language) may produce different results.

此外,使用不同版本 .NET 或使用不同作業系統或作業系統版本上 .NET 所做的字串比較,可能會傳回不同的結果。In addition, string comparisons using different versions of .NET or using .NET on different operating systems or operating system versions may return different results. 如需詳細資訊,請參閱字串及 Unicode 標準For more information, see Strings and the Unicode Standard.

使用目前文化特性的字串比較String Comparisons that Use the Current Culture

比較字串時,其中一個準則需使用目前文化特性的慣例。One criterion involves using the conventions of the current culture when comparing strings. 如果比較是以目前文化特性為依據,就會使用執行緒的目前文化特性或地區設定。Comparisons that are based on the current culture use the thread's current culture or locale. 如果使用者未設定文化特性,則會預設為控制台 [地區選項] 視窗中的設定。If the culture is not set by the user, it defaults to the setting in the Regional Options window in Control Panel. 當資料是語言相關資料,以及資料會反映區分文化特性的使用者互動時,請一律使用以目前文化特性為根據的比較。You should always use comparisons that are based on the current culture when data is linguistically relevant, and when it reflects culture-sensitive user interaction.

不過,當文化特性變更時,.NET 中的比較和大小寫行為也會有所變更。However, comparison and casing behavior in .NET changes when the culture changes. 當執行應用程式的電腦其文化特性不同於開發應用程式的電腦時,或執行中的執行緒變更其文化特性時,會發生這種情況。This happens when an application executes on a computer that has a different culture than the computer on which the application was developed, or when the executing thread changes its culture. 此種行為有其用意,但對許多開發人員來說並不容易注意到。This behavior is intentional, but it remains non-obvious to many developers. 下列範例說明美國英文 ("en-US") 和瑞典文 ("sv-SE") 文化特性之間排序次序的差異。The following example illustrates differences in sort order between the U.S. English ("en-US") and Swedish ("sv-SE") cultures. 請注意單字 "ångström"、"Windows" 和 "Visual Studio" 出現在已排序的字串陣列中的不同位置。Note that the words "ångström", "Windows", and "Visual Studio" appear in different positions in the sorted string arrays.

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
Imports System.Globalization
Imports System.Threading

Module Example
   Public Sub Main()
      Dim values() As String = { "able", "ångström", "apple", _
                                 "Æble", "Windows", "Visual Studio" }
      Array.Sort(values)
      DisplayArray(values)

      ' Change culture to Swedish (Sweden).
      Dim originalCulture As String = 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)
    End Sub
    
    Private Sub DisplayArray(values() As String)
      Console.WRiteLine("Sorting using the {0} culture:", _ 
                        CultureInfo.CurrentCulture.Name)
      For Each value As String In values
         Console.WriteLine("   {0}", value)
      Next
      Console.WriteLine()   
    End Sub
End Module
' 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

如果比較是使用目前文化特性且不區分大小寫,這類比較和區分文化特性的比較相同 (除了這些比較會忽略執行緒目前文化特性的大小寫要求以外)。Case-insensitive comparisons that use the current culture are the same as culture-sensitive comparisons, except that they ignore case as dictated by the thread's current culture. 這種行為可能會以排序順序來呈現。This behavior may manifest itself in sort orders as well.

下列方法預設為執行使用目前文化特性之語意的比較:Comparisons that use current culture semantics are the default for the following methods:

在任何情況下,都建議您呼叫具有 StringComparison 參數的多載,以讓呼叫方法的目的更清晰。In any case, we recommend that you call an overload that has a StringComparison parameter to make the intent of the method call clear.

若以語言方式解譯非語言式的字串資料,或使用其他文化特性的慣例解譯來自特定文化特性的字串資料時,可能會出現微妙或不太微妙的 Bug。Subtle and not so subtle bugs can emerge when non-linguistic string data is interpreted linguistically, or when string data from a particular culture is interpreted using the conventions of another culture. 標準範例是土耳其文 I 的問題。The canonical example is the Turkish-I problem.

對於幾乎所有拉丁字母 (包括美國英文),字元 "i" (\u0069) 是字元 "I" (\u0049) 的小寫。For nearly all Latin alphabets, including U.S. English, the character "i" (\u0069) is the lowercase version of the character "I" (\u0049). 這種大小寫規則很快成為以這種文化特性進行程式設計的人所採用的預設規則。This casing rule quickly becomes the default for someone programming in such a culture. 不過,土耳其文 ("tr-TR") 字母包括「加上一點的 I」字元 "İ" (\u0130),也就是 "i" 的大寫。However, the Turkish ("tr-TR") alphabet includes an "I with a dot" character "İ" (\u0130), which is the capital version of "i". 土耳其文中也包含「不含點的 i 」字元,"ı" (\u0131),其大寫為 "I"。Turkish also includes a lowercase "i without a dot" character, "ı" (\u0131), which capitalizes to "I". 亞塞拜然 ("az") 文化特性中也會發生這種行為。This behavior occurs in the Azerbaijani ("az") culture as well.

因此,關於將 "i" 轉換成大寫或將 "I" 轉換成小寫的假設並不適用。Therefore, assumptions made about capitalizing "i" or lowercasing "I" are not valid among all cultures. 如果您針對字串比較常式使用預設多載,就會受限於文化特性之間的差異。If you use the default overloads for string comparison routines, they will be subject to variance between cultures. 如果要比較的資料為非語言式,使用預設多載可能產生非預期的結果,如下列嘗試執行字串 "file" 和 "FILE" 之不區分大小寫的比較範例所示。If the data to be compared is non-linguistic, using the default overloads can produce undesirable results, as the following attempt to perform a case-insensitive comparison of the strings "file" and "FILE" illustrates.

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
Imports System.Globalization
Imports System.Threading

Module Example
   Public Sub Main()
      Dim 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, Nothing))
      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, Nothing))
   End Sub
End Module
' The example displays the following output:
'       Culture = English (United States)
'       (file == FILE) = True
'       
'       Culture = Turkish (Turkey)
'       (file == FILE) = False

如果不小心將文化特性用於安全性相關設定 (如下列範例所示),這項比較可能會造成重大的問題。This comparison could cause significant problems if the culture is inadvertently used in security-sensitive settings, as in the following example. 如果目前的文化特性是美國英文,方法呼叫 (如 IsFileURI("file:")) 會傳回 true;但若目前的文化特性為土耳其文,則傳回 falseA method call such as IsFileURI("file:") returns true if the current culture is U.S. English, but false if the current culture is Turkish. 因此,在土耳其文的系統中,有心人士可以使用開頭為 "FILE:" 的 URI,來規避封鎖存取不區分大小寫之 URI 的安全性措施。Thus, on Turkish systems, someone could circumvent security measures that block access to case-insensitive URIs that begin with "FILE:".

public static bool IsFileURI(String path) 
{
   return path.StartsWith("FILE:", true, null);
}
Public Shared Function IsFileURI(path As String) As Boolean 
   Return path.StartsWith("FILE:", True, Nothing)
End Function

在此情況下,由於 "file:" 應解譯為非語言式、不區分文化特性的識別項,因此程式碼應改為下列範例:In this case, because "file:" is meant to be interpreted as a non-linguistic, culture-insensitive identifier, the code should instead be written as shown in the following example.

public static bool IsFileURI(string path) 
{
   return path.StartsWith("FILE:", StringComparison.OrdinalIgnoreCase);
}
Public Shared Function IsFileURI(path As String) As Boolean 
    Return path.StartsWith("FILE:", StringComparison.OrdinalIgnoreCase)
End Function

序數字串作業Ordinal String Operations

在方法呼叫中指定 StringComparison.OrdinalStringComparison.OrdinalIgnoreCase 值意謂著非語言比較,其中忽略自然語言的特性。Specifying the StringComparison.Ordinal or StringComparison.OrdinalIgnoreCase value in a method call signifies a non-linguistic comparison in which the features of natural languages are ignored. 若使用這類 StringComparison 值來呼叫方法,方法就會以簡單的位元組比較來進行字串作業決策,而不以文化特性參數化的大小寫或對等項目資料表為根據。Methods that are invoked with these StringComparison values base string operation decisions on simple byte comparisons instead of casing or equivalence tables that are parameterized by culture. 在大部分情況下,這種方法非常適合預期的字串解譯,同時可讓程式碼更快速、可靠。In most cases, this approach best fits the intended interpretation of strings while making code faster and more reliable.

序數比較是一種字串比較,其會比較每個字串的每個位元組,而不進行語言解譯;例如,"windows" 不符合 "Windows"。Ordinal comparisons are string comparisons in which each byte of each string is compared without linguistic interpretation; for example, "windows" does not match "Windows". 這基本上是對 C 執行階段 strcmp 函式的呼叫。This is essentially a call to the C runtime strcmp function. 如果內容指出字串應該完全相符,或要求保守的比對原則時,請使用這項比較。Use this comparison when the context dictates that strings should be matched exactly or demands conservative matching policy. 此外,序數比較在決定結果時不會套用任何語言規則,因此是最快速的比較作業。Additionally, ordinal comparison is the fastest comparison operation because it applies no linguistic rules when determining a result.

.NET 中的字串可以包含內嵌的 Null 字元。Strings in .NET can contain embedded null characters. 序數和區分文化特性的比較 (包括使用不因文化特性而異的比較) 其中一個最明顯差異在於,內嵌 Null 字元在字串中的處理方式。One of the clearest differences between ordinal and culture-sensitive comparison (including comparisons that use the invariant culture) concerns the handling of embedded null characters in a string. 當您使用 String.CompareString.Equals 方法來執行區分文化特性的比較 (包括使用不因文化特性而異的比較) 時,會忽略這些字元。These characters are ignored when you use the String.Compare and String.Equals methods to perform culture-sensitive comparisons (including comparisons that use the invariant culture). 如此一來,在區分文化特性的比較中,即可將包含內嵌 Null 字元的字串視為等於不含這類字元的字串。As a result, in culture-sensitive comparisons, strings that contain embedded null characters can be considered equal to strings that do not.

重要

雖然字串比較方法可以忽略內嵌的 Null 字元,但 String.ContainsString.EndsWithString.IndexOfString.LastIndexOfString.StartsWith 之類的字串搜尋方法就不能這麼做了。Although string comparison methods disregard embedded null characters, string search methods such as String.Contains, String.EndsWith, String.IndexOf, String.LastIndexOf, and String.StartsWith do not.

下列範例會針對字串 "Aa" 以及 "A" 和 "a" 之間包含數個內嵌 Null 字元的類似字串,執行區分文化特性比較,並示範如何讓兩個字串被視為相等。The following example performs a culture-sensitive comparison of the string "Aa" with a similar string that contains several embedded null characters between "A" and "a", and shows how the two strings are considered equal.

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
Module Example
   Public Sub Main()
      Dim str1 As String = "Aa"
      Dim str2 As String = "A" + New String(Convert.ToChar(0), 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))
   End Sub
   
   Private Function ShowBytes(str As String) As String
      Dim hexString As String = String.Empty
      For ctr As Integer = 0 To str.Length - 1
         Dim result As String = String.Empty
         result = Convert.ToInt32(str.Chars(ctr)).ToString("X4")
         result = " " + result.Substring(0,2) + " " + result.Substring(2, 2)
         hexString += result
      Next
      Return hexString.Trim()
   End Function
End Module

不過,當您使用序數比較時,字串就不會被視為相等,如下列範例所示。However, the strings are not considered equal when you use ordinal comparison, as the following example shows.

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
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

第二種最保守的方式是不區分大小寫的序數比較。Case-insensitive ordinal comparisons are the next most conservative approach. 這些比較會忽略大部分的大小寫;例如,"windows" 符合 "Windows"。These comparisons ignore most casing; for example, "windows" matches "Windows". 當處理 ASCII 字元時,這項原則相當於 StringComparison.Ordinal,不過它會忽略一般 ASCII 大小寫。When dealing with ASCII characters, this policy is equivalent to StringComparison.Ordinal, except that it ignores the usual ASCII casing. 因此,[A, Z] (\u0041-\u005A) 中的任何字元會符合 [a,z] (\u0061-\007A) 中的對應字元。Therefore, any character in [A, Z] (\u0041-\u005A) matches the corresponding character in [a,z] (\u0061-\007A). ASCII 範圍之外的大小寫會使用不因文化特性而異的資料表。Casing outside the ASCII range uses the invariant culture's tables. 因此,下列比較:Therefore, the following comparison:

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

相當於下列比較 (而且更快):is equivalent to (but faster than) this comparison:

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

這些比較仍非常快速。These comparisons are still very fast.

注意

StringComparison.OrdinalIgnoreCase最能代表檔案系統、登錄機碼和值及環境變數的字串行為。The string behavior of the file system, registry keys and values, and environment variables is best represented by StringComparison.OrdinalIgnoreCase.

StringComparison.OrdinalStringComparison.OrdinalIgnoreCase 兩者都直接使用二進位值,最適合用來比對。Both StringComparison.Ordinal and StringComparison.OrdinalIgnoreCase use the binary values directly, and are best suited for matching. 當您不確定比較設定時,請使用這兩個值的其中一個。When you are not sure about your comparison settings, use one of these two values. 不過,因為它們是以位元組為單位逐一比較,所以不會依照語言排序次序來排序 (就像英文字典一樣),而是採用二進位排序次序。However, because they perform a byte-by-byte comparison, they do not sort by a linguistic sort order (like an English dictionary) but by a binary sort order. 因此,在大多數內容當中,使用者看到的結果可能很奇怪。The results may look odd in most contexts if displayed to users.

不包含 String.Equals 引數 (包括等號比較運算子) 的 StringComparison 多載是以序數語意為預設。Ordinal semantics are the default for String.Equals overloads that do not include a StringComparison argument (including the equality operator). 在任何情況下,我們建議您呼叫具有 StringComparison 參數的多載。In any case, we recommend that you call an overload that has a StringComparison parameter.

使用不因文化特性而異的字串作業String Operations that Use the Invariant Culture

採用不因文化特性而異的比較會使用靜態 CompareInfo 屬性傳回的 CultureInfo.InvariantCulture 屬性。Comparisons with the invariant culture use the CompareInfo property returned by the static CultureInfo.InvariantCulture property. 這種行為在所有系統上都相同,它會將其範圍之外的任何字元轉譯成它認為是相等非變異字元的字元。This behavior is the same on all systems; it translates any characters outside its range into what it believes are equivalent invariant characters. 這項原則很適合跨文化特性來維護一套字串行為,但通常會產生非預期的結果。This policy can be useful for maintaining one set of string behavior across cultures, but it often provides unexpected results.

採用不因文化特性而異的不分區大小寫比較,也會使用靜態 CompareInfo 屬性所傳回的靜態 CultureInfo.InvariantCulture 屬性來取得比較資訊。Case-insensitive comparisons with the invariant culture use the static CompareInfo property returned by the static CultureInfo.InvariantCulture property for comparison information as well. 這些轉譯的字元之間的任何大小寫差異都會被忽略。Any case differences among these translated characters are ignored.

使用 StringComparison.InvariantCultureStringComparison.Ordinal 的比較在 ASCII 字串上的運作方式完全相同。Comparisons that use StringComparison.InvariantCulture and StringComparison.Ordinal work identically on ASCII strings. 不過,對於必須解譯成一組位元組的字串, StringComparison.InvariantCulture 所做的語言決策就可能不適合。However, StringComparison.InvariantCulture makes linguistic decisions that might not be appropriate for strings that have to be interpreted as a set of bytes. CultureInfo.InvariantCulture.CompareInfo 物件使 Compare 方法將多組字元解譯成相等。The CultureInfo.InvariantCulture.CompareInfo object makes the Compare method interpret certain sets of characters as equivalent. 例如,下列等式在不因國別而異的文化特性之下有效:For example, the following equivalence is valid under the invariant culture:

InvariantCulture: a + ̊ = åInvariantCulture: a + ̊ = å

拉丁小寫字母 A 字元 "a" (\u0061) 在緊鄰著結合上圓圈字元 "+ " ̊" (\u030a) 時,解譯成拉丁小寫字母 A 帶上圓圈字元 "å" (\u00e5)。The LATIN SMALL LETTER A character "a" (\u0061), when it is next to the COMBINING RING ABOVE character "+ " ̊" (\u030a), is interpreted as the LATIN SMALL LETTER A WITH RING ABOVE character "å" (\u00e5). 如下列範例如示,這種行為不同於序數比較。As the following example shows, this behavior differs from ordinal comparison.

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      
Dim separated As String = ChrW(&h61) + ChrW(&h30a)
Dim combined As String = ChrW(&he5)
      
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 或可能出現像 "å" 一樣的組合的其他任何字串時,序數比較仍然會展現最明確和最適當的行為。When interpreting file names, cookies, or anything else where a combination such as "å" can appear, ordinal comparisons still offer the most transparent and fitting behavior.

權衡來看,不因國別而異的文化特性只有非常少的屬性,因此適合用於比較。On balance, the invariant culture has very few properties that make it useful for comparison. 不過,它會以語言相關的方式進行比較,這樣無法保證符號完全相等,因而不適合在任何文化特性中顯示。It does comparison in a linguistically relevant manner, which prevents it from guaranteeing full symbolic equivalence, but it is not the choice for display in any culture. 使用 StringComparison.InvariantCulture 來比較有幾個理由,其中之一是保持已排序的資料在不同文化特性下顯示時完全相同。One of the few reasons to use StringComparison.InvariantCulture for comparison is to persist ordered data for a cross-culturally identical display. 例如,如果包含已排序之顯示識別項清單的大型資料檔案伴隨一個應用程式,則加入這份清單需要插入非變異樣式排序。For example, if a large data file that contains a list of sorted identifiers for display accompanies an application, adding to this list would require an insertion with invariant-style sorting.

回到頁首Back to top

針對方法呼叫選擇 StringComparison 成員Choosing a StringComparison Member for Your Method Call

下表列出語意字串內容與 StringComparison 列舉成員的對應。The following table outlines the mapping from semantic string context to a StringComparison enumeration member.

資料Data 行為Behavior 對應的 System.StringComparisonCorresponding System.StringComparison

valuevalue
區分大小寫的內部識別項。Case-sensitive internal identifiers.

在標準中區分大小寫的識別項,例如 XML 和 HTTP。Case-sensitive identifiers in standards such as XML and HTTP.

區分大小寫的安全性相關設定。Case-sensitive security-related settings.
位元組完全相符的非語言識別項。A non-linguistic identifier, where bytes match exactly. Ordinal
不區分大小寫的內部識別項。Case-insensitive internal identifiers.

在標準中區分大小寫的識別項,例如 XML 和 HTTP。Case-insensitive identifiers in standards such as XML and HTTP.

檔案路徑。File paths.

登錄機碼和值。Registry keys and values.

環境變數。Environment variables.

資源識別項 (例如控制代碼名稱)。Resource identifiers (for example, handle names).

不區分大小寫的安全性相關設定。Case-insensitive security-related settings.
大小寫不重要的非語言識別項,特別是大部分 Windows 系統服務中儲存的資料。A non-linguistic identifier, where case is irrelevant; especially data stored in most Windows system services. OrdinalIgnoreCase
某些永續性、語言相關的資料。Some persisted, linguistically relevant data.

需要固定排序次序之語言資料的顯示。Display of linguistic data that requires a fixed sort order.
仍與語言相關之不區分文化特性的資料。Culturally agnostic data that still is linguistically relevant. InvariantCulture

-或--or-

InvariantCultureIgnoreCase
向使用者顯示的資料。Data displayed to the user.

大部分的使用者輸入。Most user input.
需要當地語言自訂的資料。Data that requires local linguistic customs. CurrentCulture

-或--or-

CurrentCultureIgnoreCase

回到頁首Back to top

.NET 中常用的字串比較方法Common String Comparison Methods in .NET

下列各節描述字串比較最常用的方法。The following sections describe the methods that are most commonly used for string comparison.

String.CompareString.Compare

預設解譯: StringComparison.CurrentCultureDefault interpretation: StringComparison.CurrentCulture.

由於是字串解譯最主要的作業,這些方法呼叫的所有執行個體都應該經過檢查,以決定字串應該根據目前文化特性來解譯,還是與文化特性分開 (符號形式)。As the operation most central to string interpretation, all instances of these method calls should be examined to determine whether strings should be interpreted according to the current culture, or dissociated from the culture (symbolically). 通常是後者,所以應該改用 StringComparison.Ordinal 比較。Typically, it is the latter, and a StringComparison.Ordinal comparison should be used instead.

System.Globalization.CompareInfo 屬性傳回的 CultureInfo.CompareInfo 類別也包含 Compare 方法,這個方法透過 CompareOptions 旗標列舉,提供大量比對選項 (序數、忽略空白字元、忽略假名類型等)。The System.Globalization.CompareInfo class, which is returned by the CultureInfo.CompareInfo property, also includes a Compare method that provides a large number of matching options (ordinal, ignoring white space, ignoring kana type, and so on) by means of the CompareOptions flag enumeration.

String.CompareToString.CompareTo

預設解譯: StringComparison.CurrentCultureDefault interpretation: StringComparison.CurrentCulture.

這個方法目前未提供任何指定 StringComparison 類型的多載。This method does not currently offer an overload that specifies a StringComparison type. 通常可以將這個方法轉換成建議的 String.Compare(String, String, StringComparison) 形式。It is usually possible to convert this method to the recommended String.Compare(String, String, StringComparison) form.

實作 IComparableIComparable<T> 介面的類型會實作這個方法。Types that implement the IComparable and IComparable<T> interfaces implement this method. 由於它不提供 StringComparison 參數的選項,所以實作這些型別通常可讓使用者在建構函式中指定 StringComparerBecause it does not offer the option of a StringComparison parameter, implementing types often let the user specify a StringComparer in their constructor. 下列範例定義 FileName 類別,該類別的類別建構函式包含 StringComparer 參數。The following example defines a FileName class whose class constructor includes a StringComparer parameter. 然後在 StringComparer 方法中使用這個 FileName.CompareTo 物件。This StringComparer object is then used in the FileName.CompareTo method.

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);
   }
}
Public Class FileName : Implements IComparable
   Dim fname As String
   Dim comparer As StringComparer 
   
   Public Sub New(name As String, comparer As StringComparer)
      If String.IsNullOrEmpty(name) Then
         Throw New ArgumentNullException("name")
      End If

      Me.fname = name
      
      If comparer IsNot Nothing Then
         Me.comparer = comparer
      Else
         Me.comparer = StringComparer.OrdinalIgnoreCase
      End If      
   End Sub

   Public ReadOnly Property Name As String
      Get
         Return fname
      End Get   
   End Property
   
   Public Function CompareTo(obj As Object) As Integer _
          Implements IComparable.CompareTo
      If obj Is Nothing Then Return 1

      If Not TypeOf obj Is FileName Then
         obj = obj.ToString()
      Else
         obj = CType(obj, FileName).Name
      End If         
      Return comparer.Compare(Me.fname, obj)
   End Function
End Class

String.EqualsString.Equals

預設解譯: StringComparison.OrdinalDefault interpretation: StringComparison.Ordinal.

String 類別可讓您呼叫靜態或執行個體 Equals 方法多載,或使用靜態等號比較運算子,以測試是否相等。The String class lets you test for equality by calling either the static or instance Equals method overloads, or by using the static equality operator. 多載和運算子預設會使用序數比較。The overloads and operator use ordinal comparison by default. 然而,即使您想要執行序數比較,我們還是建議您呼叫明確指定 StringComparison 型別的多載,這樣可以很輕鬆在程式碼中搜尋特定的字串解譯。However, we still recommend that you call an overload that explicitly specifies the StringComparison type even if you want to perform an ordinal comparison; this makes it easier to search code for a certain string interpretation.

String.ToUpper 和 String.ToLowerString.ToUpper and String.ToLower

預設解譯: StringComparison.CurrentCultureDefault interpretation: StringComparison.CurrentCulture.

請小心使用這些方法,因為強制將字串轉換為大寫或小寫,通常是做為比較不區分大小寫字串時的輕微正規化。You should be careful when you use these methods, because forcing a string to a uppercase or lowercase is often used as a small normalization for comparing strings regardless of case. 如果是這樣,請考慮使用不區分大小寫的比較。If so, consider using a case-insensitive comparison.

您也可以使用 String.ToUpperInvariantString.ToLowerInvariant 方法。The String.ToUpperInvariant and String.ToLowerInvariant methods are also available. ToUpperInvariant 是將大小寫正規化的標準方式。ToUpperInvariant is the standard way to normalize case. 使用 StringComparison.OrdinalIgnoreCase 進行的比較在行為上由兩次呼叫所構成:在兩個字串引數上都呼叫 ToUpperInvariant ,然後使用 StringComparison.Ordinal進行比較。Comparisons made using StringComparison.OrdinalIgnoreCase are behaviorally the composition of two calls: calling ToUpperInvariant on both string arguments, and doing a comparison using StringComparison.Ordinal.

特定文化特性中,也可以使用多載將表示該文化特性的 CultureInfo 物件傳遞至這個方法,以轉換為大寫和小寫。Overloads are also available for converting to uppercase and lowercase in a specific culture, by passing a CultureInfo object that represents that culture to the method.

Char.ToUpper 和 Char.ToLowerChar.ToUpper and Char.ToLower

預設解譯: StringComparison.CurrentCultureDefault interpretation: StringComparison.CurrentCulture.

這些方法的運作類似於上一節描述的 String.ToUpperString.ToLower 方法。These methods work similarly to the String.ToUpper and String.ToLower methods described in the previous section.

String.StartsWith 和 String.EndsWithString.StartsWith and String.EndsWith

預設解譯: StringComparison.CurrentCultureDefault interpretation: StringComparison.CurrentCulture.

根據預設,這兩個方法都會執行區分文化特性的比較。By default, both of these methods perform a culture-sensitive comparison.

String.IndexOf 和 String.LastIndexOfString.IndexOf and String.LastIndexOf

預設解譯: StringComparison.CurrentCultureDefault interpretation: StringComparison.CurrentCulture.

這些方法的預設多載在執行比較時,作法並不一致。There is a lack of consistency in how the default overloads of these methods perform comparisons. 所有包含 String.IndexOf 參數的 String.LastIndexOfChar 方法都執行序數比較,但包含 String.IndexOf 參數的預設 String.LastIndexOfString 方法會執行區分文化特性的比較。All String.IndexOf and String.LastIndexOf methods that include a Char parameter perform an ordinal comparison, but the default String.IndexOf and String.LastIndexOf methods that include a String parameter perform a culture-sensitive comparison.

如果您呼叫 String.IndexOf(String)String.LastIndexOf(String) 方法,並將要在目前執行個體中尋找的字串傳遞至這個方法,我們建議您呼叫明確指定 StringComparison 類型的多載。If you call the String.IndexOf(String) or String.LastIndexOf(String) method and pass it a string to locate in the current instance, we recommend that you call an overload that explicitly specifies the StringComparison type. 包含 Char 引數的多載不允許您指定 StringComparison 類型。The overloads that include a Char argument do not allow you to specify a StringComparison type.

回到頁首Back to top

間接執行字串比較的方法Methods that Perform String Comparison Indirectly

有一些以字串比較為主要作業的非字串方法會使用 StringComparer 類型。Some non-string methods that have string comparison as a central operation use the StringComparer type. StringComparer 類別包含六個靜態屬性,這些屬性會傳回 StringComparer 執行個體,而這些執行個體的 StringComparer.Compare 方法可以執行下列類型的字串比較:The StringComparer class includes six static properties that return StringComparer instances whose StringComparer.Compare methods perform the following types of string comparisons:

Array.Sort 和 Array.BinarySearchArray.Sort and Array.BinarySearch

預設解譯: StringComparison.CurrentCultureDefault interpretation: StringComparison.CurrentCulture.

當您將任何資料儲存在集合中,或從檔案或資料庫將保存的資料讀入集合時,切換目前文化特性會使集合中的非變異失效。When you store any data in a collection, or read persisted data from a file or database into a collection, switching the current culture can invalidate the invariants in the collection. Array.BinarySearch 方法假設要搜尋之陣列中的項目已排序。The Array.BinarySearch method assumes that the elements in the array to be searched are already sorted. 若要排序陣列中的任何字串項目, Array.Sort 方法會呼叫 String.Compare 方法來排序個別項目。To sort any string element in the array, the Array.Sort method calls the String.Compare method to order individual elements. 從排序陣列到搜尋其內容這段時間當中,如果文化特性變更,則使用區分文化特性的比較子可能會有危險。Using a culture-sensitive comparer can be dangerous if the culture changes between the time that the array is sorted and its contents are searched. 例如,在下列程式碼中,儲存和擷取作業在 Thread.CurrentThread.CurrentCulture 屬性。For example, in the following code, storage and retrieval operate on the comparer that is provided implicitly by the Thread.CurrentThread.CurrentCulture property. 如果文化特性在呼叫 StoreNamesDoesNameExist之間可能變更,尤其是如果陣列內容在這兩個方法呼叫之間保存在某處,則二進位搜尋可能會失敗。If the culture can change between the calls to StoreNames and DoesNameExist, and especially if the array contents are persisted somewhere between the two method calls, the binary search may fail.

// 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.
}
' Incorrect.
Dim storedNames() As String

Public Sub StoreNames(names() As String)
   Dim index As Integer = 0
   ReDim storedNames(names.Length - 1)
   
   For Each name As String In names
      Me.storedNames(index) = name
      index+= 1
   Next
   
   Array.Sort(names)          ' Line A.
End Sub

Public Function DoesNameExist(name As String) As Boolean
   Return Array.BinarySearch(Me.storedNames, name) >= 0      ' Line B.
End Function

下列範例中顯示建議的變化,其中使用相同的序數 (不區分文化特性) 比較方法來排序和搜尋陣列。A recommended variation appears in the following example, which uses the same ordinal (culture-insensitive) comparison method both to sort and to search the array. 在這兩個範例中,標記為 Line ALine B 的程式碼行中反映程式碼變更。The change code is reflected in the lines labeled Line A and Line B in the two examples.

// 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.
}
' Correct.
Dim storedNames() As String

Public Sub StoreNames(names() As String)
   Dim index As Integer = 0
   ReDim storedNames(names.Length - 1)
   
   For Each name As String In names
      Me.storedNames(index) = name
      index+= 1
   Next
   
   Array.Sort(names, StringComparer.Ordinal)           ' Line A.
End Sub

Public Function DoesNameExist(name As String) As Boolean
   Return Array.BinarySearch(Me.storedNames, name, StringComparer.Ordinal) >= 0      ' Line B.
End Function

如果跨文化特性來保存和移動這項資料,且使用排序向使用者呈現這項資料,則您可以考慮使用 StringComparison.InvariantCulture,這在語言上的表現會提供較佳的使用者輸出,且不受未來文化特性變更所影響。If this data is persisted and moved across cultures, and sorting is used to present this data to the user, you might consider using StringComparison.InvariantCulture, which operates linguistically for better user output but is unaffected by changes in culture. 下列範例修改前兩個範例,改用不因國別而異的文化特性來排序和搜尋陣列。The following example modifies the two previous examples to use the invariant culture for sorting and searching the array.

// 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.
}
' Correct.
Dim storedNames() As String

Public Sub StoreNames(names() As String)
   Dim index As Integer = 0
   ReDim storedNames(names.Length - 1)
   
   For Each name As String In names
      Me.storedNames(index) = name
      index+= 1
   Next
   
   Array.Sort(names, StringComparer.InvariantCulture)           ' Line A.
End Sub

Public Function DoesNameExist(name As String) As Boolean
   Return Array.BinarySearch(Me.storedNames, name, StringComparer.InvariantCulture) >= 0      ' Line B.
End Function

集合範例:雜湊表建構函式Collections Example: Hashtable Constructor

第二個受到字串比較方式而影響作業的範例是雜湊字串。Hashing strings provides a second example of an operation that is affected by the way in which strings are compared.

下列範例將 Hashtable 屬性所傳回的 StringComparer 物件傳遞至 StringComparer.OrdinalIgnoreCase 物件,以將後者物件執行個體化。The following example instantiates a Hashtable object by passing it the StringComparer object that is returned by the StringComparer.OrdinalIgnoreCase property. 因為衍生自 StringComparer 的類別 StringComparer 實作 IEqualityComparer 介面,所以其 GetHashCode 方法會用於計算雜湊資料表中之字串的雜湊程式碼。Because a class StringComparer that is derived from StringComparer implements the IEqualityComparer interface, its GetHashCode method is used to compute the hash code of strings in the hash table.

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);
   }
}
Const initialTableCapacity As Integer = 100
Dim h As Hashtable

Public Sub PopulateFileTable(dir As String)
   h = New Hashtable(initialTableCapacity, _
                     StringComparer.OrdinalIgnoreCase)
                     
   For Each filename As String In Directory.GetFiles(dir)
      h.Add(filename, File.GetCreationTime(filename))
   Next                        
End Sub

Public Sub PrintCreationTime(targetFile As String)
   Dim dt As Object = h(targetFile)
   If dt IsNot Nothing Then
      Console.WriteLine("File {0} was created at {1}.", _
         targetFile, _
         CDate(dt))
   Else
      Console.WriteLine("File {0} does not exist.", targetFile)
   End If
End Sub  

回到頁首Back to top

顯示和保存格式化的資料Displaying and Persisting Formatted Data

當您向使用者顯示非字串資料 (例如數字及日期和時間) 時,請採用使用者的文化特性設定進行格式化。When you display non-string data such as numbers and dates and times to users, format them by using the user's cultural settings. 根據預設,下列所有項目都會在格式化作業中使用目前執行緒文化特性:By default, the following all use the current thread culture in formatting operations:

若要明確指定應使用指定的文化特性慣例或不因文化特性而異來格式化字串,您可以執行下列動作:To explicitly specify that a string should be formatted by using the conventions of a designated culture or the invariant culture, you can do the following:

  • 使用 String.FormatToString 方法時,呼叫具有 provider 參數的多載 (例如 String.Format(IFormatProvider, String, Object[])DateTime.ToString(IFormatProvider)),並為該多載傳遞給 CultureInfo.CurrentCulture 屬性 (表示所需文化特性的 CultureInfo 執行個體) 或 CultureInfo.InvariantCulture 屬性。When using the String.Format and ToString methods, call an overload that has a provider parameter, such as String.Format(IFormatProvider, String, Object[]) or DateTime.ToString(IFormatProvider), and pass it the CultureInfo.CurrentCulture property, a CultureInfo instance that represents the desired culture, or the CultureInfo.InvariantCulture property.

  • 針對字串串連,不允許編譯器執行任何隱含的轉換。For string concatenation, do not allow the compiler to perform any implicit conversions. 反之,藉由呼叫具有 provider 參數的 ToString 多載來執行明確轉換。Instead, perform an explicit conversion by calling a ToString overload that has a provider parameter. 例如,在將 Double 值轉換為下列 C# 程式碼中的字串時,編譯器會隱含地使用目前文化特性:For example, the compiler implicitly uses the current culture when converting a Double value to a string in the following C# code:

    string concat1 = "The amount is " + 126.03 + ".";
    Console.WriteLine(concat1);
    

    您可以藉由呼叫 Double.ToString(IFormatProvider) 方法來明確指定在轉換中使用格式化慣例的文化特性,如下列 C# 程式碼所示:Instead, you can explicitly specify the culture whose formatting conventions are used in the conversion by calling the Double.ToString(IFormatProvider) method, as the following C# code does:

    string concat2 = "The amount is " + 126.03.ToString(CultureInfo.InvariantCulture) + ".";
    Console.WriteLine(concat2);
    
  • 針對字串內插補點,請將插入字串指派給 FormattableString 執行個體,而非 StringFor string interpolation, rather than assigning an interpolated string to a String instance, assign it to a FormattableString. 接著,您可以呼叫其 FormattableString.ToString() 方法產生會反映目前文化特性慣例的結果字串,也可以呼叫 FormattableString.ToString(IFormatProvider) 方法產生會反映指定文化特性慣例的結果字串。You can then call its FormattableString.ToString() method produce a result string that reflects the conventions of the current culture, or you can call the FormattableString.ToString(IFormatProvider) method to produce a result string that reflects the conventions of a specified culture. 您也可以將可格式化字串傳遞給靜態的 FormattableString.Invariant 方法,以產生會反映不因文化特性而異之慣例的結果字串。You can also pass the formattable string to the static FormattableString.Invariant method to produce a result string that reflects the conventions of the invariant culture. 下列範例將示範這個方法。The following example illustrates this approach. (此範例的輸出反映 en-US 目前文化特性。)(The output from the example reflects a current culture of en-US.)

    using System;
    using System.Globalization;
    
    class Program
    {
        static void Main()
        {
            Decimal value = 126.03m;
            FormattableString amount = $"The amount is {value:C}"; 
            Console.WriteLine(amount.ToString());
            Console.WriteLine(amount.ToString(new CultureInfo("fr-FR")));
            Console.WriteLine(FormattableString.Invariant(amount));
        }
    }
    // The example displays the following output:
    //    The amount is $126.03
    //    The amount is 126,03 €
    //    The amount is ¤126.03
    
    Imports System.Globalization
    
    Module Program
        Sub Main()
            Dim value As Decimal = 126.03
            Dim amount As FormattableString = $"The amount is {value:C}" 
            Console.WriteLine(amount.ToString())
            Console.WriteLine(amount.ToString(new CultureInfo("fr-FR")))
            Console.WriteLine(FormattableString.Invariant(amount))
        End Sub
    End Module
    ' The example displays the following output:
    '    The amount is $126.03
    '    The amount is 126,03 €
    '    The amount is ¤126.03
    

您可將非字串資料保存為二進位資料或格式化的資料。You can persist non-string data either as binary data or as formatted data. 如果您選擇將它儲存為格式化的資料,您應該呼叫包含 provider 參數之格式化方法的多載,並將 CultureInfo.InvariantCulture 屬性傳遞給它。If you choose to save it as formatted data, you should call a formatting method overload that includes a provider parameter and pass it the CultureInfo.InvariantCulture property. 針對獨立於文化特性和機器的格式化資料,不因國別而異的文化特性可提供一致的格式。The invariant culture provides a consistent format for formatted data that is independent of culture and machine. 相較之下,如果資料並非使用不因國別而異的文化特性,而是使用其他文化特性來進行格式化,這類資料的保存會有許多限制:In contrast, persisting data that is formatted by using cultures other than the invariant culture has a number of limitations:

  • 如果在具有不同文化特性的系統上擷取資料,或者,目前系統的使用者變更目前的文化特性,並嘗試擷取資料時,該資料可能會無法使用。The data is likely to be unusable if it is retrieved on a system that has a different culture, or if the user of the current system changes the current culture and tries to retrieve the data.

  • 特定電腦上的文化特性屬性可能不同於標準值。The properties of a culture on a specific computer can differ from standard values. 使用者可以隨時自訂區分文化特性的顯示設定。At any time, a user can customize culture-sensitive display settings. 因為這個緣故,使用者自訂文化特性設定之後,可能無法讀取系統上儲存的格式化資料。Because of this, formatted data that is saved on a system may not be readable after the user customizes cultural settings. 格式化資料在電腦之間的可攜性可能會有更多限制。The portability of formatted data across computers is likely to be even more limited.

  • 當數字或日期和時間格式的規範標準 (不論全球、地區或國際標準) 隨著時間變更時,這些變更會併入 Windows 作業系統更新當中。International, regional, or national standards that govern the formatting of numbers or dates and times change over time, and these changes are incorporated into Windows operating system updates. 當格式化慣例改變時,使用先前慣例來格式化的資料可能會變成無法讀取。When formatting conventions change, data that was formatted by using the previous conventions may become unreadable.

下列範例說明使用區分文化特性的格式來保存資料時產生的可攜性限制。The following example illustrates the limited portability that results from using culture-sensitive formatting to persist data. 此範例會將日期和時間值陣列儲存至檔案。The example saves an array of date and time values to a file. 這些值是使用英文 (美國) 文化特性的慣例來進行格式化。These are formatted by using the conventions of the English (United States) culture. 當應用程式將目前執行緒文化特性變更為法文 (瑞士) 後,它會嘗試使用目前文化特性的格式化慣例來讀取儲存的值。After the application changes the current thread culture to French (Switzerland), it tries to read the saved values by using the formatting conventions of the current culture. 嘗試讀取這兩樣資料項目後,會擲回 FormatException 例外狀況,且日期陣列現在會包含兩個等於 MinValue的不正確項目。The attempt to read two of the data items throws a FormatException exception, and the array of dates now contains two incorrect elements that are equal to MinValue.

using System;
using System.Globalization;
using System.IO;
using System.Text;
using System.Threading;

public class Example
{
   private static string filename = @".\dates.dat";

   public static void Main()
   {
      DateTime[] dates = { new DateTime(1758, 5, 6, 21, 26, 0), 
                           new DateTime(1818, 5, 5, 7, 19, 0), 
                           new DateTime(1870, 4, 22, 23, 54, 0),  
                           new DateTime(1890, 9, 8, 6, 47, 0), 
                           new DateTime(1905, 2, 18, 15, 12, 0) }; 
      // Write the data to a file using the current culture.
      WriteData(dates);
      // Change the current culture.
      Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture("fr-CH");
      // Read the data using the current culture.
      DateTime[] newDates = ReadData();
      foreach (var newDate in newDates)
         Console.WriteLine(newDate.ToString("g"));
   }
   
   private static void WriteData(DateTime[] dates) 
   {
      StreamWriter sw = new StreamWriter(filename, false, Encoding.UTF8);    
      for (int ctr = 0; ctr < dates.Length; ctr++) {
         sw.Write("{0}", dates[ctr].ToString("g", CultureInfo.CurrentCulture));
         if (ctr < dates.Length - 1) sw.Write("|");   
      }      
      sw.Close();
   }
   
   private static DateTime[] ReadData() 
   {
      bool exceptionOccurred = false;
           
      // Read file contents as a single string, then split it.
      StreamReader sr = new StreamReader(filename, Encoding.UTF8);
      string output = sr.ReadToEnd();
      sr.Close();   

      string[] values = output.Split( new char[] { '|' } );
      DateTime[] newDates = new DateTime[values.Length]; 
      for (int ctr = 0; ctr < values.Length; ctr++) {
         try {
            newDates[ctr] = DateTime.Parse(values[ctr], CultureInfo.CurrentCulture);
         }
         catch (FormatException) {
            Console.WriteLine("Failed to parse {0}", values[ctr]);
            exceptionOccurred = true;
         }
      }      
      if (exceptionOccurred) Console.WriteLine();
      return newDates;
   }
}
// The example displays the following output:
//       Failed to parse 4/22/1870 11:54 PM
//       Failed to parse 2/18/1905 3:12 PM
//       
//       05.06.1758 21:26
//       05.05.1818 07:19
//       01.01.0001 00:00
//       09.08.1890 06:47
//       01.01.0001 00:00
//       01.01.0001 00:00
Imports System.Globalization
Imports System.IO
Imports System.Text
Imports System.Threading

Module Example
   Private filename As String = ".\dates.dat"
   
   Public Sub Main()
      Dim dates() As Date = { #5/6/1758 9:26PM#, #5/5/1818 7:19AM#, _ 
                              #4/22/1870 11:54PM#, #9/8/1890 6:47AM#, _ 
                              #2/18/1905 3:12PM# }
      ' Write the data to a file using the current culture.
      WriteData(dates)
      ' Change the current culture.
      Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture("fr-CH")
      ' Read the data using the current culture.
      Dim newDates() As Date = ReadData()
      For Each newDate In newDates
         Console.WriteLine(newDate.ToString("g"))
      Next
   End Sub
   
   Private Sub WriteData(dates() As Date)
      Dim sw As New StreamWriter(filename, False, Encoding.Utf8)    
      For ctr As Integer = 0 To dates.Length - 1
         sw.Write("{0}", dates(ctr).ToString("g", CultureInfo.CurrentCulture))
         If ctr < dates.Length - 1 Then sw.Write("|")   
      Next      
      sw.Close()
   End Sub
   
   Private Function ReadData() As Date()
      Dim exceptionOccurred As Boolean = False
           
      ' Read file contents as a single string, then split it.
      Dim sr As New StreamReader(filename, Encoding.Utf8)
      Dim output As String = sr.ReadToEnd()
      sr.Close()   

      Dim values() As String = output.Split( {"|"c } )
      Dim newDates(values.Length - 1) As Date 
      For ctr As Integer = 0 To values.Length - 1
         Try
            newDates(ctr) = DateTime.Parse(values(ctr), CultureInfo.CurrentCulture)
         Catch e As FormatException
            Console.WriteLine("Failed to parse {0}", values(ctr))
            exceptionOccurred = True
         End Try
      Next      
      If exceptionOccurred Then Console.WriteLine()
      Return newDates
   End Function
End Module
' The example displays the following output:
'       Failed to parse 4/22/1870 11:54 PM
'       Failed to parse 2/18/1905 3:12 PM
'       
'       05.06.1758 21:26
'       05.05.1818 07:19
'       01.01.0001 00:00
'       09.08.1890 06:47
'       01.01.0001 00:00
'       01.01.0001 00:00
'

不過,如果您在 CultureInfo.CurrentCultureCultureInfo.InvariantCulture 的呼叫中將 DateTime.ToString(String, IFormatProvider) 屬性取代為 DateTime.Parse(String, IFormatProvider),則保存的日期和時間資料將會成功還原,如下列輸出所示。However, if you replace the CultureInfo.CurrentCulture property with CultureInfo.InvariantCulture in the calls to DateTime.ToString(String, IFormatProvider) and DateTime.Parse(String, IFormatProvider), the persisted date and time data is successfully restored, as the following output shows.

06.05.1758 21:26  
05.05.1818 07:19  
22.04.1870 23:54  
08.09.1890 06:47  
18.02.1905 15:12  

另請參閱See also