本文只介紹了比較方法,但是EndsWith,IndexOf等方法均采用相同的過程,先設置CultureInfo(一般情況下調用當前線程的CultureInfo,該語言文化可以通過控制面板設置),然后調用CultureInfo實例下面的CompareInfo屬性,實例化語言/國家的CompareInfo實例,並調用對應的字符串操作方法.
比較兩個字符串是最常見的字符串操作.一般應為兩個原因要比較字符串:判斷相等性或者排序(通常是為了顯示給用戶看).判斷字符串相等性或者排序時,強烈建議調用String類定義的以下方法之一,在介紹比較方法之前,下面幾個參數類型必須清楚它們的含義:
StringComparison枚舉:
[ComVisible(true)] public enum StringComparison { /// <summary> /// 使用區分區域性的排序規則對字符串進行排序和當前區域性對字符串進行比較(根據當前的語言文化對字符串進行排序。然后根據當前區域性對字符串進行比較,不忽略大小寫) /// </summary> CurrentCulture = 0, /// <summary> /// 使用區分區域性的排序規則對字符串進行排序和當前區域性對字符串進行比較(根據當前的語言文化對字符串進行排序。然后根據當前區域性對字符串進行比較,忽略大小寫) /// </summary> CurrentCultureIgnoreCase = 1, /// <summary> /// 使用區分區域性的排序規則對字符串進行排序。固定區域性對字符串進行比較(根據當前的語言文化對字符串進行排序。然后根據固定語言文化對字符串進行比較,不忽略大小寫) /// </summary> InvariantCulture = 2, /// <summary> /// 使用區分區域性的排序規則對字符串進行排序。固定區域性對字符串進行比較(根據當前的語言文化對字符串進行排序。然后根據固定語言文化對字符串進行比較,忽略大小寫) /// </summary> InvariantCultureIgnoreCase = 3, /// <summary> /// 忽略語言文化,使用序號(二進制)排序規則比較字符串。 /// </summary> Ordinal = 4, /// <summary> /// 忽略語言文化,通過使用序號(二進制)區分區域性的排序規則並忽略所比較的字符串的大小寫,來比較字符串。 /// </summary> OrdinalIgnoreCase = 5 }
CompareOptions枚舉:
字符串比較的規則,一般在設置完比較字符串的語言文化背景之后,在設置該規則.
/// <summary> /// 定義要使用的字符串比較選項 System.Globalization.CompareInfo /// </summary> [Flags] public enum CompareOptions { /// <summary> /// 指示字符串比較的默認選項設置。 /// </summary> None = 0, /// <summary> /// 指示字符串比較必須忽略大小寫。 /// </summary> IgnoreCase = 1, /// <summary> /// 指示字符串比較必須忽略非空格組合字符,如標注字符。 Unicode Standard 將組合字符定義為與基的字符,以生成新的字符組合的字符。 非空格組合字符不在呈現時本身會占用空間位置。 /// </summary> IgnoreNonSpace = 2, /// <summary> /// 指示字符串比較必須忽略符號,如空白字符、 標點、 貨幣符號、 百分比符號,數學符號、 的與符號,依次類推。 /// </summary> IgnoreSymbols = 4, /// <summary> /// 指示字符串比較必須忽略假名類型。 假名類型引用為日文平假名和片假名字符,表示在日語中的語音。 平假名用於本機日語表達式和單詞,而片假名用於從"計算機"或"Internet"等其他語言借用的詞語。 拼音聲音可以表示在平假名和片假名。 如果選擇此值,則一種聲音的平假名字符視為相等的同一個聲音的片假名字符。 /// </summary> IgnoreKanaType = 8, /// <summary> /// 指示字符串比較必須忽略字符寬度。 例如,日語的片假名字符可以編寫為全角或半角。 如果選擇此值,則片假名字符的全角形式視為相等半角形式編寫的相同字符。 /// </summary> IgnoreWidth = 16, /// <summary> /// 字符串比較必須忽略大小寫,然后執行序號比較。 此方法相當於將轉換為大寫使用固定區域性,然后對結果執行序號比較的字符串。 /// </summary> OrdinalIgnoreCase = 268435456, /// <summary> /// 指示字符串比較必須使用字符串排序算法。 在字符串排序、 連字符和撇號,以及其他非字母數字的符號,排在字母數字字符之前。 /// </summary> StringSort = 536870912, /// <summary> /// 指示字符串比較必須使用 Unicode utf-16 編碼的連續值的字符串 (由代碼單元比較代碼單位),從而導致比較速度,但不區分區域性。 字符串與代碼單元 // XXXX 開始16 YYYY 開頭的字符串之前16, ,如果 XXXX16 小於 YYYY16。 此值不能與其他組合 System.Globalization.CompareOptions值,並必須單獨使用。 /// </summary> Ordinal = 1073741824 }
(1)、Compare方法
第一種:
該方法是根據當前線程的語言文化,先對兩個字符串進行排序,然后判斷排完序之后的兩個字符串是否相等,比較規則(具體設置見 CompareOptions枚舉)為默認規則.
CultureInfo.CurrentCulture代碼如下:
注:返回的是當前線程的CurrentCulture實例屬性,該實例屬性返回一個全局CultureInfo對象,接着調用CompareInfo屬性,返回一個CompareInfo對象,最后調用Compare方法,Compare方法如下:
public virtual int Compare(string string1, string string2, CompareOptions options) { if (options == CompareOptions.OrdinalIgnoreCase) { return string.Compare(string1, string2, StringComparison.OrdinalIgnoreCase); } if ((options & CompareOptions.Ordinal) != CompareOptions.None) { if (options != CompareOptions.Ordinal) { throw new ArgumentException(Environment.GetResourceString("Argument_CompareOptionOrdinal"), "options"); } return string.CompareOrdinal(string1, string2); } if ((options & ~(CompareOptions.StringSort | CompareOptions.IgnoreWidth | CompareOptions.IgnoreKanaType | CompareOptions.IgnoreSymbols | CompareOptions.IgnoreNonSpace | CompareOptions.IgnoreCase)) != CompareOptions.None) { throw new ArgumentException(Environment.GetResourceString("Argument_InvalidFlag"), "options"); } if (string1 == null) { if (string2 == null) { return 0; } return -1; } if (string2 == null) { return 1; } return InternalCompareString(this.m_dataHandle, this.m_handleOrigin, this.m_sortName, string1, 0, string1.Length, string2, 0, string2.Length, GetNativeCompareFlags(options)); }
InternalCompareString方法位extern方法,所以無法繼續查看源碼.
調用代碼:
string a = "aaa"; string b = "bbb"; string c = "aaa"; //輸出結果 0-代表相等 -1-代表在當前線程的語言文化的排序結果是a比b小 1-代表在當前線程的語言文化的排序結果是a比b大 Console.WriteLine(string.Compare(a, b));//輸出:-1 Console.WriteLine(string.Compare(a, c));//輸出:0 //對null值的特殊處理 Console.WriteLine(string.Compare(null, null));//輸出:0 Console.WriteLine(string.Compare(a, null));//輸出:1 Console.WriteLine(string.Compare(null, a));//輸出:-1 Console.ReadKey();
第二種:
該方法是根據當前線程的語言文化,先對兩個字符串進行排序,然后對排完序兩個字符串判斷是否相等。比較規則為是否設置大小寫.
具體調用過程和第一種方法一樣,區別就是判斷過程中的大小寫設置,ignoreCase為false,則走第一種方法的比較過程.
調用代碼:
//輸出結果 0-代表相等 -1-代表在當前線程的語言文化的排序結果是a比b小 1-代表在當前線程的語言文化的排序結果是a比b大 string a = "aaa"; string c = "AAA"; Console.WriteLine(string.Compare(a, c,true));//輸出:0 //對null值的特殊處理 Console.WriteLine(string.Compare(null, null,true));//輸出:0 Console.WriteLine(string.Compare(a, null,true));//輸出:1 Console.WriteLine(string.Compare(null, a,true));//輸出:-1
第三種:
該方法是根據當前線程的語言文化,先對兩個字符串進行排序,然后對排完序兩個字符串判斷是否相等。比較規則為是否設置大小寫.具體設置參數見上面的StringComparison枚舉.
public static int Compare(string strA, string strB, StringComparison comparisonType) { if ((comparisonType - 0) > StringComparison.OrdinalIgnoreCase) { throw new ArgumentException(Environment.GetResourceString("NotSupported_StringComparison"), "comparisonType"); } if (strA == strB) { return 0; } if (strA == null) { return -1; } if (strB == null) { return 1; } switch (comparisonType) { case StringComparison.CurrentCulture: return CultureInfo.CurrentCulture.CompareInfo.Compare(strA, strB, CompareOptions.None); case StringComparison.CurrentCultureIgnoreCase: return CultureInfo.CurrentCulture.CompareInfo.Compare(strA, strB, CompareOptions.IgnoreCase); case StringComparison.InvariantCulture: return CultureInfo.InvariantCulture.CompareInfo.Compare(strA, strB, CompareOptions.None); case StringComparison.InvariantCultureIgnoreCase: return CultureInfo.InvariantCulture.CompareInfo.Compare(strA, strB, CompareOptions.IgnoreCase); case StringComparison.Ordinal: if ((strA.m_firstChar - strB.m_firstChar) == 0) { return CompareOrdinalHelper(strA, strB); } return (strA.m_firstChar - strB.m_firstChar); case StringComparison.OrdinalIgnoreCase: if (!strA.IsAscii() || !strB.IsAscii()) { return TextInfo.CompareOrdinalIgnoreCase(strA, strB); } return CompareOrdinalIgnoreCaseHelper(strA, strB); } throw new NotSupportedException(Environment.GetResourceString("NotSupported_StringComparison")); }
注:
不走第一種方法的流程,直接比較字符串的二進制大小.
第四種:
根據設置的語言文化,對字符串進行排序,然后比較兩個字符串,比較規則為是否設置大小寫忽略
該方法的代碼執行比較過程和第一種方法一樣.
調用代碼如下:
//輸出結果 0-代表相等 -1-代表在當前線程的語言文化的排序結果是a比b小 1-代表在當前線程的語言文化的排序結果是a比b大 string a = "aaa"; string c = "AAA"; Console.WriteLine(string.Compare(a, c,true,CultureInfo.CurrentCulture));//輸出:0 //對null值的特殊處理 Console.WriteLine(string.Compare(null, null,true, CultureInfo.CurrentCulture));//輸出:0 Console.WriteLine(string.Compare(a, null,true, CultureInfo.CurrentCulture));//輸出:1 Console.WriteLine(string.Compare(null, a,true, CultureInfo.CurrentCulture));//輸出:-1
第五種:
根據設置的語言文化,對字符串進行排序,然后比較兩個字符串,比較規則為CompareOptions設置的規則,具體請參考CompareOptions枚舉
該方法的代碼執行比較過程和第一種方法一樣.
調用代碼:
//輸出結果 0-代表相等 -1-代表在當前線程的語言文化的排序結果是a比b小 1-代表在當前線程的語言文化的排序結果是a比b大 string a = "aaa"; string c = "AAA"; Console.WriteLine(string.Compare(a,c,CultureInfo.CurrentCulture,CompareOptions.IgnoreCase));//輸出:0
第六種:
加了截取字符串的功能,可判斷從字符串的指定位置開始比較字符串的異同和大小,其余流程和上面的方法一樣.
第七種:
實例方法
public int CompareTo(object value) { if (value == null) { return 1; } if (!(value is string)) { throw new ArgumentException(Environment.GetResourceString("Arg_MustBeString")); } return Compare(this, (string) value, StringComparison.CurrentCulture); }
調用代碼:
//輸出結果 0-代表相等 -1-代表在當前線程的語言文化的排序結果是a比b小 1-代表在當前線程的語言文化的排序結果是a比b大 string a = "aaa"; string c = "aaa"; Console.WriteLine(a.CompareTo(c));//輸出:0
(2)、Equals方法
第一種:
StringComparsion參數上面有說明:
public static bool Equals(string a, string b, StringComparison comparisonType) { if ((comparisonType < StringComparison.CurrentCulture) || (comparisonType > StringComparison.OrdinalIgnoreCase)) { throw new ArgumentException(Environment.GetResourceString("NotSupported_StringComparison"), "comparisonType"); } if (a == b) { return true; } if ((a == null) || (b == null)) { return false; } switch (comparisonType) { case StringComparison.CurrentCulture: return (CultureInfo.CurrentCulture.CompareInfo.Compare(a, b, CompareOptions.None) == 0); case StringComparison.CurrentCultureIgnoreCase: return (CultureInfo.CurrentCulture.CompareInfo.Compare(a, b, CompareOptions.IgnoreCase) == 0); case StringComparison.InvariantCulture: return (CultureInfo.InvariantCulture.CompareInfo.Compare(a, b, CompareOptions.None) == 0); case StringComparison.InvariantCultureIgnoreCase: return (CultureInfo.InvariantCulture.CompareInfo.Compare(a, b, CompareOptions.IgnoreCase) == 0); case StringComparison.Ordinal: return ((a.Length == b.Length) && EqualsHelper(a, b)); case StringComparison.OrdinalIgnoreCase: if (a.Length == b.Length) { if (a.IsAscii() && b.IsAscii()) { return (CompareOrdinalIgnoreCaseHelper(a, b) == 0); } return (TextInfo.CompareOrdinalIgnoreCase(a, b) == 0); } return false; } throw new ArgumentException(Environment.GetResourceString("NotSupported_StringComparison"), "comparisonType"); }
根據設置的語言文化,對字符串進行排序,然后比較兩個字符串,比較規則為固定的規則,和Compare比較方法一致.
調用代碼:
var str = "aaa"; var str1 = "aaa"; Console.WriteLine("根據當前的語言文化對字符串進行排序。然后根據當前區域性對字符串進行比較,不忽略大小寫的規則判斷str和str1是否相等? 結果:{0}", String.Equals(str, str1, StringComparison.CurrentCulture)); Console.WriteLine("根據當前的語言文化對字符串進行排序。然后根據當前區域性對字符串進行比較,忽略大小寫的規則判斷str和str1是否相等? 結果:{0}", String.Equals(str, str1, StringComparison.CurrentCultureIgnoreCase)); Console.WriteLine("根據當前的語言文化對字符串進行排序。然后根據固定語言文化對字符串進行比較,不忽略大小寫的規則判斷str和str1是否相等? 結果:{0}", String.Equals(str, str1, StringComparison.InvariantCulture)); Console.WriteLine("根據當前的語言文化對字符串進行排序。然后根據固定語言文化對字符串進行比較,忽略大小寫的規則判斷str和str1是否相等? 結果:{0}", String.Equals(str, str1, StringComparison.InvariantCultureIgnoreCase)); Console.WriteLine("忽略語言文化,使用序號(二進制)排序規則比較字符串。判斷str和str1是否相等? 結果:{0}", String.Equals(str, str1, StringComparison.Ordinal)); Console.WriteLine("忽略語言文化,通過使用序號(二進制)區分區域性的排序規則並忽略所比較的字符串的大小寫,來比較字符串。判斷str和str1是否相等? 結果:{0}", String.Equals(str, str1, StringComparison.OrdinalIgnoreCase)); Console.ReadKey();
第二種:
,代碼如下:
public static bool Equals(string a, string b) { if (a == b) { return true; } if ((a == null) || (b == null)) { return false; } if (a.Length != b.Length) { return false; } return EqualsHelper(a, b); }
var str = "aaa"; var str1 = "aaa"; Console.WriteLine("str和str1相等嗎?答案:{0}", String.Equals(str, str1));
結合第一種方法分析,發現第二種方法等同於:
Console.WriteLine("str和str1相等嗎?答案:{0}", String.Equals(str, str1, StringComparison.Ordinal));
有如下佐證:
注:StringComparison.Ordinal這種方式強制忽略語言文化比較字符串的大小,是字符串比較最快的方式.
第三種:第二種靜態版本的實例實現版本
public bool Equals(string value) { if (this == null) { throw new NullReferenceException(); } if (value == null) { return false; } if (this == value) { return true; } if (this.Length != value.Length) { return false; } return EqualsHelper(this, value); }
調用方式:
var str = "aaa"; var str1 = "aaa"; Console.WriteLine("str和str1相等嗎?答案:{0}", str.Equals(str1));
第四種:第一種靜態版本的實例實現版本
第五種:第三種和第二種的特殊版本
public override bool Equals(object obj) { if (this == null) { throw new NullReferenceException(); } string strB = obj as string; if (strB == null) { return false; } if (this == obj) { return true; } if (this.Length != strB.Length) { return false; } return EqualsHelper(this, strB); }
雖然傳入的是obj,但是將obj轉換成了string,然后執行EqualsHelper方法.
注:
(1)、許多程序都將字符串用於內部編程目的,比如路徑名、文件名、URL、注冊表項/值、環境變量、反射、XML標記、XML特性等.
這些字符串通常只在程序內部使用,不向用戶顯示.出於編程目的比較字符串,使用StringComparsion.Ordinal或者StringComparison.OrdinalIgnoreCase是最好的,因為忽略文化是字符串比較最快的方式.
(2)、但是要以語言文化正確的方式來比較字符串(通常是為了向用戶顯示),就應該使用StringComparsion.CurrentCultrue或者StringComparsion.CurrentCultrueIgnoreCase.
(3)、StringComparsion.InvariantCultrue和StringComparsion.InvariantCultrueIgnoreCase慎用,雖然這兩個值能保證比較時語言文化的正確性,但用來比較內部編程所需的字符串,所花的時間遠超出序號比較,也就是Ordinal和OrdinalIgnoreCase的比較方式.在處理要想用戶顯示的字符串時,選擇它也不恰當,因為它代表不適用任何具體的語言文化.
(4)、