第20章 字符串与正则表达式

本章开始学习字符串和正则表达式,对于要学习字符串(string)这个“简单”类型,可能有些读者会心存疑惑,因为字符串是平时使用的非常多的一种类型,可能大家对它已经比较熟悉了,觉得它本身很简单,实际上string是个挺“复杂”的类型,它提供了非常多的功能,故而本章使用了专门的篇幅来介绍字符串类型。

正则表达式大家可能接触得不多,但它确实是很有用的类型,多用于字符串处理。正则表达式是一个非常强大的工具,在很多领域都有着广泛的应用。需要注意的是,正则表达式虽然重要,但学习起来有一定的难度,建议读者一定要认真阅读的同时勤于动手练习,只有实践与理论相结合才能真正地理解和掌握正则表达式。好了,我们开始吧!

20.1 理解字符串

❑在BCL中,字符串使用类String来表示,小写形式的string是C#语言中的数据类型,string类型对应CTS中的System.String类型。

❑字符串是Char类型的字符数组。

❑字符串是引用类型,它的值位于堆上。

❑字符串变量一旦赋值,它的值就不再能够改变,因此字符串对象是不变对象。

❑运算符==和!=运算符比较的是string对象的值,而不是引用。实际上是因为String重载了==和!=运算符,如下所示:


public static bool operator==(string a,string b)

{

return Equals(a,b);

}

public static bool operator!=(string a,string b)

{

return!Equals(a,b);

}


❑如果要改变一个字符串的值,因为字符串是不变类型,因此结果都是生成一个新的字符串对象,其值为新的字符串值,指向原字符串变量的指针改为指向新的字符串对象。

❑如果在一个循环中改变字符串值,或者合并多个字符串的值,那么由于每次都会创建新的字符串对象,因此会产生许多临时的中间字符串对象,这十分影响性能和执行效率,并造成资源浪费。

第20章 字符串与正则表达式 - 图1

图 20-1 改变字符串的值

在图20-1中,str1字符串变量的值原来为"ab",当把它的值改变为"cde"时,CLR就会创建一个新的字符串对象,它包含值"cde",并将str1指向新的对象。由于没有变量再指向旧的对象,因此,它会在某个时候被垃圾回收机制清理掉,以回收它所占用的内存资源。

为了提高性能和保证执行效率,.NET Framework维护了一个字符串池,也称为“暂存池”或者“常量池”(string interning pool),用以对使用过的字符串进行缓存。其工作机制为:当为字符串类型的变量赋值时,此值同时被放入一个称为“暂存池”的地方,在以后需要创建新的字符串时,CLR会首先检查池中是否存在具有相同值的字符串对象,如果存在,就将变量指向池中已经存在的对象,那么旧的对象又得以重用了,这个过程称为“字符串驻留”。实际上,这是一种缓存机制,此机制减少了字符串占用的存储空间,因为有部分字符串可以“复用”,因此就避免了创建新的对象,也就不需要为创建新的字符串而申请更多的内存。注意,这一过程并非任何时候都会执行,如果新创建的字符串值在“暂存池”中无法找到,也就是没有缓存相同值的字符串对象,那么此时CLR将会创建新的String对象。

使用代码来验证“字符串驻留”的过程,让读者可以更直观地看到这一过程的实际存在。在例子中,首先声明一个字符串变量str1,并为它赋值"a",然后使用"+="运算符联结字符串"a"和"b",此时应该返回一个新的字符串对象,它具有常量文本值"ab",那么值为"ab"的字符串对象是否被放入了“暂存池”呢?答案是没有,因为"ab"是属于动态创建的字符串,此时并不执行“字符串驻留”过程,如代码清单20-1所示。

代码清单20-1 字符串驻留


1 using System;

2

3 namespace ProgrammingCSharp4

4{

5 class StringSample

6{

7 public static void Main()

8{

9 string str1="a";

10 str1+="b";

11 IsInterned(str1);

12//string str2="ab";

13}

14

15 private static void IsInterned(string str)

16{

17 if(string.IsInterned(str)!=null)

18{

19 Console.WriteLine(“字符串\”{0}\“位于暂存池中”,str);

20}

21 else

22{

23 Console.WriteLine(“字符串\”{0}\“没有在暂存池中”,str);

24}

25}

26}

27}


上述代码的运行结果如下:


字符串"ab"没有在暂存池中

请按任意键继续……


由上述代码可知,“字符串驻留”过程并没有执行。接下来把上述代码中第12行的注释去掉,再重新运行一遍试试看,结果如下:


字符串"ab"位于暂存池中

请按任意键继续……


这说明CLR将值为"ab"的对象放入了“暂存池”,虽然为str2变量赋值的语句在判断"ab"是否在“暂存池”语句的后面,“字符串驻留”机制仍然生效了。

因此,“暂存池”缓存字符串对象的原则是:直接出现在代码中的字符串都会被放入“暂存池”,而动态创建的字符串对象并不会被放入。