第8章字符串和正则表达式.docx

上传人:b****4 文档编号:6084034 上传时间:2023-05-09 格式:DOCX 页数:27 大小:35.75KB
下载 相关 举报
第8章字符串和正则表达式.docx_第1页
第1页 / 共27页
第8章字符串和正则表达式.docx_第2页
第2页 / 共27页
第8章字符串和正则表达式.docx_第3页
第3页 / 共27页
第8章字符串和正则表达式.docx_第4页
第4页 / 共27页
第8章字符串和正则表达式.docx_第5页
第5页 / 共27页
第8章字符串和正则表达式.docx_第6页
第6页 / 共27页
第8章字符串和正则表达式.docx_第7页
第7页 / 共27页
第8章字符串和正则表达式.docx_第8页
第8页 / 共27页
第8章字符串和正则表达式.docx_第9页
第9页 / 共27页
第8章字符串和正则表达式.docx_第10页
第10页 / 共27页
第8章字符串和正则表达式.docx_第11页
第11页 / 共27页
第8章字符串和正则表达式.docx_第12页
第12页 / 共27页
第8章字符串和正则表达式.docx_第13页
第13页 / 共27页
第8章字符串和正则表达式.docx_第14页
第14页 / 共27页
第8章字符串和正则表达式.docx_第15页
第15页 / 共27页
第8章字符串和正则表达式.docx_第16页
第16页 / 共27页
第8章字符串和正则表达式.docx_第17页
第17页 / 共27页
第8章字符串和正则表达式.docx_第18页
第18页 / 共27页
第8章字符串和正则表达式.docx_第19页
第19页 / 共27页
第8章字符串和正则表达式.docx_第20页
第20页 / 共27页
亲,该文档总共27页,到这儿已超出免费预览范围,如果喜欢就下载吧!
下载资源
资源描述

第8章字符串和正则表达式.docx

《第8章字符串和正则表达式.docx》由会员分享,可在线阅读,更多相关《第8章字符串和正则表达式.docx(27页珍藏版)》请在冰点文库上搜索。

第8章字符串和正则表达式.docx

第8章字符串和正则表达式

第8章字符串和正则表达式

在本书的第一部分,我们一直在使用字符串,并说明C#中string关键字的映射实际上指向.NET基类System.String。

System.String是一个功能非常强大且用途非常广泛的基类,但它不是.NET中唯一与字符串相关的类。

本章首先复习一下System.String的特性,再介绍如何使用其他的.NET类来处理字符串,特别是System.Text和System.Text.RegularExpressions命名空间中的类。

本章主要介绍下述内容:

● 创建字符串:

如果多次修改一个字符串,例如,在显示字符串或将其传递给其他方法或应用程序前,创建一个较长的字符串,String类就会变得效率低下。

对于这种情况,应使用另一个类System.Text.StringBuilder,因为它是专门为这种情况设计的。

● 格式化表达式:

这些表达式将用于后面几章中的Console.WriteLine()方法。

格式化表达式使用两个有效的接口IFormatProvider和IFormattable来处理。

在自己的类上执行这两个接口,就可以定义自己的格式化序列,这样,Console.WriteLine()和类似的类就可以以指定的方式显示类的值。

● 正则表达式:

.NET还提供了一些非常复杂的类来识别字符串,或从长字符串中提取满足某些复杂条件的子字符串。

例如,找出字符串中重复出现的某个字符或一组字符,或者找出以s开头、且至少包含一个n的所有单词,或者找出遵循雇员ID或社会安全号码约定的字符串。

虽然可以使用String类,编写方法来执行这类处理,但这类方法编写起来比较繁琐,而使用System.Text.RegularExpressions命名空间中的类就比较简单,System.Text.RegularExpressions专门用于执行这类处理。

8.1 System.String类

在介绍其他字符串类之前,先快速复习一下String类上一些可用的方法。

System.String是一个类,专门用于存储字符串,允许对字符串进行许多操作。

由于这种数据类型非常重要,C#提供了它自己的关键字和相关的语法,以便于使用这个类来处理字符串。

使用运算符重载可以连接字符串:

stringmessage1="Hello"; //return"Hello"

message1+=",There";   //return"Hello,There"

