第8章字符串和正則表達(dá)式_圖文_第1頁(yè)
第8章字符串和正則表達(dá)式_圖文_第2頁(yè)
第8章字符串和正則表達(dá)式_圖文_第3頁(yè)
第8章字符串和正則表達(dá)式_圖文_第4頁(yè)
第8章字符串和正則表達(dá)式_圖文_第5頁(yè)
已閱讀5頁(yè),還剩25頁(yè)未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說(shuō)明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)

文檔簡(jiǎn)介

1、第8章 字符串和正則表達(dá)式在本書(shū)的第一部分,我們一直在使用字符串,并說(shuō)明C#中string關(guān)鍵字的映射實(shí)際上指向.NET 基類(lèi)System.String。System.String是一個(gè)功能非常強(qiáng)大且用途非常廣泛的基類(lèi),但它不是.NET中唯一與字符串相關(guān)的類(lèi)。本章首先復(fù)習(xí)一下System.String的特性,再介紹如何使用其他的.NET類(lèi)來(lái)處理字符串,特別是System.Text 和 System.Text.Regular Expressions命名空間中的類(lèi)。本章主要介紹下述內(nèi)容: 創(chuàng)建字符串:如果多次修改一個(gè)字符串,例如,在顯示字符串或?qū)⑵鋫鬟f給其他方法或應(yīng)用程序前,創(chuàng)建一個(gè)較長(zhǎng)

2、的字符串,String類(lèi)就會(huì)變得效率低下。對(duì)于這種情況,應(yīng)使用另一個(gè)類(lèi)System.Text.StringBuilder,因?yàn)樗菍?zhuān)門(mén)為這種情況設(shè)計(jì)的。 格式化表達(dá)式:這些表達(dá)式將用于后面幾章中的Console.WriteLine(方法。格式化表達(dá)式使用兩個(gè)有效的接口IFormatProvider和IFormattable來(lái)處理。在自己的類(lèi)上執(zhí)行這兩個(gè)接口,就可以定義自己的格式化序列,這樣,Console.WriteLine(和類(lèi)似的類(lèi)就可以以指定的方式顯示類(lèi)的值。 正則表達(dá)式:.NET還提供了一些非常復(fù)雜的類(lèi)來(lái)識(shí)別字符串,或從長(zhǎng)字符串中提取滿足某些復(fù)雜條件的子字符串。例如

3、,找出字符串中重復(fù)出現(xiàn)的某個(gè)字符或一組字符,或者找出以s開(kāi)頭、且至少包含一個(gè)n的所有單詞,或者找出遵循雇員ID或社會(huì)安全號(hào)碼約定的字符串。雖然可以使用String類(lèi),編寫(xiě)方法來(lái)執(zhí)行這類(lèi)處理,但這類(lèi)方法編寫(xiě)起來(lái)比較繁瑣,而使用System.Text.RegularExpressions命名空間中的類(lèi)就比較簡(jiǎn)單,System.Text. RegularExpressions專(zhuān)門(mén)用于執(zhí)行這類(lèi)處理。8.1  System.String類(lèi)在介紹其他字符串類(lèi)之前,先快速?gòu)?fù)習(xí)一下String類(lèi)上一些可用的方法。System.String是一個(gè)類(lèi),專(zhuān)門(mén)用于存儲(chǔ)字符串,允許對(duì)字符串進(jìn)行許多操作。由于這

4、種數(shù)據(jù)類(lèi)型非常重要,C#提供了它自己的關(guān)鍵字和相關(guān)的語(yǔ)法,以便于使用這個(gè)類(lèi)來(lái)處理字符串。使用運(yùn)算符重載可以連接字符串:string message1 = "Hello"  /return "Hello"message1 += ", There"    / return "Hello, There "string message2 = message1 + "!"     / return "Hello, T

5、here!"C#還允許使用類(lèi)似于索引器的語(yǔ)法來(lái)提取指定的字符:char char4 = message4;   / returns 'a'. Note the char is zero-indexed這個(gè)類(lèi)可以完成許多常見(jiàn)的任務(wù),例如替換字符、刪除空白和把字母變成大寫(xiě)形式等??捎玫姆椒ㄈ绫?-1所示。表  8-1方 法作 用Compare比較字符串的內(nèi)容,考慮文化背景(區(qū)域,確定某些字符是否相等CompareOrdinal與Compare一樣,但不考慮文化背景Concat把多個(gè)字符串實(shí)例合并為一個(gè)實(shí)例CopyTo把特定數(shù)量的字符從選定的下

6、標(biāo)復(fù)制到數(shù)組的一個(gè)全新實(shí)例中Format格式化包含各種值的字符串和如何格式化每個(gè)值的說(shuō)明符IndexOf定位字符串中第一次出現(xiàn)某個(gè)給定子字符串或字符的位置IndexOfAny定位字符串中第一次出現(xiàn)某個(gè)字符或一組字符的位置Insert把一個(gè)字符串實(shí)例插入到另一個(gè)字符串實(shí)例的指定索引處Join合并字符串?dāng)?shù)組,建立一個(gè)新字符串LastIndexOf與IndexOf一樣,但定位最后一次出現(xiàn)的位置 LastIndexOfAny與IndexOfAny,但定位最后一次出現(xiàn)的位置PadLeft在字符串的開(kāi)頭,通過(guò)添加指定的重復(fù)字符填充字符串PadRight在字符串的結(jié)尾,通過(guò)添加指定的重復(fù)字符填充字符串Rep

