This article is currently in the process of being translated into Chinese (~98% done).
Strings
string就是一段文本。通常包括2个或以个的字符,因为只有一个的话,就应该考虑换用char类型。不过,字符串也可以是空串,或者甚至是null值,因为这是一个引用类型。字符串的定义与已经介绍过的其它数据类型的定义基本是一样的:
string s;
或者,在定义时就赋值:
string name = "John Doe";在C#中,任何由一对双引号引起来的内容都可能作为字符串,如上例所示 - 用双引号把一个或多个字符引起来只是简单地告诉编译器,双引号内的内容应被解释为字符串,而非其它的,比如,关键字和命令。
字符串是不可变的
C#中,字符串是不可变的,意思是一旦建立,就不能被改变。但日常的实际显然不会是这样的,因此.Net framework已经为我们处理好 - 不是定义新字符串来做变化操作,而是在每次改变字符串时就生成一个新字符串。这样的处理逻辑上虽然没问题,但仍然让代码的效率有些许降低,虽然程度可能小到不会引起注意。下例演示了这种情况:
string numbers = "";
for (int i = 0; i < 10000; i++)
numbers += i.ToString();例子中,代码执行了10,000次循环,每次都把当前索引加入到字符串上。根据上面所述,实际操作并不是改变当前的字符串让其包含新加入的数字,而是新生成一个字符串然后赋值给原来的那个变量,把旧的那个值丢给.NET framework去清理。而这会重复10,000次!如果已知会多次建立字符串,一般都推荐使用另一个类StringBuilder来替代:
StringBuilder numbers = new StringBuilder();
for (int i = 0; i < 10000; i++)
numbers.Append(i);
Console.WriteLine(numbers.ToString());基本的字符串操作
虽然有字符串不可变的前提,代码还是通常会大量使用和操作简单的字符串,对此不需要担心 - 除非是出现在一个非常巨大的循环里面,通常都没问题!以下是一些可以对字符串进行的基本操作:
可以通过简单地"相加"(使用加号)把两个或多个字符串连接起来:
string name = "John" + " " + "Doe";当然也可以这样操作字符串变量,可以把用双引号引起来的字符串与字符串变量,或可以转化为字符串的变量(通过使用所有对象都有的ToString()方法)混合相加。不过有个更“干净”些的方法是使用String类的Format方法:
string name = "John Doe";
int age = 42;
string userString = String.Format("{0} is {1} years old and lives in {2}", name, age, "New York");注意此处是怎样使用编了号的占位符({0}, {1}等),然后用方法中的参数去替换它们的。要记住占位符的索引和数量必需与传递的参数匹配!
Length属性可以用来检查当前字符串的长度,如,进行合法性验证。Length属性与其它属性和方法结合起来也很有用,如, Substring()和IndexOf()方法。Substring方法可截取部分字符串,而IndexOf方法可以查找给定字符/字符串的第一个位置。还是用示例来演示:
string name = "John Doe";
int indexOfSpace = name.IndexOf(' ') + 1;
string lastName = name.Substring(indexOfSpace, name.Length - indexOfSpace);
Console.WriteLine(lastName);简单讲解:代码中先定义了一个name变量,然后用IndexOf()方法找到空格字符的第一个位置。然后使用Substring()方法,通过传入开始位置及截取长度,获得第一个空格字符之后的所有内容。
String类另一个很有用的方法是Replace()方法。此方法可以在一个字符串上进行查找/替换操作,象这样:
string name = "John Doe";
Console.WriteLine(name.Replace("John", "Jane"));Replace()方法操作条件很宽松 - 如果要查找的字符串(第一个参数)不存在,则不会做任何操作(也不会有抛出异常这样的事发生)。如果存在,其将被用第二个参数替换。不过,如果还是想在做替换操作前检查一下是否存在,可以使用Contains()方法:
string name = "John Doe";
if (name.Contains("John"))
Console.WriteLine(name.Replace("John", "Jane"));
else
Console.WriteLine("John was not found!");有时会需要知道字符串是否以某个特定的字符或字符串开头或结尾。这可以使用String类的StartsWith()和EndsWith()方法,由名称即可知道它们的功能:
string name = "John Doe";
if ((name.StartsWith("John")) && (name.EndsWith("Doe")))
Console.WriteLine("Hello, Mr. Doe!");还有很多更加有用的String类方法,而使用它们的方式肯定比上述那些短小的示例中的要多。想要了解更多,请参考MSDN String类文档。
逐字字符串与转义
在定义字符串时很快会遇到某些特定字符有特殊用途。最重要的例子就是双引号本身,因为既然已经被用于向编译器标识字符串的开始和结束,又要怎么把它用在字符串里面呢?答案很简单,就是转义,也就是向编译器表明,特定的字符应按其字面意思处理,而不是其常用的功能。例子如下:
Console.WriteLine("What do you mean by \"Hello, world\" ??");运行的结果如下:
What do you mean by "Hello, world" ??换言之,上例中只是在双引号前面使用了反斜杠,以标识这里并非字符串的结束,而是一个真正的双引号。那么... 字符串中需要反斜杠而不是用来对其它字符进行转义时又怎么办呢?那就再转义 - 再加一个反斜杠:
Console.WriteLine("Right here \\ Right now");结果是:
Right here \ Right now原因是反斜杠不仅用于转义双引号 - 还用于加在一系列其它字符前面以赋予它们特殊的意义。比如,\n表示换行,\t表示制表符等等。此处是一个不错的转义序列的列表。
逐字字符串
作为所有这些转义操作的另一个选项是使用逐字字符串。这与定义常规字符串一样,只是在最前面加上@字符,这样,在其内部,所有的字符就都会按其字面意义处理了:
Console.WriteLine(@"In a verbatim string \ everything is literal: \n & \t");输入与输入的内容看起来是完全一样的:
In a verbatim string \ everything is literal: \n & \t此规则只有一个例外:双引号仍然需要转义,不过这也合理,因为毕竟不这样的话,编译器又怎么去判断字符串的结束呢?并且,在逐字字符串中,双引号不是用反斜杠来转义,而是用另一个双引号,象这样:
Console.WriteLine(@"What do you mean by ""Hello, world"" ??");结果象这样:
What do you mean by "Hello, world" ??总结
对于所有的程序员来说字符串都是个非常重要的主题 - 写程序总是会需要处理字符串的。幸好C#提供了所需的全部工具,这也是本章努力想展示的。