我们在编程时所需的许多核心功能并不是由C#语言提供的,而是由.NET Framework中的类型提供的。本节我们将介绍Framework在基础编程任务(例如虚的等值比较、顺序比较以及类型转换)中的作用。我们还会介绍Framework中的基本类型,例如String、DateTime和Enum.
本章中的绝大部分类型位于System命名空间下,但以下几种类型除外:
1.StringBuilder类型定义在System。Text命名空间中。该命名空间中还包含用于进行文本编码的类型。
2.CultureInfo及其相关类型定义在System.Globalization命名空间中。
2.Xmlconvert类型定义在System.Xml命名空间中。
6.1 字符串与文本处理
6.1.1 字符
C#中的一个char代表一个Unicode字符。char是System.Char的别名。在第2章中,我们介绍了如何表示char字面量,例如:
char c = 'a';
char newLine = '\n';
System.Char定义了一系列静态方法对字符处理,例如:ToUpper、ToLower和IsWhiteSpace。这些方法既可以通过System.Char类型进行调用也可以使用其别名char进行调用:
Console.WriteLine(System.Char.ToUpper('c'));
Console.WriteLine(char.IsWhiteSpace('\t'));
ToUpper和ToLower会受到最终用户语言环境的影响,这可能会导致微妙的缺陷。例如,下面的表达式在土耳其语言环境中会得到false值:
Console.WriteLine(char.ToUpper('i') == 'I');
因为在土耳其语中,char.ToUpprt(‘i’)的结果为‘Ī’。为了避免这个问题,System.Char和System.String还提供了和语言环境无关的,有Invariant后缀的ToUpper和ToLower。它们会使用英语语言规则:
Console.WriteLine(char.ToUpperInvariant('i') == 'I');
它是以下写法的简化形式:
Console.WriteLine(char.ToUpper('i',System.Globalization.CultureInfo.InvariantCulture) == 'I');
char类型中有相当一部分的静态方法用于字符分类,如表:
静态方法 | 包含的字符 | 包含的Unicode分类 |
---|---|---|
IsLetter | A-Z,a-z和其他字母字符 | UpperCaseLetter LowerCaseLetter TittleCaseLetter ModifierLetter OtherLetter |
IsUpper | 大小写字母 | UpperCaseLetter |
IsLower | 小写字母 | LowerCaseLetter |
IsDigit | 0-9和其他字母表中的数字 | DecimalDigitNumber |
IsLetterOrDigit | 字母和数字 | IsLetter、IsDigit |
IsNumber | 所有数字以及Unicode分数和罗马数字符号 | DecimalDigitNumber LetterNumber OtherNumber |
IsSeparator | 空格与所有Unicode分隔符 | LIneSeparate ParagraphSeparate |
IsWhiteSpace | 所有分隔符,以及\n、\r、\t、\f和\v | LIneSeparate ParagraphSeparate |
IsPunctuation | 西方和其他字母表中的标点符号 | DashPunchtuation Connector Punctuation InitialQuote PunctuationFinalQuotePPunctuation |
IsSymbol | 大部分其他的可打印符号 | MathSymbol ModifierSymbol OtherSymbol |
isControl | 值小于0x20的不可打印的控制字符。例如\n、\r、\t、\0和0x7F与0x9A之间的字符 | (无) |
对于更详细的分类,char提供了一个名为GetUnicodeCategory的静态方法,它返回一个UnicodeCategory枚举值,它的成员即表的6-1最右边一列的值。
我们完全由能力通过显示转换一个整数来制造出一个Unicode集之外的char。因此要检测字符的有效性,可以调用char.GetUnicodeCategory方法:如果返回值为UnicodeCategory.OhterNotAssigned那么这个字符就是无效的。
一个char字符占用16给二进制位,这足以表示基本多文种平面(Basic Multilingual Plane)中的所有Unicode字符。但是如果超出了这个范围,就必须使用替代组(Surrogate pairs)。
6.1.2 字符串
C#中的string(==System.String)是一个不可变的(不可修改)的字符序列。在第二章笔记中,我们介绍了如何表示一个字符串字面量,执行相等比较以及如何连接两个字符串。本章我们将介绍其余字符串处理函数:System.String类的静态和实例成员函数。
6.1.2.1 创建字符串
创建字符串的最简单方法就是将字面量赋给一个变量,这和我们在第2章的做法一样:
string s1 = "Hello";
string s2 = "First Line\r\nSecond Line";
string s3 = @"\\r_server\n_fileshare\t_helloworld.cs";
Console.WriteLine(s1);
Console.WriteLine(s2);
Console.WriteLine(s3);
若要创建一个重复的字符序列,可以使用string类的构造器:
Console.WriteLine(new string('*',10));//**********
我们还可以从char数组来构造一个字符串,而ToCharArray方法则执行了相反的操作:
char[] ca = "Hello".ToCharArray();
string sca = new string(ca);
Console.WriteLine(sca);//Hello
string类的重载构造器可以接收各种(不安全的)指针类型,以便从类似char*这种类型中创建字符串。
6.1.2.2 null和空字符串
空字符串指长度为零的字符串。我们可以使用字面量或静态string.Empty字段来创建一个空字符串。若要判断一个字符串是否为空字符串,则可以执行一个相等比较,或测试它的Length属性:
string empty = "";
Console.WriteLine(empty == ""); //True
Console.WriteLine(empty == string.Empty); //True
Console.WriteLine(empty.Length == 0); //True
字符串是引用类型,因此它可以为null:
string nullString = null;
Console.WriteLine(nullString == null);//True
Console.WriteLine(nullString == ""); //False
Console.WriteLine(nullString.Length == 0); //NullReferenceException
我们可以使用静态方法string.IsNullOrEmpty来判断一个字符串是否为null或空字符串。
6.1.2.3 访问字符串中的字符
字符串的索引器可以返回一个指定索引位置的字符。和所有操作字符串的方法相似,索引是从0开始计数的。
string str = "abcde";
char letter = str[1];
Console.WriteLine(letter);//letter == 'b'
string还实现了IEnumerable,所以可以用foreach遍历它的字符:
foreach (char c in "abc")
{
Console.Write(c + ",");//1,2,3
}
6.1.2.4 字符串内搜索
在字符串内执行搜索的最简单方法是StartsWith、EndsWith和Contains,这些方法均返回true或false:
Console.WriteLine("quick brown fox".StartsWith("quick"));//True
Console.WriteLine("quick brown fox".EndsWith("fox"));//True
Console.WriteLine("quick brown fox2".EndsWith("fox"));//false
Console.WriteLine("quick brown fox".EndsWith("fox"));//True
Console.WriteLine("quick brown fox".Contains("brown"));//True
StartsWith和EndsWith提供了重载方法,使用StringComparsion枚举或者CultureInfo对象来控制大小写和文化相关的规则。其默认行为是使用当前文化规则执行区分大小写的匹配。以下代码则使用了不变文化规则执行不区分大小写的搜索:
bool isaBc = "abcdef".StartsWith("aBc", StringComparison.InvariantCultureIgnoreCase);
Console.WriteLine(isaBc); //True
Contains则没有提供这种便利的重载方法,但是可以使用IndexOf方法实现相同的效果。
IndexOf的功能更强,它返回指定字符或者字符串的首次出现的位置(-1则表示该子字符串不存在):
Console.WriteLine("abcdef".IndexOf("cd"));
IndexOf也提供了重载方法,不但接受StringComparison枚举值,还可以接受startPosition参数,指定初始搜索位置。
Console.WriteLine("abcde abcde".IndexOf("CD",6,StringComparison.CurrentCultureIgnoreCase));//8
LastIndexOf和IndexOf类似,只是它从后向前进行搜索的。
IndexOfAny返回和字符集中的任意一个字符第一个匹配的位置:
Console.WriteLine("ab,cd ef".IndexOfAny(new char[] {
' ',','}));//2
Console.WriteLine("pas5wOrd".IndexOfAny("0123456789".ToCharArray()));//3
LastIndexOfAny则从相反方向执行相同操作。
6.1.2.5 字符串处理
string是不可变的,因此所有“处理”字符串的方法都会返回一个新的字符串,而原始的字符串则不受影响(其效果和重新为一个字符串变量赋值一样)。
Substring方法可以提取部分字符串:
string left3 = "12345".Substring(0,3);
string mid3 = "12345".Substring(1,3);
Console.WriteLine(left3);//123
Console.WriteLine(mid3);//234
若省略长度,则会得到剩余的字符串:
string end3 = "12345".Substring(2);
Console.WriteLine(end3);//345
Insert和Remove在特定的位置插入或者删除一些字符:
string s1 = "helloworld".Insert(5, ",");
Console.WriteLine(s1); //hello,world
string s2 = s1.Remove(5, 2);
Console.WriteLine(s2); //helloorld
PadLeft和PadRight会特定的字符(如果未指定则使用空格)将字符串填充为指定的长度:
Console.WriteLine("12345".PadLeft(9,'*')); //****12345
Console.WriteLine("12345".PadLeft(9));// 12345
如果输入字符串长度大于填充长度,则返回不发生变化的原始字符串。
TrimStart和TrimEnd会从字符串的开始或结尾删除指定的字符,Trim则是从开始和结尾执行删除操作。这些方法默认会删除空白字符(包括空格、制表符、换行和这些字符的Unicode变体):
Console.WriteLine(" abc \t\r\n ".Trim().Length);//3
Replace会替换字符串中所有(非重叠的)特定字符或子字符串:
Console.WriteLine("to be done".Replace(" "," | "));//to | be |done
Console.WriteLine("to be done".Replace(" ", ""));//tobedone
ToUpper和ToLower会返回与输入字符串相对应的大写和小写字符串。默认情况下,它会受用户当前语言设置的影响;ToUpperInvariant和ToLowerInvariant则总是应用英语字母表规则。
6.1.2.6 字符串的分割与连接
Split将字符串分割为若干部分:
string[] words = "The quick brown fox".Split();
foreach (string word in words)
Console.Write(word + "|"); //The|quick|brown|fox|
默认情况下,Split使用空白字符作为分隔符;重载的方法也可以接收char和string分隔符的params数组。Split还可以接受一个StringSplitOptions枚举值以删除空白项。这在一行文本中有多种单词分隔符时很有用。
静态方法Join则执行和Split相反的操作。它需要一个分隔符和字符串的数组:
string[] words = "The quick brown fox".Split();
string together = string.Join(",", words);
Console.WriteLine(together);//The,quick,brown,fox
静态方法Concat和Join类似,但是它仅仅接受一个字符串的params数组,且不支持分隔符。Concat操作和+操作符效果完全相同(实际上编译器会将+转换为Concat):
string sentence = string.Concat("The", "quick", "brown", "fox");
string sameSentence = "The" + "quick" + "brown" + "fox";
Console.WriteLine(sentence);//Thequickbrownfox
Console.WriteLine(sameSentence);//Thequickbrownfox
6.1.2.7 String.Format与组合格式字符串
静态方法Format提供了创建嵌入变量的字符串的便利方法。嵌入的变量(或值)可以时任何类型,而Format则会直接调用它们的ToString方法。
包含嵌入变量的主字符串称为组合格式字符串。调用String.Format时,需要提供一个组合格式字符串,后面紧跟每一个嵌入的变量,例如:
string composite = "It's {0} degrees in {1} on this{2}morning";
string s = string.Format(composite, 35, "Perth", DateTime.Now.DayOfWeek);
Console.WriteLine(s); //It 's 35 degrees in Perth on thisSaturdaymorning
从C#6开始,可以使用插值字符串字面量来达成同样的效果。只需要在字符串前面加上$符号,并将表达式写在花括号中:
string s = $"It's hot this{
DateTime.Now.DayOfWeek} morning";
Console.WriteLine(s);
花括号里面的每一个数字称为格式项。这些数字对应参数的位置,后面可以跟随:
1.逗号与应用的最小宽度
2.冒号与格式字符串
3.最小宽度用于对齐各个列。如果其值为负数,则左对齐,否则为右对齐,例如:
string composite = "Name={0,-20} Credit Limit={1,15:C}";
Console.WriteLine(string.Format(composite,"Mary",500));
Console.WriteLine(string.Format(composite, "Elizabeth", 20000));
运行结果:
Name=Mary Credit Limit= ¥500.00
Name=Elizabeth Credit Limit= ¥20,000.00
如果不使用string.Format则上述方法可写为:
string composites = "Name=" + "Mary".PadRight(20) + "Credit Limit"+ 500.ToString("C").PadLeft(15);
Console.WriteLine(composites);
上例的信用额度是通过”C“格式字符串转换为货币值的。
6.1.3 字符串的比较
.NET Framework在两个值的比较上划分了两个不同的概念:相等比较和顺序比较。等值比较验证两个实例是否从语义上是相同的,而顺序比较则验证两个实例(如果有的话)按照升序或者降序排列的话,哪一个应当首先出现。
等值比较并不是顺序比较的一个子集。这两种方法各自有不同的用途。例如,两个不同的值却可以有相同的排序位置。
可以使用==操作符或者string的Equals方法来进行字符串的等值比较。后者可以指定一些选项*例如不区分大小写),因此功能更强。
另一个不同点是,如果将变量转换为object类型,则==就不一定是按字符串处理的了。
对于字符串的顺序比较则可使用实例方法CompareTo或者静态方法Compare或CompareOrdinal:这些方法会返回一个正数、负数或者0.这取决于第一个值是在第二个值之后、之前还是同时出现。
6.1.3.1 序列比较于文化相关的字符串比较
字符串比较有两种基本方法:序列比较(ordinal)和文化相关的比较(culture-sensitive)。序列比较会直接将字符串解析为数字(按照它们的Unicode字符数值);而文化相关的比较则参照特定的字母表来解释字符。有两种特殊的文化:”当前文化“,基于计算机控制面板的设定;”不变文化“,在任何计算机上都是相同的(并且和美国文化密切一致)。
对于相等比较,序列和文化相关的算法都是非常有用的。而排序时,人们则通常选择文化相关的比较:因为当按照字符进行排序时,通常需要一个字母顺序表。序列比较依赖的是Unicode的数字位置,这恰好会使英文字母按照顺序排序,但即使是这样也可能不能满足要求。例如,在区分大小写的情况下,考虑如下字符串”Atom“、”autom"和“Zamia"。使用不变文化则其排列顺序将是:
"autom"、"Atom"、"Zamia"
而使用序列比较则为:
"Atom"、"Zamia"、"autom"
这是因为不变文化封装了一个字母表,它认为大写字符与其对应的小写字符是相邻的(aAbBcCdD…)。然而序列比较算法将所有大写字母排列在前面,然后才是全部小写字母(A…Z,a…z)这实际上回归到了20世纪60年代发明的ASCII字符集了。
6.1.3.2 字符串的相等比较
结果序列比较有局限性,但是字符串的==运算符总是执行区分大小写的序列比较。不带有参数的string.Equals方法也是用同样的方式。这就是string类型的”默认“相等比较的行为。
字符串的==和Equals方法选择序列算法的原因是因为它既高效有确定。字符串相等比较是基础操作,并远比顺序比较使用频繁。
”严格“的相等概念与==运算符的一般用途是一致的。
下面的方法允许执行文化相关的大小写比较:
public bool Equals(string value, StringComparison comparisonType);
public static bool Equals(string a, string b, StringComparison comparisonType);
我们更推荐静态的版本因为它在两个字符串中的一个或者全部为null的时候仍然有效。
StringComparison是枚举类型,其定义如下:
//
// 摘要:
// Specifies the culture, case, and sort rules to be used by certain overloads of
// the System.String.Compare(System.String,System.String) and System.String.Equals(System.Object)
// methods.
public enum StringComparison
{
//
// 摘要:
// Compare strings using culture-sensitive sort rules and the current culture.
CurrentCulture = 0,
//
// 摘要:
// Compare strings using culture-sensitive sort rules, the current culture, and
// ignoring the case of the strings being compared.
CurrentCultureIgnoreCase = 1,
//
// 摘要:
// Compare strings using culture-sensitive sort rules and the invariant culture.
InvariantCulture = 2,
//
// 摘要:
// Compare strings using culture-sensitive sort rules, the invariant culture, and
// ignoring the case of the strings being compared.
InvariantCultureIgnoreCase = 3,
//
// 摘要:
// Compare strings using ordinal (binary) sort rules.
Ordinal = 4,
//
// 摘要:
// Compare strings using ordinal (binary) sort rules and ignoring the case of the
// strings being compared.
OrdinalIgnoreCase = 5
}
例如:
Console.WriteLine(string.Equals("foo","FOO", StringComparison.OrdinalIgnoreCase));
Console.WriteLine(" " == "ǖ");
Console.WriteLine(string.Equals(" ", "ǖ", StringComparison.CurrentCulture));
上述中的第三个比较是由计算机当前语言设置决定的。
6.1.3.3 字符串的顺序比较
String的实例方法CompareTo执行文化相关的区分大小写的顺序比较,与==运算符不同,CompareTo不使用序列比较。这是因为对于排序来说,文化相关的算法更为有效。
以下是方法的定义:
public int CompareTo(string strB);
实例方法CompareTo实现了IComparable泛型接口,它也是在整个.NET Framework中使用的标准比较协议。这意味着string的CompareTo定义了字符串在应用程序中,作为集合元素时排序的默认行为。
对于其他类型的比较则可以调用静态方法Compare和CompareOrdinal:
//
// 摘要:
// Compares substrings of two specified System.String objects, ignoring or honoring
// their case, and returns an integer that indicates their relative position in
// the sort order.
//
// 参数:
// strA:
// The first string to use in the comparison.
//
// indexA:
// The position of the substring within strA.
//
// strB:
// The second string to use in the comparison.
//
// indexB:
// The position of the substring within strB.
//
// length:
// The maximum number of characters in the substrings to compare.
//
// ignoreCase:
// true to ignore case during the comparison; otherwise, false.
//
// 返回结果:
// A 32-bit signed integer that indicates the lexical relationship between the two
// comparands.
// Value – Condition
// Less than zero – The substring in strA precedes the substring in strB in the
// sort order.
// Zero – The substrings occur in the same position in the sort order, or length
// is zero.
// Greater than zero – The substring in strA follows the substring in strB in the
// sort order.
//
// 异常:
// T:System.ArgumentOutOfRangeException:
// indexA is greater than strA.System.String.Length. -or- indexB is greater than
// strB.System.String.Length. -or- indexA, indexB, or length is negative. -or- Either
// indexA or indexB is null, and length is greater than zero.
public static int Compare(String? strA, int indexA, String? strB, int indexB, int length, bool ignoreCase);
//
// 摘要:
// Compares substrings of two specified System.String objects and returns an integer
// that indicates their relative position in the sort order.
//
// 参数:
// strA:
// The first string to use in the comparison.
//
// indexA:
// The position of the substring within strA.
//
// strB:
// The second string to use in the comparison.
//
// indexB:
// The position of the substring within strB.
//
// length:
// The maximum number of characters in the substrings to compare.
//
// 返回结果:
// A 32-bit signed integer indicating the lexical relationship between the two comparands.
// Value – Condition
// Less than zero – The substring in strA precedes the substring in strB in the
// sort order.
// Zero – The substrings occur in the same position in the sort order, or length
// is zero.
// Greater than zero – The substring in strA follows the substring in strB in the
// sort order.
//
// 异常:
// T:System.ArgumentOutOfRangeException:
// indexA is greater than strA.System.String.Length. -or- indexB is greater than
// strB.System.String.Length. -or- indexA, indexB, or length is negative. -or- Either
// indexA or indexB is null, and length is greater than zero.
public static int Compare(String? strA, int indexA, String? strB, int indexB, int length);
//
// 摘要:
// Compares two specified System.String objects using the specified comparison options
// and culture-specific information to influence the comparison, and returns an
// integer that indicates the relationship of the two strings to each other in the
// sort order.
//
// 参数:
// strA:
// The first string to compare.
//
// strB:
// The second string to compare.
//
// culture:
// The culture that supplies culture-specific comparison information. If culture
// is null, the current culture is used.
//
// options:
// Options to use when performing the comparison (such as ignoring case or symbols).
//
// 返回结果:
// A 32-bit signed integer that indicates the lexical relationship between strA
// and strB, as shown in the following table
// Value – Condition
// Less than zero –strA precedes strB in the sort order.
// Zero –strA occurs in the same position as strB in the sort order.
// Greater than zero –strA follows strB in the sort order.
//
// 异常:
// T:System.ArgumentException:
// options is not a System.Globalization.CompareOptions value.
public static int Compare(String? strA, String? strB, CultureInfo? culture, CompareOptions options);
//
// 摘要:
// Compares two specified System.String objects, ignoring or honoring their case,
// and using culture-specific information to influence the comparison, and returns
// an integer that indicates their relative position in the sort order.
//
// 参数:
// strA:
// The first string to compare.
//
// strB:
// The second string to compare.
//
// ignoreCase:
// true to ignore case during the comparison; otherwise, false.
//
// culture:
// An object that supplies culture-specific comparison information. If culture is
// null, the current culture is used.
//
// 返回结果:
// A 32-bit signed integer that indicates the lexical relationship between the two
// comparands.
// Value – Condition
// Less than zero –strA precedes strB in the sort order.
// Zero –strA occurs in the same position as strB in the sort order.
// Greater than zero –strA follows strB in the sort order.
public static int Compare(String? strA, String? strB, bool ignoreCase, CultureInfo? culture);
//
// 摘要:
// Compares two specified System.String objects, ignoring or honoring their case,
// and returns an integer that indicates their relative position in the sort order.
//
// 参数:
// strA:
// The first string to compare.
//
// strB:
// The second string to compare.
//
// ignoreCase:
// true to ignore case during the comparison; otherwise, false.
//
// 返回结果:
// A 32-bit signed integer that indicates the lexical relationship between the two
// comparands.
// Value – Condition
// Less than zero –strA precedes strB in the sort order.
// Zero –strA occurs in the same position as strB in the sort order.
// Greater than zero –strA follows strB in the sort order.
public static int Compare(String? strA, String? strB, bool ignoreCase);
//
// 摘要:
// Compares two specified System.String objects using the specified rules, and returns
// an integer that indicates their relative position in the sort order.
//
// 参数:
// strA:
// The first string to compare.
//
// strB:
// The second string to compare.
//
// comparisonType:
// One of the enumeration values that specifies the rules to use in the comparison.
//
// 返回结果:
// A 32-bit signed integer that indicates the lexical relationship between the two
// comparands.
// Value – Condition
// Less than zero –strA precedes strB in the sort order.
// Zero –strA is in the same position as strB in the sort order.
// Greater than zero –strA follows strB in the sort order.
//
// 异常:
// T:System.ArgumentException:
// comparisonType is not a System.StringComparison value.
//
// T:System.NotSupportedException:
// System.StringComparison is not supported.
public static int Compare(String? strA, String? strB, StringComparison comparisonType);
//
// 摘要:
// Compares substrings of two specified System.String objects using the specified
// rules, and returns an integer that indicates their relative position in the sort
// order.
//
// 参数:
// strA:
// The first string to use in the comparison.
//
// indexA:
// The position of the substring within strA.
//
// strB:
// The second string to use in the comparison.
//
// indexB:
// The position of the substring within strB.
//
// length:
// The maximum number of characters in the substrings to compare.
//
// comparisonType:
// One of the enumeration values that specifies the rules to use in the comparison.
//
// 返回结果:
// A 32-bit signed integer that indicates the lexical relationship between the two
// comparands.
// Value – Condition
// Less than zero – The substring in strA precedes the substring in strB in the
// sort order.
// Zero – The substrings occur in the same position in the sort order, or the length
// parameter is zero.
// Greater than zero – The substring in strA follows the substring in strB in the
// sort order.
//
// 异常:
// T:System.ArgumentOutOfRangeException:
// indexA is greater than strA.System.String.Length. -or- indexB is greater than
// strB.System.String.Length. -or- indexA, indexB, or length is negative. -or- Either
// indexA or indexB is null, and length is greater than zero.
//
// T:System.ArgumentException:
// comparisonType is not a System.StringComparison value.
public static int Compare(String? strA, int indexA, String? strB, int indexB, int length, StringComparison comparisonType);
//
// 摘要:
// Compares substrings of two specified System.String objects using the specified
// comparison options and culture-specific information to influence the comparison,
// and returns an integer that indicates the relationship of the two substrings
// to each other in the sort order.
//
// 参数:
// strA:
// The first string to use in the comparison.
//
// indexA:
// The starting position of the substring within strA.
//
// strB:
// The second string to use in the comparison.
//
// indexB:
// The starting position of the substring within strB.
//
// length:
// The maximum number of characters in the substrings to compare.
//
// culture:
// An object that supplies culture-specific comparison information. If culture is
// null, the current culture is used.
//
// options:
// Options to use when performing the comparison (such as ignoring case or symbols).
//
// 返回结果:
// An integer that indicates the lexical relationship between the two substrings,
// as shown in the following table.
// Value – Condition
// Less than zero – The substring in strA precedes the substring in strB in the
// sort order.
// Zero – The substrings occur in the same position in the sort order, or length
// is zero.
// Greater than zero – The substring in strA follows the substring in strB in the
// sort order.
//
// 异常:
// T:System.ArgumentException:
// options is not a System.Globalization.CompareOptions value.
//
// T:System.ArgumentOutOfRangeException:
// indexA is greater than strA.Length. -or- indexB is greater than strB.Length.
// -or- indexA, indexB, or length is negative. -or- Either strA or strB is null,
// and length is greater than zero.
public static int Compare(String? strA, int indexA, String? strB, int indexB, int length, CultureInfo? culture, CompareOptions options);
//
// 摘要:
// Compares substrings of two specified System.String objects, ignoring or honoring
// their case and using culture-specific information to influence the comparison,
// and returns an integer that indicates their relative position in the sort order.
//
// 参数:
// strA:
// The first string to use in the comparison.
//
// indexA:
// The position of the substring within strA.
//
// strB:
// The second string to use in the comparison.
//
// indexB:
// The position of the substring within strB.
//
// length:
// The maximum number of characters in the substrings to compare.
//
// ignoreCase:
// true to ignore case during the comparison; otherwise, false.
//
// culture:
// An object that supplies culture-specific comparison information. If culture is
// null, the current culture is used.
//
// 返回结果:
// An integer that indicates the lexical relationship between the two comparands.
// Value – Condition
// Less than zero – The substring in strA precedes the substring in strB in the
// sort order.
// Zero – The substrings occur in the same position in the sort order, or length
// is zero.
// Greater than zero – The substring in strA follows the substring in strB in the
// sort order.
//
// 异常:
// T:System.ArgumentOutOfRangeException:
// indexA is greater than strA.System.String.Length. -or- indexB is greater than
// strB.System.String.Length. -or- indexA, indexB, or length is negative. -or- Either
// strA or strB is null, and length is greater than zero.
public static int Compare(String? strA, int indexA, String? strB, int indexB, int length, bool ignoreCase, CultureInfo? culture);
//
// 摘要:
// Compares two specified System.String objects and returns an integer that indicates
// their relative position in the sort order.
//
// 参数:
// strA:
// The first string to compare.
//
// strB:
// The second string to compare.
//
// 返回结果:
// A 32-bit signed integer that indicates the lexical relationship between the two
// comparands.
// Value – Condition
// Less than zero –strA precedes strB in the sort order.
// Zero –strA occurs in the same position as strB in the sort order.
// Greater than zero –strA follows strB in the sort order.
public static int Compare(String? strA, String? strB);
//
// 摘要:
// Compares substrings of two specified System.String objects by evaluating the
// numeric values of the corresponding System.Char objects in each substring.
//
// 参数:
// strA:
// The first string to use in the comparison.
//
// indexA:
// The starting index of the substring in strA.
//
// strB:
// The second string to use in the comparison.
//
// indexB:
// The starting index of the substring in strB.
//
// length:
// The maximum number of characters in the substrings to compare.
//
// 返回结果:
// A 32-bit signed integer that indicates the lexical relationship between the two
// comparands.
// Value – Condition
// Less than zero – The substring in strA is less than the substring in strB.
// Zero – The substrings are equal, or length is zero.
// Greater than zero – The substring in strA is greater than the substring in strB.
//
// 异常:
// T:System.ArgumentOutOfRangeException:
// strA is not null and indexA is greater than strA.System.String.Length. -or- strB
// is not null and indexB is greater than strB.System.String.Length. -or- indexA,
// indexB, or length is negative.
public static int CompareOrdinal(String? strA, int indexA, String? strB, int indexB, int length);
//
// 摘要:
// Compares two specified System.String objects by evaluating the numeric values
// of the corresponding System.Char objects in each string.
//
// 参数:
// strA:
// The first string to compare.
//
// strB:
// The second string to compare.
//
// 返回结果:
// An integer that indicates the lexical relationship between the two comparands.
// Value – Condition
// Less than zero –strA is less than strB.
// Zero –strA and strB are equal.
// Greater than zero –strA is greater than strB.
public static int CompareOrdinal(String? strA, String? strB);
后面两个方法,是前面两个方法的快捷调用形式。
所有的顺序比较都放回正数、负数或者零。者取决于第一个值是在第二个值之后,之前还是相同的位置:
Console.WriteLine("Boston".CompareTo("Austin"));//1
Console.WriteLine("Boston".CompareTo("Boston"));//0
Console.WriteLine("Boston".CompareTo("Chicago"));//-1
Console.WriteLine("Boston0".CompareTo("Boston1"));//-1
Console.WriteLine("Boston2".CompareTo("Boston1"));//1
Console.WriteLine("ǖ".CompareTo("ǖ"));//0
Console.WriteLine("foo".CompareTo("FOO"));//-1
以下语句使用当前文化进行不区分大小写的比较:
Console.WriteLine(string.Compare("foo", "FOO", true));//0
6.1.4 StringBuilder
StringBuilder类(System.Text命名空间)表示一个可变(可编辑)的字符串。
StringBuilder可以直接进行子字符串的Append、Insert、Remove和Replace而不需要替换整个StringBuilder。
StringBuilder的构造器可以选择接受一个初始字符串值,及其内部容量的初始值(默认为16个字符)。如果需要更大的容量,则StringBuilder会自动(以较小的性能代价)调整它的内部结构,以至其最大的容量(默认为int.MaxValue)。
StringBulider一般通过反复调用Append来构建较长的字符串,这比反复连接字符串类型对象要高效得多。
StringBuilder sb = new StringBuilder();
for(int i = 0; i < 50; i++)
{
sb.Append(i + ",");
}
Console.WriteLine(sb.ToString());
输出结果:
0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,
在上述例子中,表达式i+","表示我们仍然在反复连接字符串。但是由于我们使用的字符串很小,因此这个操作的性能开销非常少,并不会随着循环迭代而增长。然而,为了实现最好的性能,我们可以将循环体修改为:
StringBuilder sb = new StringBuilder();
for(int i = 0; i < 50; i++)
{
sb.Append(i);
sb.Append(",");
}
Console.WriteLine(sb.ToString());
AppendLine执行Append操作,随后添加一个换行序列(在Windows下为”\r\n“)。而AppendFormat则接受一个组合格式字符串,这和String.Format是类似的。除了Insert、Remove和Replace方法(Replace方法和字符串的Replace方法类似),StringBuilder还定义了Length属性和一个可写的索引器,以获得/设置每一个字符。
如果要清除StringBuilder的内容,可以创建一个新的StringBuilder或者将其Length设置为零。
将StringBuilder的Length属性设置为0并不会减小其内部容量。因此,如果之前StringBuilder已经包含了一百万个字符,则它在Length设置为0后仍然占用2MB的内存。因此,如果希望释放这些内存,则必须新建一个StringBuilder,然后将旧的对象清除出作用域(从而可以被垃圾回收)。
6.1.5 文本编码和Unicode
字符集是一种字符配置,每一对配置包含了数字码或者代码点(code point)。常用的字符集由两种:Unicode和ASCII。Unicode具有约一百万个字符的地址空间,目前已经分配的大约有100000个。Unicode包括世界上使用最广泛的语言、一些历史语言及特殊符号。ASCII字符集只是Unicode字符集的前128个字符。它包括US风格键盘上的大多数字符。ASCII比Unicode出现早30年,有时仍以其简单性和高效性而得到应用,它的每一个字符是用一个字节表示的。
.NET类型系统的设计使用的是Unicode字符集,但是它隐含支持ASCII字符集,因为ASCII字符集是Unicode的子集。
文本编码(text encoding)是将字符从数字代码点映射到二进制表示的方法。在.NET中,文本编码主要用预处理文本文件和流。当将一个文本文件读取到字符串时,文本编码器(text encoder)会将文件数据从二进制转换为char和string类型使用的内部Unicode表示。文本编码能够限制哪些字符被识别并影响存储效率。
.NET的文本编码分为两类:
一类是将Unicode字符映射到其他字符集
一类是使用标准的Unicode编码模式
第一类包含遗留的编码方式,例如IBM的EBCDIC,以及包含前128个区域扩展字符的8位字符集(这种将字符集字以代码页进行区分的方法,在Unicode之前就已经普遍存在了)。ASCII编码也属于这一类:它将对前128个字符编码,然后去掉其他字符。这个分类也包含GB18030(这种编码方式并非遗留编码方式),这种编码是从2000年以后,在中国开发或者在中国销售的应用程序的强制编码标准。
第二类是UTF-8、UTF-16、UTF-32。每一种编码方式在空间的使用效率都有所差别。UTF-8对于大多数文本而言是最具空间效率的:它使用1~4个字节来表示每一个字符。前128个字符只需要一个字节,这样它就可以兼容ASCII.UTF-8是最普遍的文本文件和流编码方式(特别是在Internet上),并且是.NET中默认的流的I/O编码方式(事实上,它几乎是所有语言隐含的默认编码方式)。
UTF-16使用一个或者两个16位字来表示一个字符,它是.NET内部表示字符和字符串的方式。有一些程序也使用UTF-16来写入文件内容。
UTF-32是空间效率最低的:每一个代码点直接对应一个32位数,所以每一个字符都会占用4个字节。因此,UTF-32很少使用。然而由于每一个字符都有同样的字节数,因此它可以简化随机访问操作。
6.1.5.1 获取一个Encoding对象
System.Text中的Encoding类是封装文本编码的基类型。它有一些子类,封装了具有相同特性的编码方式。实例化一个正确配置的编码类的最简单方法是使用标准的IANA(互联网数字分配机构吗Internet Assigned Numbers Authority)字符集名称调用Encoding.GetEncoding方法。
Encoding utf8 = Encoding.GetEncoding("utf-8");
Encoding chinese = Encoding.GetEncoding("GB18030");
最常用的编码也可以通过调用Encoding类的特定静态属性获得:
编码名称 | Encoding类的静态属性 |
---|---|
UTF-8 | Encoding.UTF8 |
UTF-16 | Encoding.Unicode(注意不是UTF-16) |
UTF-32 | Encoding.UTF-32 |
ASCII | Encoding.ASCII |
静态方法GetEncodings返回所有支持的编码方式清单及其标准的LANA名称:
foreach(EncodingInfo info in Encoding.GetEncodings())
Console.WriteLine(info.Name);
获取编码方式的另一个方法是直接实例化Encoding类。这样做可以通过构造器参数来设置各种选项,包括:
1.在解码,如果遇到一个无效字节系列,是否抛出异常。默认为false.
2.对UTF-16/UTF-32进行编码/解码时是否使用最高有效字节优先(大字节存储顺序)或最低有效字节优先(小字节存储顺序,little endian)。默认值为小字节存储顺序。这也是Windows操作系统的标准。
3.是否使用字节顺序标记(表示字节顺序的前缀)。
6.1.5.2 文件与流I/O编码
Encoding对象最常见的应用是控制文件或流的文本读写操作,例如,下面的代码将“Testing…"以UTF-16的编码方式写入文件data.txt中:
System.IO.File.WriteAllText("data.txt", "testing", Encoding.Unicode);
如果省略最后一个参数,则WriteAllText会使用最普遍的UTF-8编码方式。
UTF-8是所有文件和流I/O的默认文本编码方式。
6.1.5.3 编码为字节数组
Encoding对象可以将文本转换为字节数组,反之亦然。GetBytes方法用指定的编码方式将string转换为byte[];而GetString则将byte[]转换为string:
System.IO.File.WriteAllText("data.txt", "testing"