7、lace用另一個(gè)字符或子字符串替換字符串中給定的字符或子字符串Split在出現(xiàn)給定字符的地方,把字符串拆分為一個(gè)子字符串?dāng)?shù)組Substring在字符串中獲取給定位置的子字符串ToLower把字符串轉(zhuǎn)換為小寫(xiě)形式ToUpper把字符串轉(zhuǎn)換為大寫(xiě)形式Trim刪除首尾的空白注意:這個(gè)表并不完整,但可以讓您明白字符串所提供的功能。8.1.1  創(chuàng)建字符串如上所述,string類(lèi)是一個(gè)功能非常強(qiáng)大的類(lèi),它執(zhí)行許多很有用的方法。但是,string類(lèi)存在一個(gè)問(wèn)題:重復(fù)修改給定的字符串,效率會(huì)很低,它實(shí)際上是一個(gè)不可變的數(shù)據(jù)類(lèi)型,一旦對(duì)字符串對(duì)象進(jìn)行了初始化,該字符串對(duì)象就不能改變了。表面上修改字符

8、串內(nèi)容的方法和運(yùn)算符實(shí)際上是創(chuàng)建一個(gè)新的字符串,如果必要,可以把舊字符串的內(nèi)容復(fù)制到新字符串中。例如,下面的代碼:string greetingText = "Hello from all the guys at Wrox Press. "greetingText += "We do hope you enjoy this book as much as we enjoyed writing it."在執(zhí)行這段代碼時(shí),首先,創(chuàng)建一個(gè)System.String類(lèi)型的對(duì)象,并初始化為文本"Hello from all the guys at Wro

9、x Press. "。注意句號(hào)后面有一個(gè)空格。此時(shí).NET 運(yùn)行庫(kù)會(huì)為該字符串分配足夠的內(nèi)存來(lái)保存這個(gè)文本(39個(gè)字符,再設(shè)置變量greetingText,表示這個(gè)字符串實(shí)例。從語(yǔ)法上看,下一行代碼是把更多的文本添加到字符串中。實(shí)際上并非如此,而是創(chuàng)建一個(gè)新字符串實(shí)例,給它分配足夠的內(nèi)存,以保存合并起來(lái)的文本(共103個(gè)字符。最初的文本"Hello from all the people at Wrox Press."復(fù)制到這個(gè)新字符串中,再加上額外的文本"We do hope you enjoy this book as much as we enjo

10、yed writing it."。然后更新存儲(chǔ)在變量greetingText中的地址,使變量正確地指向新的字符串對(duì)象。舊的字符串對(duì)象被撤銷(xiāo)了引用- 不再有變量引用它,下一次垃圾收集器清理應(yīng)用程序中所有未使用的對(duì)象時(shí),就會(huì)刪除它。這本身還不壞,但假定要對(duì)這個(gè)字符串加密,在字母表中,用ASCII碼中的字符替代其中的每個(gè)字母(標(biāo)點(diǎn)符號(hào)除外,作為非常簡(jiǎn)單的加密模式的一部分,就會(huì)把該字符串變成"Ifmmp gspn bmm uif hvst bu Xspy Qsftt. Xf ep ipqf zpv fokpz uijt cppl bt nvdi bt xf fokpzfe xsju

11、joh ju."。完成這個(gè)任務(wù)有好幾種方式,但最簡(jiǎn)單、最高效的一種(假定只使用String類(lèi)是使用String. Replace(方法,把字符串中指定的子字符串用另一個(gè)子字符串代替。使用Replace(,加密文本的代碼如下所示:string greetingText = "Hello from all the guys at Wrox Press. "greetingText += "We do hope you enjoy this book as much as we enjoyed writing it."for(int i = '

12、;z' i>='a' ; i-char old1 = (chari;char new1 = (char(i+1;greetingText = greetingText.Replace(old1, new1;for(int i = 'Z' i>='A' ; i-char old1 = (chari;char new1 = (char(i+1;greetingText = greetingText.Replace(old1, new1;Console.WriteLine("Encoded:n" + greeti

