C# 字符串操作基本过程(Equals、Compare、EndsWith等处理方法)
本文只介绍了比较方法,但是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)、