stringmessage2=message1+"!

";    //return"Hello,There!

"

C#还允许使用类似于索引器的语法来提取指定的字符:

charchar4=message[4];  //returns'a'.Notethechariszero-indexed

这个类可以完成许多常见的任务,例如替换字符、删除空白和把字母变成大写形式等。

可用的方法如表8-1所示。

表 8-1

方法

作用

Compare

比较字符串的内容,考虑文化背景(区域),确定某些字符是否相等

CompareOrdinal

与Compare一样,但不考虑文化背景

Concat

把多个字符串实例合并为一个实例

CopyTo

把特定数量的字符从选定的下标复制到数组的一个全新实例中

Format

格式化包含各种值的字符串和如何格式化每个值的说明符

IndexOf

定位字符串中第一次出现某个给定子字符串或字符的位置

IndexOfAny

定位字符串中第一次出现某个字符或一组字符的位置

Insert

把一个字符串实例插入到另一个字符串实例的指定索引处

Join

合并字符串数组,建立一个新字符串

LastIndexOf

与IndexOf一样,但定位最后一次出现的位置

LastIndexOfAny

与IndexOfAny,但定位最后一次出现的位置

PadLeft

在字符串的开头,通过添加指定的重复字符填充字符串

PadRight

在字符串的结尾,通过添加指定的重复字符填充字符串

Replace

用另一个字符或子字符串替换字符串中给定的字符或子字符串

Split

在出现给定字符的地方,把字符串拆分为一个子字符串数组

Substring

在字符串中获取给定位置的子字符串

ToLower

把字符串转换为小写形式

ToUpper

把字符串转换为大写形式

Trim

删除首尾的空白

注意:

这个表并不完整,但可以让您明白字符串所提供的功能。

8.1.1 创建字符串

如上所述,string类是一个功能非常强大的类,它执行许多很有用的方法。

但是,string类存在一个问题:

重复修改给定的字符串,效率会很低,它实际上是一个不可变的数据类型,一旦对字符串对象进行了初始化,该字符串对象就不能改变了。

表面上修改字符串内容的方法和运算符实际上是创建一个新的字符串,如果必要,可以把旧字符串的内容复制到新字符串中。

例如,下面的代码:

stringgreetingText="HellofromalltheguysatWroxPress.";

greetingText+="Wedohopeyouenjoythisbookasmuchasweenjoyedwritingit.";

在执行这段代码时,首先,创建一个System.String类型的对象,并初始化为文本"HellofromalltheguysatWroxPress."。

注意句号后面有一个空格。

此时.NET运行库会为该字符串分配足够的内存来保存这个文本(39个字符),再设置变量greetingText,表示这个字符串实例。

从语法上看,下一行代码是把更多的文本添加到字符串中。

实际上并非如此,而是创建一个新字符串实例,给它分配足够的内存,以保存合并起来的文本(共103个字符)。

最初的文本"HellofromallthepeopleatWroxPress."复制到这个新字符串中,再加上额外的文本"Wedohopeyouenjoythisbookasmuchasweenjoyedwritingit."。

然后更新存储在变量greetingText中的地址,使变量正确地指向新的字符串对象。

旧的字符串对象被撤销了引用--不再有变量引用它,下一次垃圾收集器清理应用程序中所有未使用的对象时,就会删除它。

这本身还不坏,但假定要对这个字符串加密,在字母表中,用ASCII码中的字符替代其中的每个字母(标点符号除外),作为非常简单的加密模式的一部分,就会把该字符串变成"IfmmpgspnbmmuifhvstbuXspyQsftt.Xfepipqfzpvfokpzuijtcpplbtnvdibtxffokpzfexsjujohju."。

完成这个任务有好几种方式,但最简单、最高效的一种(假定只使用String类)是使用String.Replace()方法,把字符串中指定的子字符串用另一个子字符串代替。

使用Replace(),加密文本的代码如下所示:

stringgreetingText="HellofromalltheguysatWroxPress.";

greetingText+="Wedohopeyouenjoythisbookasmuchasweenjoyedwritingit.";

for(inti='z';i>='a';i--)