13、ngText;注意:為了簡(jiǎn)單起見(jiàn),這段代碼沒(méi)有把Z換成A,或把z換成a。這些字符分別編碼為和。Replace(以一種智能化的方式工作,在某種程度上,它并沒(méi)有創(chuàng)建一個(gè)新字符串,除非要對(duì)舊字符串進(jìn)行某些改變。原來(lái)的字符串包含23個(gè)不同的小寫(xiě)字母,和3個(gè)不同的大寫(xiě)字母。所以Replace(就分配一個(gè)新字符串,共26次,每個(gè)新字符串都包含103個(gè)字符。因此加密過(guò)程需要在堆上有一個(gè)能存儲(chǔ)總共2678個(gè)字符的字符串對(duì)象,最終將等待被垃圾收集!顯然,如果使用字符串進(jìn)行文字處理,應(yīng)用程序就會(huì)有嚴(yán)重的性能問(wèn)題。為了解決這個(gè)問(wèn)題,Microsoft提供了System.Text.StringBuilder類(lèi)。Str

14、ingBuilder不像String那樣支持非常多的方法。在StringBuilder上可以進(jìn)行的處理僅限于替換和添加或刪除字符串中的文本。但是,它的工作方式非常高效。在使用String類(lèi)構(gòu)造一個(gè)字符串時(shí),要給它分配足夠的內(nèi)存來(lái)保存字符串,但StringBuilder通常分配的內(nèi)存會(huì)比需要的更多。開(kāi)發(fā)人員可以選擇顯式指定StringBuilder要分配多少內(nèi)存,但如果沒(méi)有顯式指定,存儲(chǔ)單元量在默認(rèn)情況下就根據(jù)StringBuilder初始化時(shí)的字符串長(zhǎng)度來(lái)確定。它有兩個(gè)主要的屬性: Length指定字符串的實(shí)際長(zhǎng)度; Capacity是字符串占據(jù)存儲(chǔ)單元的最大長(zhǎng)度。對(duì)字符串

15、的修改就在賦予StringBuilder實(shí)例的存儲(chǔ)單元中進(jìn)行,這就大大提高了添加子字符串和替換單個(gè)字符的效率。刪除或插入子字符串仍然效率低下,因?yàn)檫@需要移動(dòng)隨后的字符串。只有執(zhí)行擴(kuò)展字符串容量的操作,才需要給字符串分配新內(nèi)存,才可能移動(dòng)包含的整個(gè)字符串。在添加額外的容量時(shí),從經(jīng)驗(yàn)來(lái)看,StringBuilder如果檢測(cè)到容量超出,且容量沒(méi)有設(shè)置新值,就會(huì)使自己的容量翻倍。例如,如果使用StringBuilder對(duì)象構(gòu)造最初的歡迎字符串,可以編寫(xiě)下面的代碼:StringBuilder greetingBuilder =new StringBuilder("Hello from all

16、the guys at Wrox Press. ", 150;greetingBuilder.AppendFormat("We do hope you enjoy this book as much as we enjoyed writing it"    注意:為了使用StringBuilder類(lèi),需要在代碼中引用System.Text。在這段代碼中,為StringBuilder設(shè)置的初始容量是150。最好把容量設(shè)置為字符串可能的最大長(zhǎng)度,確保StringBuilder不需要重新分配內(nèi)存,因?yàn)槠淙萘孔銐蛴昧?。理論上,可以設(shè)置盡可能大

17、的數(shù)字,足夠給該容量傳送一個(gè)int,但如果實(shí)際上給字符串分配20億個(gè)字符的空間(這是StringBuilder實(shí)例允許擁有的最大理論空間,系統(tǒng)就可能會(huì)沒(méi)有足夠的內(nèi)存。執(zhí)行上面的代碼,首先創(chuàng)建一個(gè)StringBuilder對(duì)象,如圖8-1所示。 圖  8-1在調(diào)用Append(方法時(shí),其他文本就放在空的空間中,不需要分配更多的內(nèi)存。但是,多次替換文本才能獲得使用StringBuilder所帶來(lái)的性能提高。例如,如果要以前面的方式加密文本,就可以執(zhí)行整個(gè)加密過(guò)程,無(wú)須分配更多的內(nèi)存:StringBuilder greetingBuilder =new StringBuilder

18、("Hello from all the guys at Wrox Press. ", 150;greetingBuilder.Append("We do hope you enjoy this book as much as we " + "enjoyed writing it"Console.WriteLine("Not Encoded:n" + greetingBuilder;for(int i = 'z' i>='a' ; i-char old1 = (chari;ch

19、ar new1 = (char(i+1;greetingBuilder = greetingBuilder.Replace(old1, new1;for(int i = 'Z' i>='A' ; i-   char old1 = (chari;char new1 = (char(i+1;greetingBuilder = greetingBuilder.Replace(old1, new1;Console.WriteLine("Encoded:n" + greetingBuilder; 這段代碼使用了StringBu

20、ilder.Replace(方法,它的功能與String.Replace(一樣,但不需要在過(guò)程中復(fù)制字符串。在上述代碼中,為存儲(chǔ)字符串而分配的總存儲(chǔ)單元是150個(gè)字符,用于StringBuilder實(shí)例以及在最后一個(gè)Console.WriteLine(語(yǔ)句中執(zhí)行字符串操作期間分配的內(nèi)存。一般,使用StringBuilder可以執(zhí)行字符串的操作,String可以存儲(chǔ)字符串或顯示最終結(jié)果。8.1.2  StringBuilder成員前面介紹了StringBuilder的一個(gè)構(gòu)造函數(shù),它的參數(shù)是一個(gè)初始字符串及該字符串的容量。還有幾個(gè)其他的StringBuilder構(gòu)造函數(shù),例如,可以只提

21、供一個(gè)字符串:StringBuilder sb = new StringBuilder("Hello"或者用給定的容量創(chuàng)建一個(gè)空的StringBuilder:StringBuilder sb = new StringBuilder(20;除了前面介紹的Length 和 Capacity屬性外,還有一個(gè)只讀屬性MaxCapacity,它表示對(duì)給定的StringBuilder實(shí)例的容量限制。在默認(rèn)情況下,這由int.MaxValue給定(大約20億,如前所述。但在構(gòu)造StringBuilder對(duì)象時(shí),也可以把這個(gè)值設(shè)置為較低的值:/ This will both set ini

22、tial capacity to 100, but the max will be 500./ Hence, this StringBuilder can never grow to more than 500 characters,/ otherwise it will raise exception if you try to do that.StringBuilder sb = new StringBuilder(100, 500;還可以隨時(shí)顯式地設(shè)置容量,但如果把這個(gè)值設(shè)置為低于字符串的當(dāng)前長(zhǎng)度,或者超出了最大容量,就會(huì)拋出一個(gè)異常:StringBuilder sb = new Str

23、ingBuilder("Hello"sb.Capacity = 100;主要的StringBuilder方法如表8-2所示。表  8-2名 稱(chēng)作 用Append(給當(dāng)前字符串添加一個(gè)字符串AppendFormat(添加特定格式的字符串Insert(在當(dāng)前字符串中插入一個(gè)子字符串Remove(從當(dāng)前字符串中刪除字符Replace(在當(dāng)前字符串中,用某個(gè)字符替換另一個(gè)字符,或者用當(dāng)前字符串中的一個(gè)子字符串替換另一字符串ToString(把當(dāng)前字符串轉(zhuǎn)換為System.String對(duì)象(在System.Object中被重寫(xiě)表  8-2其中一些方法還有幾種格式的

24、重載方法。注意:AppendFormat(實(shí)際上會(huì)在調(diào)用Console.WriteLine(時(shí)調(diào)用,它負(fù)責(zé)確定所有像0:D的格式化表達(dá)式應(yīng)使用什么表達(dá)式替代。下一節(jié)討論這個(gè)問(wèn)題。不能把StringBuilder轉(zhuǎn)換為String(隱式轉(zhuǎn)換和顯式轉(zhuǎn)換都不行。如果要把StringBuilder的內(nèi)容輸出為String,唯一的方式是使用ToString(方法。前面介紹了StringBuilder類(lèi),說(shuō)明了使用它提高性能的一些方式。注意,這個(gè)類(lèi)并不總能提高性能。StringBuilder類(lèi)基本上應(yīng)在處理多個(gè)字符串時(shí)使用。但如果只是連接兩個(gè)字符串,使用System.String會(huì)比較好。8.1.3

25、60; 格式化字符串前面的代碼示例中編寫(xiě)了許多類(lèi)和結(jié)構(gòu),對(duì)這些類(lèi)和結(jié)構(gòu)執(zhí)行ToString(方法,都是為了顯示給定變量的內(nèi)容。但是,用戶常常希望以各種可能的方式顯示變量的內(nèi)容,在不同的文化或地區(qū)背景中有不同的格式。.NET基類(lèi)System.DateTime就是最明顯的一個(gè)示例:可以把日期顯示為10 June 2008、10 Jun 2008、6/10/08 (美國(guó)、10/6/08 (英國(guó)或10.06.2008 (德國(guó)。同樣,第6章中編寫(xiě)的Vector結(jié)構(gòu)執(zhí)行Vector.ToString(方法,是為了以(4, 56, 8格式顯示矢量。編寫(xiě)矢量的另一個(gè)非常常用的方式是4i + 56j + 8k。

26、如果要使類(lèi)的用戶友好性比較高,就需要使用某些工具以用戶希望的方式顯示它們的字符串表示。.NET運(yùn)行庫(kù)定義了一種標(biāo)準(zhǔn)方式:使用接口IFormattable,本節(jié)的主題就是說(shuō)明如何把這個(gè)重要特性添加到類(lèi)和結(jié)構(gòu)上。在顯示一個(gè)變量時(shí),常常需要指定它的格式,此時(shí)我們經(jīng)常調(diào)用Console.WriteLine(方法。因此,我們把這個(gè)方法作為示例,但這里的討論適用于格式化字符串的大多數(shù)情況。例如,如果要在列表框或文本框中顯示一個(gè)變量的值,一般要使用String.Format(方法來(lái)獲得該變量的合適字符串表示,但用于請(qǐng)求所需格式的格式說(shuō)明符與傳遞給Console.WriteLine(的格式相同,因此本節(jié)把Co

27、nsole.WriteLine(作為一個(gè)示例來(lái)說(shuō)明。首先看看在為基本類(lèi)型提供格式字符串時(shí)會(huì)發(fā)生什么,再看看如何把自己的類(lèi)和結(jié)構(gòu)的格式說(shuō)明符添加到過(guò)程中。第2章在Console.Write(和Console.WriteLine(中使用了格式字符串:double d = 13.45;int i = 45;Console.WriteLine("The double is 0,10:E and the int contains 1", d, i;格式字符串本身大都由要顯示的文本組成,但只要有要格式化的變量,它在參數(shù)列表中的下標(biāo)就必須放在括號(hào)中。在括號(hào)中還可以有與該項(xiàng)的格式相關(guān)的其他

28、信息,例如可以包含: 該項(xiàng)的字符串表示要占用的字符數(shù),這個(gè)信息的前面應(yīng)有一個(gè)逗號(hào),負(fù)值表示該項(xiàng)應(yīng)左對(duì)齊,正值表示該項(xiàng)應(yīng)右對(duì)齊。如果該項(xiàng)占用的字符數(shù)比給定的多,其內(nèi)容也會(huì)完整地顯示出來(lái)。 格式說(shuō)明符也可以顯示出來(lái)。它的前面應(yīng)有一個(gè)冒號(hào),表示應(yīng)如何格式化該項(xiàng)。例如,把一個(gè)數(shù)字格式化為貨幣,或者以科學(xué)計(jì)數(shù)法顯示。第2章簡(jiǎn)要介紹了數(shù)字類(lèi)型的常見(jiàn)格式說(shuō)明符,表8-3再次引用該表。表  8-3格 式 符應(yīng) 用含 義示 例C數(shù)字類(lèi)型 專(zhuān)用場(chǎng)合的貨幣值$4834.50 (USA£4834.50 (UKD只用于整數(shù)類(lèi)型一般的整數(shù)4834E數(shù)字類(lèi)型科學(xué)計(jì)數(shù)法4.834E+0

29、03F數(shù)字類(lèi)型小數(shù)點(diǎn)后的位數(shù)固定4384.50G數(shù)字類(lèi)型一般的數(shù)字4384.5N數(shù)字類(lèi)型通常是專(zhuān)用場(chǎng)合的數(shù)字格式4,384.50 (UK/USA4 384,50 (歐洲大陸P數(shù)字類(lèi)型百分比計(jì)數(shù)法432,000.00%X只用于整數(shù)類(lèi)型十六進(jìn)制格式1120 (如果要顯示0x1120,需要寫(xiě)上0x如果要在整數(shù)上加上前導(dǎo)0,可以將格式說(shuō)明符0重復(fù)所需的次數(shù)。例如,格式說(shuō)明符0000會(huì)把3顯示為0003,99顯示為0099。這里不能給出完整的列表,因?yàn)槠渌麛?shù)據(jù)類(lèi)型有自己的格式說(shuō)明符。本節(jié)的主要目的是說(shuō)明如何為自己的類(lèi)定義格式說(shuō)明符。1. 字符串的格式化為了說(shuō)明如何格式化字符串,看看執(zhí)行下面的語(yǔ)句會(huì)得到什

30、么結(jié)果:Console.WriteLine("The double is 0,10:E and the int contains 1", d, i;Console.WriteLine(只是把參數(shù)的完整列表傳送給靜態(tài)方法String.Format(,如果要在字符串中以其他方式格式化這些值,例如顯示在一個(gè)文本框中,也可以調(diào)用這個(gè)方法。帶有3個(gè)參數(shù)的WriteLine(重載方法如下:/ Likely implementation of Console.WriteLine(public void WriteLine(string format, object arg0, objec

31、t arg1Console.WriteLine(string.Format(format, arg0, arg1;上面的代碼依次調(diào)用了帶有1個(gè)參數(shù)的重載方法WriteLine(,僅顯示了傳遞過(guò)來(lái)的字符串的內(nèi)容,沒(méi)有對(duì)它進(jìn)行進(jìn)一步的格式化。String.Format(現(xiàn)在需要用對(duì)應(yīng)對(duì)象的合適字符串表示來(lái)替換每個(gè)格式說(shuō)明符,構(gòu)造最終的字符串。但是,如前所述,對(duì)于這個(gè)建立字符串的過(guò)程,需要StringBuilder實(shí)例,而不是String實(shí)例。在這個(gè)示例中,StringBuilder實(shí)例是用字符串的第一部分(即文本"The double is"創(chuàng)建和初始化的。然后調(diào)用String

32、Builder.AppendFormat(方法,傳遞第一個(gè)格式說(shuō)明符0,10:E和相應(yīng)的對(duì)象double,把這個(gè)對(duì)象的字符串表示添加到構(gòu)造好的字符串中,這個(gè)過(guò)程會(huì)繼續(xù)重復(fù)調(diào)用StringBuilder.Append(和StringBuilder.AppendFormat(方法,直到得到了全部格式化好的字符串為止。下面的內(nèi)容比較有趣。StringBuilder.AppendFormat(需要指出如何格式化對(duì)象,它首先檢查對(duì)象,確定它是否執(zhí)行System命名空間中的接口IFormattable。只要試著把這個(gè)對(duì)象轉(zhuǎn)換為接口,看看轉(zhuǎn)換是否成功即可,或者使用C#關(guān)鍵字is,也能實(shí)現(xiàn)此測(cè)試。如果測(cè)試失敗

33、,AppendFormat(只會(huì)調(diào)用對(duì)象的ToString(方法,所有的對(duì)象都從System.Object繼承了這個(gè)方法或重寫(xiě)了該方法。在前面給出的編寫(xiě)各種類(lèi)和結(jié)構(gòu)的示例中,執(zhí)行過(guò)程都是這樣,因?yàn)槲覀兙帉?xiě)的類(lèi)都沒(méi)有執(zhí)行這個(gè)接口。這就是在前面的章節(jié)中,Object.ToString(的重寫(xiě)方法允許在Console.WriteLine(語(yǔ)句中顯示類(lèi)和結(jié)構(gòu)如Vector的原因。但是,所有預(yù)定義的基本數(shù)字類(lèi)型都執(zhí)行這個(gè)接口,對(duì)于這些類(lèi)型,特別是這個(gè)示例中的double和int,就不會(huì)調(diào)用繼承自System.Object的基本ToString(方法。為了理解這個(gè)過(guò)程,需要了解IFormattable接口

34、。IFormattable只定義了一個(gè)方法,該方法也叫作ToString(,它帶有兩個(gè)參數(shù),這與System. Object版本的ToString(不同,它不帶參數(shù)。下面是IFormattable的定義:interface IFormattablestring ToString(string format, IFormatProvider formatProvider;這個(gè)ToString(重載方法的第一個(gè)參數(shù)是一個(gè)字符串,它指定要求的格式。換言之,它是字符串的說(shuō)明符部分,放在字符串的中,該參數(shù)最初傳遞給Console.WriteLine(或String. Format(。例如,在本例中,最初

35、的語(yǔ)句如下:Console.WriteLine("The double is 0,10:E and the int contains 1", d, i;在計(jì)算第一個(gè)說(shuō)明符0,10:E時(shí),在double變量d上調(diào)用這個(gè)重載方法,傳遞給它的第一個(gè)參數(shù)是E。StringBuilder.AppendFormat(傳遞的總是顯示在原始字符串的合適格式說(shuō)明符內(nèi)冒號(hào)后面的文本。本書(shū)不討論ToString(的第2個(gè)參數(shù),它是執(zhí)行接口IFormatProvider的對(duì)象引用。這個(gè)接口提供了ToString(在格式化對(duì)象時(shí)需要考慮的更多信息- 一般包括文化背景信息(.NET文化背景類(lèi)似于Win

36、dows時(shí)區(qū),如果格式化貨幣或日期,就需要這些信息。如果直接從源代碼中調(diào)用這個(gè)ToString(重載方法,就需要提供這樣一個(gè)對(duì)象。但StringBuilder. Append Format(為這個(gè)參數(shù)傳遞一個(gè)空值。如果formatProvider為空,ToString(就要使用系統(tǒng)設(shè)置中指定的文化背景信息。現(xiàn)在回過(guò)頭來(lái)看看本例。第一個(gè)要格式化的項(xiàng)是double,對(duì)此要求使用指數(shù)計(jì)數(shù)法,格式說(shuō)明符為E。如前所述,StringBuilder.AppendFormat(方法會(huì)建立執(zhí)行IFormattable接口的對(duì)象double,因此要調(diào)用帶有兩個(gè)參數(shù)的ToString(重載方法,其第一個(gè)參數(shù)是字符

37、串"E",第二個(gè)參數(shù)為空?,F(xiàn)在double的這個(gè)方法在執(zhí)行時(shí),會(huì)考慮要求的格式和當(dāng)前的文化背景,以合適的格式返回double的字符串表示。StringBuilder.AppendFormat(則按照需要在返回的字符串中添加前導(dǎo)空格,使之共有10個(gè)字符。下一個(gè)要格式化的對(duì)象是int,它不需要任何特殊的格式 (格式說(shuō)明符是1。由于沒(méi)有格式要求,StringBuilder.AppendFormat(會(huì)給該格式字符串傳遞一個(gè)空引用,并適當(dāng)?shù)仨憫?yīng)帶有兩個(gè)參數(shù)的int.ToString(重載方法。由于沒(méi)有特殊的格式要求,所以也可以調(diào)用不帶參數(shù)的ToString(方法。整個(gè)字符串格式化過(guò)

38、程如圖8-2所示。 圖  8-22. FormattableVector示例前面介紹了如何構(gòu)造格式字符串,下面擴(kuò)展本書(shū)第6章的Vector示例,以多種方式格式化矢量。這個(gè)示例的代碼可以從上下載。只要理解了所涉及的規(guī)則,實(shí)際編寫(xiě)代碼就相當(dāng)簡(jiǎn)單了。我們只需要實(shí)現(xiàn)IFormattable,提供由該接口定義的ToString(重載方法即可。要支持的格式說(shuō)明符如下: N 應(yīng)解釋為一個(gè)請(qǐng)求,以提供一個(gè)數(shù)字,即矢量的模,它是其成員的平方和,在數(shù)學(xué)上等于Vector的長(zhǎng)度的平方,通常放在兩個(gè)豎杠的中間:|34.5|。 VE 應(yīng)解釋為以科學(xué)計(jì)數(shù)法顯示每個(gè)成員的一個(gè)請(qǐng)求,例

39、如說(shuō)明符E應(yīng)用于double,就可以表示為(2.3E+01, 4.5E+02, 1.0E+00。 IJK應(yīng)解釋為以格式23i + 450j + 1k顯示矢量的一個(gè)請(qǐng)求。  其他內(nèi)容應(yīng)僅返回Vector的默認(rèn)表示方法(23, 450, 1.0。為了簡(jiǎn)單起見(jiàn),我們不以IJK和科學(xué)計(jì)數(shù)法的格式執(zhí)行任何選項(xiàng),以顯示矢量,而是以不區(qū)分大小寫(xiě)的方式來(lái)測(cè)試說(shuō)明符,允許使用ijk和IJK。注意,使用什么字符串表示格式說(shuō)明符完全取決于用戶。為此,首先修改Vector的聲明,使之執(zhí)行IFormattable:struct Vector : IFormattablepublic double x

40、, y, z;   / Beginning part of Vector下面添加帶有2個(gè)參數(shù)的ToString(重載方法:public string ToString(string format, IFormatProvider formatProviderif (format = nullreturn ToString(;string formatUpper = format.ToUpper(;switch (formatUppercase "N":return "| " + Norm(.ToString( + " |&q

41、uot;case "VE":return String.Format("( 0:E, 1:E, 2:E ", x, y, z;case "IJK":StringBuilder sb = new StringBuilder(x.ToString(, 30;sb.AppendFormat (" i + "sb.AppendFormat (y.ToString(;sb.AppendFormat (" j + "sb.AppendFormat (z.ToString(;sb.AppendFormat (

42、" k"return sb.ToString(;default:return ToString(;這就是我們要編寫(xiě)的代碼。注意在調(diào)用任何方法前,應(yīng)防止使用格式字符串為空的參數(shù)。我們希望這個(gè)方法盡可能健壯,所有基本類(lèi)型的格式說(shuō)明符都是不區(qū)分大小寫(xiě)的,其他開(kāi)發(fā)人員也希望能使用我們的類(lèi)。對(duì)于格式說(shuō)明符VE,需要把每個(gè)成員格式化為科學(xué)計(jì)數(shù)法,所以再次使用String.Format (方法。字段x、y和z都是double類(lèi)型。對(duì)于IJK格式限定符,把幾個(gè)子字符串添加到字符串中,因此使用StringBuilder對(duì)象來(lái)提高性能。為了保證完整,也可以再次使用前面開(kāi)發(fā)的無(wú)參數(shù)的ToStri

43、ng(重載方法:public override string ToString(return "( " + x + " , " + y + " , " + z + " " 最后,需要添加一個(gè)Norm(方法,計(jì)算矢量的平方(模,因?yàn)樵陂_(kāi)發(fā)Vector結(jié)構(gòu)時(shí),沒(méi)有提供這個(gè)方法:public double Norm(return x*x + y*y + z*z;下面用一些合適的測(cè)試代碼測(cè)試可格式化的矢量:static void Main(Vector v1 = new Vector(1,32,5;Vector v2 =

44、new Vector(845.4, 54.3, -7.8;Console.WriteLine("nIn IJK format,nv1 is 0,30:IJKnv2 is 1,30:IJK", v1, v2;Console.WriteLine("nIn default format,nv1 is 0,30nv2 is 1,30", v1, v2;Console.WriteLine("nIn VE formatnv1 is 0,30:VEnv2 is 1,30:VE", v1, v2;Console.WriteLine("nNo

45、rms are:nv1 is 0,20:Nnv2 is 1,20:N", v1, v2;運(yùn)行這個(gè)示例的結(jié)果如下所示:FormattableVectorIn IJK format,v1 is               1 i + 32 j + 5 kv2 is        845.4 i + 54.3 j + -7.8 kIn default format,v1 is

46、0;                ( 1 , 32 , 5 v2 is          ( 845.4 , 54.3 , -7.8 In VE formatv1 is ( 1.000000E+000, 3.200000E+001, 5.000000E+000 v2 is ( 8.454000E+002, 5.430000E+001,-7.800000E+

47、000 Norms are:v1 is           | 1050 |v2 is      | 717710.49 |這說(shuō)明了選用的定制格式說(shuō)明符是正確的。8.2  正則表達(dá)式正則表達(dá)式在各種程序中都有著難以置信的作用,但并不是所有的開(kāi)發(fā)人員都知道這一點(diǎn)。正則表達(dá)式可以看做一種有特定功能的小型編程語(yǔ)言:在大的字符串表達(dá)式中定位一個(gè)子字符串。它不是一種新技術(shù),最初它是在UNIX環(huán)境中開(kāi)發(fā)的,與Perl一起使用得比較多。Microso

48、ft把它移植到Windows中,到目前為止在腳本語(yǔ)言中用得比較多。但System. Text.RegularExpressions命名空間中的許多.NET類(lèi)都支持正則表達(dá)式。.NET Framework的各個(gè)部分都使用正則表達(dá)式,例如,在ASP.NET的驗(yàn)證服務(wù)器控件中就使用了正則表達(dá)式。許多人都不太熟悉正則表達(dá)式語(yǔ)言,所以本節(jié)將主要解釋正則表達(dá)式和相關(guān)的.NET類(lèi)。如果您很熟悉正則表達(dá)式,就可以跳過(guò)本節(jié),學(xué)習(xí).NET基類(lèi)的引用。注意,.NET正則表達(dá)式引擎是為兼容Perl 5的正則表達(dá)式而設(shè)計(jì)的,但有一些新特性。8.2.1  正則表達(dá)式概述正則表達(dá)式語(yǔ)言是一種專(zhuān)門(mén)用于字符串處理的語(yǔ)

49、言。它包含兩個(gè)功能: 一組用于標(biāo)識(shí)字符類(lèi)型的轉(zhuǎn)義代碼。您可能很熟悉DOS表達(dá)式中的*字符表示任意子字符串(例如,DOS命令Dir Re*會(huì)列出所有名稱(chēng)以Re開(kāi)頭的文件。正則表達(dá)式使用與*類(lèi)似的許多序列來(lái)表示"任意一個(gè)字符"、"一個(gè)單詞"、"一個(gè)可選的字符"等。 一個(gè)系統(tǒng)。在搜索操作中,它把子字符串和中間結(jié)果的各個(gè)部分組合起來(lái)。使用正則表達(dá)式,可以對(duì)字符串執(zhí)行許多復(fù)雜而高級(jí)的操作,例如: 區(qū)分(可以是標(biāo)記或刪除字符串中所有重復(fù)的單詞,例如,把The computer books books轉(zhuǎn)換為T(mén)he co

50、mputer books。 把所有單詞都轉(zhuǎn)換為標(biāo)題格式,例如把this is a Title轉(zhuǎn)換為T(mén)his Is A Title。 把長(zhǎng)于3個(gè)字符的所有單詞都轉(zhuǎn)換為標(biāo)題格式,例如把this is a Title轉(zhuǎn)換為T(mén)his is a Title。 確保句子有正確的大寫(xiě)形式。 區(qū)分URI的各個(gè)元素(例如,提取出協(xié)議、計(jì)算機(jī)名、文件名等。當(dāng)然,這些都是可以在C#中用System.String和System.Text.StringBuilder的各種方法執(zhí)行的任務(wù)。但是,在一些情況下,還需要編寫(xiě)相當(dāng)多的C#代碼。如果使用正則表達(dá)式,這些代碼一般可以壓縮為幾行

51、代碼。實(shí)際上,是實(shí)例化了一個(gè)對(duì)象System.Text.Regular Expressions.RegEx(甚至更簡(jiǎn)單:調(diào)用靜態(tài)的RegEx(方法,給它傳送要處理的字符串和一個(gè)正則表達(dá)式(這是一個(gè)字符串,包含用正則表達(dá)式語(yǔ)言編寫(xiě)的指令,就可以了。正則表達(dá)式字符串初看起來(lái)像是一般的字符串,但其中包含了轉(zhuǎn)義序列和有特定含義的其他字符。例如,序列b表示一個(gè)字的開(kāi)頭和結(jié)尾(字的邊界,如果要表示正在查找以字符th開(kāi)頭的字,就可以編寫(xiě)正則表達(dá)式bth(即序列字邊界是- t - h。如果要搜索所有以th結(jié)尾的字,就可以編寫(xiě)thb(序列t - h-字邊界。但是,正則表達(dá)式要比這復(fù)雜得多,包括可以在搜索操作中找

52、到存儲(chǔ)部分文本的工具性程序。本節(jié)僅介紹正則表達(dá)式的功能。提示:正則表達(dá)式的更多信息可參閱圖書(shū)B(niǎo)eginning Regular Expressions(ISBN:978-0-7645-7489-4。假定應(yīng)用程序需要把US電話號(hào)碼轉(zhuǎn)換為國(guó)際格式。在美國(guó),電話號(hào)碼的格式為314-123-1234,常常寫(xiě)作(314123-1234。在把這個(gè)國(guó)家格式轉(zhuǎn)換為國(guó)際格式時(shí),必須在電話號(hào)碼的前面加上+1(美國(guó)的國(guó)家代碼,并給區(qū)號(hào)加上括號(hào):+1(314 123-1234。在查找和替換時(shí),這并不復(fù)雜,但如果要使用String類(lèi)完成這個(gè)轉(zhuǎn)換,就需要編寫(xiě)一些代碼(這表示,必須使用System.String上的方法來(lái)編

53、寫(xiě)代碼,而正則表達(dá)式語(yǔ)言可以構(gòu)造一個(gè)短的字符串來(lái)表達(dá)上述含義。所以,本節(jié)只有一個(gè)非常簡(jiǎn)單的示例,我們只考慮如何查找字符串中的某些子字符串,無(wú)須考慮如何修改它們。8.2.2  RegularExpressionsPlayaround示例下面將開(kāi)發(fā)一個(gè)小示例RegularExpressionsPlayaround,執(zhí)行并顯示一些搜索的結(jié)果,說(shuō)明正則表達(dá)式的一些特性,以及如何在C#中使用.NET正則表達(dá)式引擎。這個(gè)示例文檔中使用的文本是引自另一本有關(guān)ASP.NET的Wrox Press書(shū)籍ASP.NET 3.5高級(jí)編程(第5版,清華大學(xué)出版社引進(jìn)并出版:string Text = &quo

54、t;This comprehensive compendium provides a broad and thorough investigation of allaspects of programming with ASP.NET. Entirely revised and updated for the 3.5 Release of .NET, this book will give you the information you need to master ASP.NETand build a dynamic, successful, enterprise Web applicati

55、on."注意:不考慮換行,則上面的表達(dá)式是合法的C#代碼- 說(shuō)明了使用字符串時(shí)應(yīng)在前面加上符號(hào)。我們把這個(gè)文本稱(chēng)為輸入字符串。為了說(shuō)明正則表達(dá)式.NET類(lèi),我們先進(jìn)行一次純文本的搜索,這次搜索不帶任何轉(zhuǎn)義序列或正則表達(dá)式命令。假定要查找所有的字符串ion,把這個(gè)搜索字符串稱(chēng)為模式。使用正則表達(dá)式和上面聲明的變量Text,編寫(xiě)出下面的代碼:string Pattern = "ion"MatchCollection Matches = Regex.Matches(Text, Pattern,RegexOptions.IgnoreCase |RegexOptions.E

56、xplicitCapture;foreach (Match NextMatch in MatchesConsole.WriteLine(NextMatch.Index;在這段代碼中,使用了System.Text.RegularExpressions命名空間中Regex類(lèi)的靜態(tài)方法Matches(。這個(gè)方法的參數(shù)是一些輸入文本、一個(gè)模式和RegexOptions枚舉中的一組可選標(biāo)志。在本例中,指定所有的搜索都不應(yīng)區(qū)分大小寫(xiě)。另一個(gè)標(biāo)記ExplicitCapture 改變了收集匹配的方式,對(duì)于本例,這樣可以使搜索的效率更高,其原因詳見(jiàn)后面的內(nèi)容(盡管它還有這里沒(méi)有介紹的其他用法。Matches(返

57、回MatchCollections對(duì)象的引用。匹配是一個(gè)技術(shù)術(shù)語(yǔ),表示在表達(dá)式中查找模式實(shí)例的結(jié)果,用System.Text.RegularExpressions.Match來(lái)代表。因此,我們返回一個(gè)包含所有匹配的MatchCollection,每個(gè)匹配都用一個(gè)Match對(duì)象來(lái)表示。在上面的代碼中,只是在集合中迭代,使用Match類(lèi)的Index屬性,返回輸入文本中匹配所在的索引。運(yùn)行這段代碼,將得到3個(gè)匹配。表8-4描述了RegexOptions枚舉的一些選項(xiàng)。表  8-4成 員 名說(shuō) 明CultureInvariant指定忽略字符串的文化背景ExplicitCapture修改收集匹

58、配的方式,確保把明確指定的匹配作為有效的搜索結(jié)果IgnoreCase忽略輸入字符串的大小寫(xiě)IgnorePatternWhitespace在字符串中刪除未轉(zhuǎn)義的空白,使注釋用英鎊符號(hào)或短橫線符號(hào)指定Multiline修改字符和$,把它們應(yīng)用于每一行的開(kāi)頭和結(jié)尾,而不僅僅應(yīng)用于整個(gè)字符串的開(kāi)頭和結(jié)尾RightToLeft從右到左地讀取輸入字符串,而不是從左到右地讀取(適合于一些亞洲語(yǔ)言或其他以這種方式讀取的語(yǔ)言Singleline指定句點(diǎn)的含義(.,它原來(lái)表示單行模式,現(xiàn)在改為匹配每個(gè)字符除了一些新的.NET基類(lèi)外,其他內(nèi)容都不是新的。但正則表達(dá)式的功能主要取決于模式字符串。原因是模式字符串不僅僅

59、包含純文本。如前所述,它還可以包含元字符和轉(zhuǎn)義序列,其中元字符是給出命令的特定字符,而轉(zhuǎn)義序列的工作方式與C#的轉(zhuǎn)義序列相同,它們都是以反斜杠開(kāi)頭的字符,具有特殊的含義。例如,假定要查找以n開(kāi)頭的字,就可以使用轉(zhuǎn)義序列b,它表示一個(gè)字的邊界(字的邊界是以字母數(shù)字表中的某個(gè)字符開(kāi)頭,或者后面是一個(gè)空白字符或標(biāo)點(diǎn)符號(hào)??梢跃帉?xiě)如下代碼:string Pattern = "bn"MatchCollection Matches = Regex.Matches(Text, Pattern,RegexOptions.IgnoreCase | RegexOptions.ExplicitCapture;注意字符串前面的符號(hào)。要在運(yùn)行時(shí)把b傳遞給.NET正則表達(dá)式引擎,反斜杠不應(yīng)被C#編譯器解釋為轉(zhuǎn)義序列。如果要查找以序列ion結(jié)尾的字,可以使用下面的代碼:string Pattern = "ionb"如果要查找以字母a開(kāi)頭,以序列io

溫馨提示

  • 1. 本站所有資源如無(wú)特殊說(shuō)明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒(méi)有圖紙預(yù)覽就沒(méi)有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫(kù)網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。

評(píng)論

0/150

提交評(píng)論