TOC

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#提供了所需的全部工具,这也是本章努力想展示的。


This article has been fully translated into the following languages: Is your preferred language not on the list? Click here to help us translate this article into your language!