{

charold1=(char)i;

charnew1=(char)(i+1);

greetingText=greetingText.Replace(old1,new1);

}

for(inti='Z';i>='A';i--)

{

charold1=(char)i;

charnew1=(char)(i+1);

greetingText=greetingText.Replace(old1,new1);

}

Console.WriteLine("Encoded:

\n"+greetingText);

注意:

为了简单起见,这段代码没有把Z换成A,或把z换成a。

这些字符分别编码为[和{。

Replace()以一种智能化的方式工作,在某种程度上,它并没有创建一个新字符串,除非要对旧字符串进行某些改变。

原来的字符串包含23个不同的小写字母,和3个不同的大写字母。

所以Replace()就分配一个新字符串,共26次,每个新字符串都包含103个字符。

因此加密过程需要在堆上有一个能存储总共2678个字符的字符串对象,最终将等待被垃圾收集!

显然,如果使用字符串进行文字处理,应用程序就会有严重的性能问题。

为了解决这个问题,Microsoft提供了System.Text.StringBuilder类。

StringBuilder不像String那样支持非常多的方法。

在StringBuilder上可以进行的处理仅限于替换和添加或删除字符串中的文本。

但是,它的工作方式非常高效。

在使用String类构造一个字符串时,要给它分配足够的内存来保存字符串,但StringBuilder通常分配的内存会比需要的更多。

开发人员可以选择显式指定StringBuilder要分配多少内存,但如果没有显式指定,存储单元量在默认情况下就根据StringBuilder初始化时的字符串长度来确定。

它有两个主要的属性:

● Length指定字符串的实际长度;

● Capacity是字符串占据存储单元的最大长度。

对字符串的修改就在赋予StringBuilder实例的存储单元中进行,这就大大提高了添加子字符串和替换单个字符的效率。

删除或插入子字符串仍然效率低下,因为这需要移动随后的字符串。

只有执行扩展字符串容量的操作,才需要给字符串分配新内存,才可能移动包含的整个字符串。

在添加额外的容量时,从经验来看,StringBuilder如果检测到容量超出,且容量没有设置新值,就会使自己的容量翻倍。

例如,如果使用StringBuilder对象构造最初的欢迎字符串,可以编写下面的代码:

StringBuildergreetingBuilder=

newStringBuilder("HellofromalltheguysatWroxPress.",150);

greetingBuilder.AppendFormat("Wedohopeyouenjoythisbookasmuchasweenjoyed

writingit");   

注意:

为了使用StringBuilder类,需要在代码中引用System.Text。

在这段代码中,为StringBuilder设置的初始容量是150。

最好把容量设置为字符串可能的最大长度,确保StringBuilder不需要重新分配内存,因为其容量足够用了。

理论上,可以设置尽可能大的数字,足够给该容量传送一个int,但如果实际上给字符串分配20亿个字符的空间(这是StringBuilder实例允许拥有的最大理论空间),系统就可能会没有足够的内存。

执行上面的代码,首先创建一个StringBuilder对象,如图8-1所示。

 

图 8-1

在调用Append()方法时,其他文本就放在空的空间中,不需要分配更多的内存。

但是,多次替换文本才能获得使用StringBuilder所带来的性能提高。

例如,如果要以前面的方式加密文本,就可以执行整个加密过程,无须分配更多的内存:

StringBuildergreetingBuilder=

newStringBuilder("HellofromalltheguysatWroxPress.",150);

greetingBuilder.Append("Wedohopeyouenjoythisbookasmuchaswe

"+"enjoyedwritingit");

Console.WriteLine("NotEncoded:

\n"+greetingBuilder);

for(inti='z';i>='a';i--)

{

charold1=(char)i;

charnew1=(char)(i+1);

greetingBuilder=greetingBuilder.Replace(old1,new1);

}

for(inti='Z';i>='A';i--  )

{

charold1=(char)i;

charnew1=(char)(i+1);

greetingBuilder=greetingBuilder.Replace(old1,new1);

}

Console.WriteLine("Encoded:

\n"+greetingBuilder);

这段代码使用了StringBuilder.Replace()方法,它的功能与String.Replace()一样,但不需要在过程中复制字符串。

在上述代码中,为存储字符串而分配的总存储单元是150个字符,用于StringBuilder实例以及在最后一个Console.WriteLine()语句中执行字符串操作期间分配的内存。

一般,使用StringBuilder可以执行字符串的操作,String可以存储字符串或显示最终结果。

8.1.2 StringBuilder成员

前面介绍了StringBuilder的一个构造函数,它的参数是一个初始字符串及该字符串的容量。

还有几个其他的StringBuilder构造函数,例如,可以只提供一个字符串:

StringBuildersb=newStringBuilder("Hello");

或者用给定的容量创建一个空的StringBuilder:

StringBuildersb=newStringBuilder(20);

除了前面介绍的Length和Capacity属性外,还有一个只读属性MaxCapacity,它表示对给定的StringBuilder实例的容量限制。

在默认情况下,这由int.MaxValue给定(大约20亿,如前所述)。

但在构造StringBuilder对象时,也可以把这个值设置为较低的值:

//Thiswillbothsetinitialcapacityto100,butthemaxwillbe500.

//Hence,thisStringBuildercannevergrowtomorethan500characters,

//otherwiseitwillraiseexceptionifyoutrytodothat.

StringBuildersb=newStringBuilder(100,500);

还可以随时显式地设置容量,但如果把这个值设置为低于字符串的当前长度,或者超出了最大容量,就会抛出一个异常:

StringBuildersb=newStringBuilder("Hello");

sb.Capacity=100;

主要的StringBuilder方法如表8-2所示。

表 8-2

名称

作用

Append()

给当前字符串添加一个字符串

AppendFormat()

添加特定格式的字符串

Insert()

在当前字符串中插入一个子字符串

Remove()

从当前字符串中删除字符

Replace()

在当前字符串中,用某个字符替换另一个字符,或者用当前字符串中的一个子字符串替换另一字符串

ToString()

把当前字符串转换为System.String对象(在System.Object中被重写)

表 8-2其中一些方法还有几种格式的重载方法。

注意:

AppendFormat()实际上会在调用Console.WriteLine()时调用,它负责确定所有像{0:

D}的格式化表达式应使用什么表达式替代。

下一节讨论这个问题。

不能把StringBuilder转换为String(隐式转换和显式转换都不行)。

如果要把StringBuilder的内容输出为String,唯一的方式是使用ToString()方法。

前面介绍了StringBuilder类,说明了使用它提高性能的一些方式。

注意,这个类并不总能提高性能。

StringBuilder类基本上应在处理多个字符串时使用。

但如果只是连接两个字符串,使用System.String会比较好。

8.1.3 格式化字符串

前面的代码示例中编写了许多类和结构,对这些类和结构执行ToString()方法,都是为了显示给定变量的内容。

但是,用户常常希望以各种可能的方式显示变量的内容,在不同的文化或地区背景中有不同的格式。

.NET基类System.DateTime就是最明显的一个示例:

可以把日期显示为10June2008、10Jun2008、6/10/08(美国)、10/6/08(英国)或10.06.2008(德国)。

同样,第6章中编写的Vector结构执行Vector.ToString()方法,是为了以(4,56,8)格式显示矢量。

编写矢量的另一个非常常用的方式是4i+56j+8k。

如果要使类的用户友好性比较高,就需要使用某些工具以用户希望的方式显示它们的字符串表示。

.NET运行库定义了一种标准方式:

使用接口IFormattable,本节的主题就是说明如何把这个重要特性添加到类和结构上。

在显示一个变量时,常常需要指定它的格式,此时我们经常调用Console.WriteLine()方法。

因此,我们把这个方法作为示例,但这里的讨论适用于格式化字符串的大多数情况。

例如,如果要在列表框或文本框中显示一个变量的值,一般要使用String.Format()方法来获得该变量的合适字符串表示,但用于请求所需格式的格式说明符与传递给Console.WriteLine()的格式相同,因此本节把Console.WriteLine()作为一个示例来说明。

首先看看在为基本类型提供格式字符串时会发生什么,再看看如何把自己的类和结构的格式说明符添加到过程中。

第2章在Console.Write()和Console.WriteLine()中使用了格式字符串:

doubled=13.45;

inti=45;

Console.WriteLine("Thedoubleis{0,10:

E}andtheintcontains{1}",d,i);

格式字符串本身大都由要显示的文本组成,但只要有要格式化的变量,它在参数列表中的下标就必须放在括号中。

在括号中还可以有与该项的格式相关的其他信息,例如可以包含:

● 该项的字符串表示要占用的字符数,这个信息的前面应有一个逗号,负值表示该项应左对齐,正值表示该项应右对齐。

如果该项占用的字符数比给定的多,其内容也会完整地显示出来。

● 格式说明符也可以显示出来。

它的前面应有一个冒号,表示应如何格式化该项。

例如,把一个数字格式化为货币,或者以科学计数法显示。

第2章简要介绍了数字类型的常见格式说明符,表8-3再次引用该表。

表 8-3

格式符

应用

含义

示例

C

数字类型

专用场合的货币值

$4834.50(USA)

£4834.50(UK)

D

只用于整数类型

一般的整数

4834

E

数字类型

科学计数法

4.834E+003

F

数字类型

小数点后的位数固定

4384.50

G

数字类型

一般的数字

4384.5

N

数字类型

通常是专用场合的数字格式

4,384.50(UK/USA)

4384,50(欧洲大陆)

P

数字类型

百分比计数法

432,000.00%

X

只用于整数类型

十六进制格式

1120(如果要显示0x1120,需要写上0x)

如果要在整数上加上前导0,可以将格式说明符0重复所需的次数。

例如,格式说明符0000会把3显示为0003,99显示为0099。

这里不能给出完整的列表,因为其他数据类型有自己的格式说明符。

本节的主要目的是说明如何为自己的类定义格式说明符。

1.字符串的格式化

为了说明如何格式化字符串,看看执行下面的语句会得到什么结果:

Console.WriteLine("Thedoubleis{0,10:

E}andtheintcontains{1}",d,i);

Console.WriteLine()只是把参数的完整列表传送给静态方法String.Format(),如果要在字符串中以其他方式格式化这些值,例如显示在一个文本框中,也可以调用这个方法。

带有3个参数的WriteLine()重载方法如下:

//LikelyimplementationofConsole.WriteLine()

publicvoidWriteLine(stringformat,objectarg0,objectarg1)

{

Console.WriteLine(string.Format(format,arg0,arg1));

}

上面的代码依次调用了带有1个参数的重载方法WriteLine(),仅显示了传递过来的字符串的内容,没有对它进行进一步的格式化。

String.Format()现在需要用对应对象的合适字符串表示来替换每个格式说明符,构造最终的字符串。

但是,如前所述,对于这个建立字符串的过程,需要StringBuilder实例,而不是String实例。

在这个示例中,StringBuilder实例是用字符串的第一部分(即文本"Thedoubleis")创建和初始化的。

然后调用StringBuilder.AppendFormat()方法,传递第一个格式说明符"{0,10:

E}"和相应的对象double,把这个对象的字符串表示添加到构造好的字符串中,这个过程会继续重复调用StringBuilder.Append()和StringBuilder.AppendFormat()方法,直到得到了全部格式化好的字符串为止。

下面的内容比较有趣。

StringBuilder.AppendFormat()需要指出如何格式化对象,它首先检查对象,确定它是否执行System命名空间中的接口IFormattable。

只要试着把这个对象转换为接口,看看转换是否成功即可,或者使用C#关键字is,也能实现此测试。

如果测试失败,AppendFormat()只会调用对象的ToString()方法,所有的对象都从System.Object继承了这个方法或重写了该方法。

在前面给出的编写各种类和结构的示例中,执行过程都是这样,因为我们编写的类都没有执行这个接口。

这就是在前面的章节中,Object.ToString()的重写方法允许在Console.WriteLine()语句中显示类和结构如Vector的原因。

但是,所有预定义的基本数字类型都执行这个接口,对于这些类型,特别是这个示例中的double和int,就不会调用继承自System.Object的基

展开阅读全文
相关资源
猜你喜欢
相关搜索
资源标签

当前位置:首页 > 工程科技 > 能源化工

copyright@ 2008-2023 冰点文库 网站版权所有

经营许可证编号:鄂ICP备19020893号-2