2020年10月9日星期五

006 零基礎學的Python 第一篇 Python語言基礎 第6章 字串與規則運算式

 申明本站飛宇網 https://feiyetopro.blogspot.com/自網路收集整理之書籍文章影音僅供預覽交流學習研究,其[書籍、文章、影音]情節內容, 評論屬其個人行為, 與本網站無關。版權歸原作者和出版社所有,請在下載 24 小時內刪除,不得用作商業用途;如果您喜歡其作品,請支持訂閱購買[正版]謝謝!


6章 字串與規則運算式

本章將介紹Python中字串和規則運算式的概念。字串是開發應用中常用的資料類型,字串的處理是實際應用中經常面對的問題。Python提供了功能強大的字串模組,本章將詳細介紹字串模組中函數的使用。規則運算式專門用於匹配應用中的資料,能夠簡化字串的處理常式,Python提供了模組匹配規則運算式。最後將介紹字串函數和規則運算式的應用場合。

本章的知識點:

·字串的格式化

·字串的截取、合併、過濾等操作

·字串的查找

·規則運算式的語法

·Python的規則運算式模組

6.1 常見的字串操作

字串是Python的一種基本類型,字串的操作包括字串的格式化、字串的截取、過濾、合併、查找等操作。下面將一一介紹這些內容。

6.1.1 字串的格式化

C語言使用函數printf()sprintf()格式化輸出結果,Python也提供了類似的功能。Python將若干值插入帶有%標記的字串中,從而可以動態地輸出字串。字串的格式化語法如下所示。


"%s" % str1

"%s %s" % (str1, str2)


【代碼說明】 1行代碼使用一個值格式化字串。第2行代碼使用多個值格式化字串,用於替換的值組成一個元組。

下面這段代碼演示了字串的格式化操作。


01      # 格式化字串

02      str1 = "version"

03      num = 1.0

04      format = "%s" % str1

05      print (format)

06      format = "%s %d" % (str1, num)

07      print (format)


【代碼說明】

·第4行代碼用變數str1的值替換字串中的%s

·第5行代碼輸出結果是“version”。

·第6行代碼分別用變數str1num的值替換%s%d的值。%d表示替換的值為整型。

·第7行代碼輸出結果為“version 1”。

注意  如果要格式化多個值,元組中元素的順序必須和格式化字串中替代符的順序一致,否則,可能出現類型不匹配的問題。如果將上例中的%s%d調換位置,將拋出如下異常:


TypeError: int argument required


使用%f可以格式化浮點數的精度,根據指定的精度做四捨五入。示例如下:


01      # 帶精度的格式化

02      print ("浮點型數字: %f" % 1.25)       # 以浮點數格式列印

03      print ("浮點型數字: %.1f" % 1.25)     # 精確到小數點後1

04      print ("浮點型數字: %.2f" % 1.254)    # 精確到小數點後2


【代碼說明】

·第2行代碼格式化浮點數1.25。預設情況下,將輸出小數點後6位元數字。輸出結果:


浮點型數字: 1.250000


·第3行代碼格式化小數點後1位元數字,“四捨五入”後的結果為1.3。輸出結果:


浮點型數字: 1.3


·第4行代碼格式化小數點後2位元數位。輸出結果:


浮點型數字: 1.25


此外,Python還提供了對八進制、十六進位等數位進行格式化的替代符。表6-1列出了Python中格式化字串的替代符及其含義。

6-1 Python格式化字串的替代符及其含義


注意  如果要在字串中輸出“%”,需要使用“%%”。

前面使用了元素格式化多個值,也可以用字典格式化多個值,並指定格式化字串的名稱。下面這段代碼說明了字典格式化字串的用法。


01      # 使用字典格式化字串

02      print ("%(version)s: %(num).1f" % {"version": "version", "num": 2})


【代碼說明】 2行代碼定義了一個匿名字典,該字典中的兩個value值分別對應字串中的%(version)s%(num).1f。輸出結果:version:2.0

Python可以實現字串的對齊操作,類似C語言中的%[[+/-]n]s。此外,還提供了字串對齊的函數。


01      # 字串對齊

02      word = "version3.0"

03      print (word.center(20))

04      print (word.center(20, "*"))

05      print (word.ljust(0))

06      print (word.rjust(20))

07      print ("%30s" % word)


【代碼說明】

·第3行代碼調用center()輸出變數word的值。變數word兩側各輸出5個空格。輸出結果:


version3.0


·第4行代碼調用center()輸出變數word的值,並指定第2個參數的值為“*”。變數word兩側各輸出5個“*”。輸出結果:


*****version3.0*****


·第5行代碼調用ljust()輸出變數word的值,ljust()輸出結果左對齊。輸出結果:


version3.0


·第6行代碼調用rjust()輸出變數word的值,rjust()輸出結果右對齊。參數20表示一共輸出20個字元,“version3.0”占10個字元,左邊填充10個空格。輸出結果:


          version3.0


·第7行代碼,“%30s”表示先輸出30個空格,再輸出變數word的值,類似於word.rjust(30)的作用。輸出結果:


                    version3.0


6.1.2 字串的轉義符

電腦中存在可見字元與不可見字元。可見字元是指鍵盤上的字母、數位和符號。不可見字元是指換行、回車等字元,對於不可見字元可以使用轉義字元來表示。Python中轉義字元的用法和Java相同,都是使用\作為轉義字元。下面這段代碼演示了轉義字元的使用。


01      # 輸出轉義字元

02      path = "hello\tworld\n"

03      print (path)

04      print (len(path))

05      path = r"hello\tworld\n"

06      print (path)

07      print (len(path))


【代碼說明】

·第2行代碼,在“hello”和“world”之間輸出定位字元,在字串末尾輸出分行符號。

·第3行代碼輸出結果:


hello world


·第4行代碼輸出字串的長度,其中的“\t”、“\n”各占一個字元。輸出結果為12

·第5行代碼,忽略轉義字元的作用,直接輸出字串原始的內容。

·第6行代碼輸出結果:


hello\tworld\n


·第7行代碼輸出字串的長度。輸出結果為“14”。

注意  Python的定位字元只占1個字元,而不是2個或4個字元。

Python支援的轉義字元如表6-2所示。

6-2 Python的轉義字元及其含義


注意  如果要在字串中輸出“\”,需要使用“\\”。

Python還提供了函數strip()lstrip()rstrip()去掉字串中的轉義符。


# strip()去掉轉義字元

word = "\thello world\n"

print ("直接輸出:", word)

print ("strip()後輸出:", word.strip())

print ("lstrip()後輸出:", word.lstrip())

print ("rstrip()後輸出:", word.rstrip())


【代碼說明】

·第3行代碼直接輸出字串。輸出結果:


直接輸出:hello world


·第4行代碼調用strip()去除轉義字元。輸出結果:


strip()後輸出: hello world


·第5行代碼調用lstrip()去除字串前面的轉義字元“\t”,字串末尾的“\n”依然存在。輸出結果:


lstrip()後輸出: hello world


·第6行代碼調用rstrip()去除字串末尾的轉義字元“\n”,字串前面的“\t”依然存在。輸出結果:


rstrip()後輸出:        hello world


6.1.3 字串的合併

Java語言一樣,Python使用+連接不同的字串。Python會根據+兩側變數的類型,決定執行連接操作或加法運算。如果+兩側都是字串類型,則進行連接操作;如果+兩側都是數位類型,則進行加法運算;如果+兩側是不同的類型,將拋出異常。


TypeError: cannot concatenate 'str' and 'int' objects


下面的代碼演示了字串的連接方法。


01      # 使用"+"連接字串

02      str1 = "hello "

03      str2 = "world "

04      str3 = "hello "

05      str4 = "China "

06      result = str1 + str2 + str3

07      result += str4

08      print (result)


【代碼說明】

·第6行代碼,把變數str1str2str3的值連接起來,並把結果存放在變數result中。

·第7行代碼,使用運算子“+=”連接變數resultstr4

·第8行代碼輸出結果:


hello world hello China


可見,使用+對多個字串進行連接稍顯煩瑣。Python提供了函數join()連接字串,join()配合清單實現多個字串的連接十分方便。


01      # 使用join()連接字串

02      strs = ["hello ", "world ", "hello ", "China "]

03      result = "".join(strs)

04      print (result)


【代碼說明】

·第2行代碼用清單取代變數,把多個字串存放在清單中。

·第3行代碼調用join(),每次連接清單中的一個元素。

·第4行代碼輸出結果:


hello world hello China


前面學習了reduce()函數,reduce()的作用就是對某個變數進行累計。這裡可以對字串進行累計連接,從而實現多個字串進行連接的功能。


01      # 使用reduce()連接字串

02      from functools import reduce

03      import operator

04      strs = ["hello ", "world ", "hello ", "China "]

05      result = reduce(operator.add, strs, "")

06      print (result)


【代碼說明】

·第3行代碼導入模組operator,利用方法add()實現累計連接。

·第5行代碼調用reduce()實現對空字串“”的累計連接,每次連接清單strs中的一個元素。

·第6行代碼輸出結果:


hello world hello China


6.1.4 字串的截取

字串的截取是實際應用中經常使用的技術,被截取的部分稱為子串Java中使用函數substr()獲取子串,C#使用函數substring()獲取子串。而Python由於內置了序列,可以通過前面介紹的索引、切片獲取子串,也可以使用函數split()來獲取。字串也屬於序列,下面這段代碼使用序列的索引獲取子串。


01      # 使用索引截取子串

02      word = "world"

03      print (word[4])


【代碼說明】 3行代碼,訪問字串第5個字元的值。輸出結果為d

通過切片可以實現對字串有規律的截取。切片的語法格式如下所示。


string[start : end : step]


【代碼說明】 其中string表示需要取子串的源字串變數。[start:end:step]表示從string的第start個索引位置開始到第end個索引之間截取子串,截取的步長是step。即每次截取字元string[start+step],直到第end個索引。索引從0開始計數。

下面這段代碼演示了使用切片截取子串的功能。


01      # 特殊切片截取子串

02      str1 = "hello world"

03      print (str1[0:3])

04      print (str1[::2])

05      print (str1[1::2])


【代碼說明】

·第3行代碼,截取字串中第1個字元到第3個字元之間的部分。輸出結果為“wor”。

·第4行代碼,[::2]切片省略了開始和結束字元。從字串的第1個字元開始,以2為步長逐個截取字元。輸出結果為“hlowrd”。

·第5行代碼,切片中的數位1表示從字串的第2個字元開始取字元,數位2表示以2為步長逐個截取字元。輸出結果為“el ol”。

如果要同時截取多個子串,可以使用函數split()實現。函數split()的聲明如下所示。


split([char] [,num])


【代碼說明】

·參數char表示用於分割的字元,預設的分割字元是空格。

·參數num表示分割的次數。如果num等於2,將把源字串分割為3個子串。預設情況下,將根據字元char在字串中出現的個數來分割子串。

·函數的返回值是由子串組成的列表。

下面這段代碼演示了split()的使用。


01      # 使用split()獲取子串

02      sentence = "Bob said: 1, 2, 3, 4"

03      print ("使用空格取子串:", sentence.split())

04      print ("使用逗號取子串:", sentence.split(","))

05      print ("使用兩個逗號取子串:", sentence.split(",", 2))


【代碼說明】

·第3行代碼根據空格來獲取子串。字串sentence中有5個空格,將返回由6個子串組成的列表。輸出結果:


使用空格取子串: ['Bob', 'said:', '1,', '2,', '3,', '4']


·第4行代碼根據逗號來獲取子串。字串sentence中有3個空格,將返回由4個子串組成的列表。輸出結果:


使用逗號取子串: ['Bob said: 1', ' 2', ' 3', ' 4']


·第5行代碼根據逗號來分割字串,並把字串sentence分割為3個子串。輸出結果:


使用兩個逗號取子串: ['Bob said: 1', ' 2', ' 3, 4']


字元串連接後,Python將分配新的空間給連接後的字串,源字串保持不變。


01      str1 = "a"

02      print (id(str1))

03      print (id(str1 + "b"))


【代碼說明】

·第2行代碼輸出str1的內部標識。輸出結果為“12144224”。

·第3行代碼,進行字元串連接,新的字串將獲得新的標識。輸出結果為“12486880”。

6.1.5 字串的比較

Java使用equals()比較兩個字串的內容,Python直接使用==”“!=操作符比較兩個字串的內容。如果比較的兩個變數的類型不相同,比較的內容也不相同。下面這段代碼演示了Python中字串的比較。


01      # 字串的比較

02      str1 = 1

03      str2 = "1"

04      if str1 == str2:

05          print ("相同")

06      else:

07          print ("不相同")

08      if str(str1) == str2:

09          print ("相同")

10      else:

11          print ("不相同")


【代碼說明】

·第2行代碼定義了1個數位類型的變數str1

·第3行代碼定義了1個字串類型的變數str2

·第4行代碼比較str1str2的值。由於str1str2的類型不同,所以兩者的內容也不相同。輸出結果為“不相同”。

·第8行代碼,把數字型的變數str1轉換為字串類型,數位1被轉換為字串“1”。然後再與str2進行比較。輸出結果為“相同”。

如果要比較字串中的一部分內容,可以先截取子串,再使用==操作符進行比較。如果要比較字串的開頭或結尾部分,更方便的方法是使用startswith()endswith()函數。startswith()的聲明如下所示。


startswith(substring, [,start [,end]])


【代碼說明】

·參數substring是與源字串開頭部分比較的子串。

·參數start表示開始比較的位置。

·參數end表示比較結束的位置,即在start:end範圍內搜索子串substring

·如果字串以substring開頭,則返回True;否則,返回False

endswith()的參數和返回值類似startswith(),不同的是endswith()從源字串的尾部開始搜索。下面這段代碼演示了startswith()endswith()的使用。


01      # 比較字串的開始和結束處

02      word = "hello world"

03      print ("hello" == word[0:5])

04      print (word.startswith("hello"))

05      print (word.endswith("ld", 6))

06      print (word.endswith("ld", 6, 10))

07      print (word.endswith("ld", 6, len(word)))


【代碼說明】

·第3行代碼先獲取子串[0:5],再與“hello”進行比較。輸出結果為“True”。

·第4行代碼調用startswith()。比較字串變數word的開頭部分“hello”。輸出結果為“True”。

·第5行代碼,從字串變數word的結尾到word[6]之間搜索子串“ld”。輸出結果為“True”。

·第6行代碼,從“分片”word[6:10]中搜索子串“ld”。由於搜索的字元不包括位置10所在的字元,所以在word[6:10]中搜索不到子串“ld”。輸出結果為“False”。

·第7行代碼,從“分片”word[6:len(word)]中搜索子串“ld”,len(word)的值為11。輸出結果為“True”。

注意  startswith()endswith()相當於分片[0:n]n是源字串中最後一個索引。startswith()endswith()不能用於比較源字串中任意一部分的子串。

6.1.6 字串的反轉

字串反轉是指把字串中最後一個字元移到字串第一個位置,按照倒序的方式依次前移。Java中使用StringBuffer類處理字串,並通過迴圈對字串進行反轉。例如,下面這段Java代碼實現了一個字串反轉的函數。


01      //Java實現字串反轉

02      public static String reverse(String s)

03      {

04          int length = s.length();

05          StringBuffer result=new StringBuffer(length);

06          for(int i = length - 1;i >= 0;i--)

07              result.append(s.charAt(i));

08          return result.toString();

09      }


Python沒有提供對字串進行反轉的函數,也沒有類似charAt()這樣的函數。但是可以使用清單和字串索引來實現字串的反轉,並通過range()進行迴圈。


01      # 迴圈輸出反轉的字串

02      def reverse(s):

03          out = ""

04          li = list(s)

05          for i in range(len(li), 0, -1):

06              out += "".join(li[i-1])

07          return out


【代碼說明】

·第2行代碼定義了一個函數reverse(),參數s表示需要反轉的字串。

·第3行代碼定義了一個返回變數out,用於存放字串反轉後的結果。

·第4行代碼創建了一個清單li,字串s中的字元成為清單li的元素。

·第5~6行代碼從清單中的最後一個元素開始處理,依次連接到變數out中。

·第7行代碼返回變數out的值。

函數reverse()的調用形式如下所示。


print (reverse("hello"))


【代碼說明】 輸出reverse()的返回值,輸出結果為olleh

不難發現,Python的實現代碼更簡短,而且更容易理解。使用者還可以通過清單的reverse()函數實現字串的反轉,實現的代碼將進一步簡化。


01      # 使用listreverse()

02      def reverse(s):

03          li = list(s)

04          li.reverse()

05          s = "".join(li)

06          return s


【代碼說明】

·第4行代碼調用列表的reverse()實現了forin…迴圈的功能。

·第5行代碼調用join()把反轉後清單li的元素依次連接到變數s中。

Python的清單是對字串進行處理的常用方式,靈活使用清單等內置資料結構處理字串,能夠簡化程式設計的複雜度。利用序列的切片實現字串的反轉最為簡潔,reverse()函數的主體只需要一行代碼即可。


01      def reverse(s):

02          return s[::-1]


【代碼說明】 -1表示從字串最後一個索引開始倒序排列。

6.1.7 字串的查找和替換

Java中字串的查找使用函數indexOf(),返回源字串中第1次出現目標子串的索引。如果需要從右往左查找可以使用函數lastIndexOf()Python也提供了類似功能的函數,函數find()indexOf()的作用相同,rfind()lastIndexOf()的作用相同。find()的聲明如下所示。


find(substring [, start [ ,end]])


【代碼說明】

·參數substring表示待查找的子串。

·參數start表示開始搜索的索引位置。

·參數end表示結束搜索的索引位置,即在分片[start:end]中查找。

·如果找到字串substring,則返回substring在源字串中第1次出現的索引。否則,返回-1

rfind()的參數與find()相同,不同的是rfind()從字串的尾部開始查找子串。下面這段代碼演示了find()rfind()的使用。


01      # 查找字串

02      sentence = "This is a apple."

03      print (sentence.find("a"))

04      sentence = "This is a apple."

05      print (sentence.rfind("a"))


【代碼說明】

·第3行代碼使用函數find(),從sentence的頭部開始查找字串“a”。輸出結果為“8”。

·第5行代碼使用函數rfind(),從sentence的尾部開始查找字串“a”。輸出結果為“10”。

Java使用replaceFirst()replaceAll()實現字串的替換。replaceFirst()用於替換源字串中第1次出現的子串,replaceAll()用於替換源字串中所有出現的子串。這兩個函數通過規則運算式來查找子串。而Python使用函數replace()實現字串的替換,該函數可以指定替換的次數,相當於Java函數replaceFirst()replaceAll()的合併。但是replace()不支援規則運算式的語法。replace()的聲明如下所示。


replace(old, new [, max])


【代碼說明】

·參數old表示將被替換的字串。

·參數new表示替換old的字串。

·參數max表示使用new替換old的次數。

·函數返回一個新的字串。如果子串old不在源字串中,則函數返回源字串的值。

下面這段代碼演示了replace()的使用。


01      # 字串的替換

02      sentence = "hello world, hello China"

03      print (sentence.replace("hello", "hi"))

04      print (sentence.replace("hello", "hi", 1))

05      print (sentence.replace("abc", "hi"))


【代碼說明】

·第3行代碼把sentence中的“hello”替換為“hi”。由於沒有給出參數max的值,所以sentence中的“hello”都將被“hi”替換。輸出結果:


hi world, hi China


·第4行代碼,參數max的值為1,所以sentence中第1次出現的“hello”被“hi”替換,後面的出現的子串“hello”保持不變。輸出結果:


hi world, hello China


·第5行代碼,由於sentence中沒有子串“abc”,所以替換失敗。replace()返回sentence的值。輸出結果:


hello world, hello China


注意  replace()先創建變數sentence的拷貝,然後在拷貝中替換字串,並不會改變變數sentence的內容。

6.1.8 字串與日期的轉換

在開發中,經常把日期類型轉換為字串類型使用。字串與日期的轉換是工作中頻繁遇到的問題。Java提供了SimpleDateFormat類實現日期到字串的轉換。Python提供了time模組處理日期和時間。函數strftime()可以實現從時間到字串的轉換。strftime()的聲明如下所示。


strftime(format[, tuple]) -> string


【代碼說明】

·參數format表示格式化日期的特殊字元。例如,“%Y-%m-%d”相當於Java中的“yyyy-MM-dd”。

·參數tuple表示需要轉換的時間,用元組存儲。元組中的元素分別表示年、月、日、時、分、秒。

·函數返回一個表示時間的字串。

參數format格式化日期的常用標記如表6-3所示。

6-3 格式化日期的常用標記



字串到時間的轉換需要進行兩次轉換,需要使用time模組和datetime類,轉換過程分為如下3個步驟。

1)調用函數strptime()把字串轉換為一個的元組,進行第1次轉換。strptime()的聲明如下所示。


strptime(string, format) -> struct_time


【代碼說明】

·參數string表示需要轉換的字串。

·參數format表示日期時間的輸出格式。

·函數返回一個存放時間的元組。

2)把表示時間的元組賦值給表示年、月、日的3個變數。

3)把表示年、月、日的3個變數傳遞給函數datetime(),進行第2次轉換。datetime類的datetime()函數如下所示。


datetime(year, month, day[, hour[, minute[, second[, microsecond[,tzinfo]]]]])


【代碼說明】

·參數yearmonthday分別表示年、月、日,這3個參數必不可少。

·函數返回1datetime類型的變數。

下面這段代碼演示了時間到字串、字串到時間的轉換過程。


01      import time,datetime

02     

03      # 時間到字串的轉換

04      print (time.strftime("%Y-%m-%d %X", time.localtime()))

05      # 字串到時間的轉換

06      t = time.strptime("2008-08-08", "%Y-%m-%d")

07      y, m, d = t[0:3]

08      print (datetime.datetime(y, m, d))


【代碼說明】

·第4行代碼中,函數localtime()返回當前的時間,strftime把當前的時間格式轉化為字串類型。輸出結果:


2008-02-14 13:52:11


·第6行代碼,把字串“2008-08-08”轉換為一個元組返回。

·第7行代碼,把元組中前3個表示年、月、日的元素賦值給3個變數。

·第8行代碼,調用datetime()返回時間類型。輸出結果:


2008-08-08 00:00:00


注意  格式化日期的特殊標記是區分大小寫的,%Y%y不相同。

 

6.2 規則運算式應

規則運算式用於搜索、替換和解析字串。規則運算式遵循一定的語法規則,使用非常靈活,功能強大。使用規則運算式編寫一些邏輯驗證非常方便,例如電子郵寄地址格式的驗證。Python提供了re模組實現規則運算式的驗證。

6.2.1 規則運算式簡介

規則運算式是用於文本匹配的工具,它在源字串中查找與給定的規則運算式相匹配的部分。一個規則運算式是由字母、數位和特殊字元(括弧、星號、問號等)組成。規則運算式中有許多特殊的字元,這些特殊字元是構成規則運算式的要素。表6-4說明了規則運算式中特殊字元的含義。

6-4 規則運算式中的特殊字元


其中,匹配符[]可以指定一個匹配範圍,例如[ok]將匹配包含ok的字元。同時[]可以與\w\s\d等標記等價。例如,[0-9a-zA-Z_]等價於\w[^0-9]等價於\D

注意  ^[^m]中的“^”的含義並不相同,後者的“^”表示“除了……”的意思。

如果要匹配電話號碼,需要形如\d\d\d\d-\d\d\d\d\d\d\d這樣的規則運算式。其中出現了11\d,表達方式煩瑣。而且某些地區的電話號碼是8位元數位,區號也有可能是3位元或4位元數字,因此這個規則運算式就不能滿足要求了。規則運算式作為一門小型的語言,還提供了對運算式的一部分進行重複處理的功能。例如,*可以對規則運算式的某個部分重複匹配多次。這種匹配符號稱為限定詞。表6-5列出了規則運算式中常用的限定詞。

6-5 規則運算式中的常用限定詞


利用{}可以控制字元重複的次數。例如,\d{1,4}表示1~3位元數字。前面提到的電話號碼,可以採用如下的規則運算式。


\d{3}-\d{8} | \d{4}-\d{7}


【代碼說明】 該運算式匹配區號為3位的8位數電話號碼或區號為4位的7位數電話號碼。

在書寫電話號碼時,區號和本地號碼之間採用-間隔,也有的在區號兩側加圓括號分隔。這個需求也可以使用規則運算式實現。


[\( ]?\d{3}[\]-)?\d{8}|[\( ]?\d{4}[\]-]?\d{7}


【代碼說明】 [\(-]?表示最多只能取(-其中之一,因此該運算式支持-()兩種分隔區號的方式,同時也支援把區號和本地電話號碼連在一起書寫,例如:010-1234567801012345678(010)12345678

注意  “(”和“)”是規則運算式中的特殊字元,如果要把它們作為普通字元處理,需要在前面添加轉義字元“\”。

如果要對規則運算式進行嵌套,就需要使用分組()。例如,對3位元數位重複3次,可以使用如下的規則運算式表示。


(\d\d\d){2}


如果對字串123456789進行匹配,則匹配到子串123456。如果規則運算式寫為如下的形式,將匹配到不同的結果。


\d\d\d{2}


該運算式相當於\d\d\d\d,匹配的結果為12345678

規則運算式的每個分組會自動擁有一個組號。從左往右第1個出現的圓括號為第1個分組,表示為\1;第2個出現的圓括號為第2個分組,表示為\2,依次類推。組號可以用於重複匹配某個分組。例如,對字串abc重複兩次可以表示為如下的規則運算式。


(abc)\1


如果對字串abcabcab進行匹配,則匹配的結果為abcabc

預設情況下,規則運算式將匹配最長的字串作為結果。可以通過在限定詞後面添加的方式,獲取最短匹配的結果。例如,對字元a到字元c之間的字元進行匹配。


a.*c


如果對字串abcabc進行匹配,則匹配結果為源字串abcabc,即最長匹配。如果把規則運算式改為如下的方式,則將進行最短匹配。


a.*?c


*後添加了一個?,匹配結果為abcabc。當匹配到第1個字元c後,匹配立刻中斷,並返回匹配結果。然後繼續進行新的匹配,即返回了第2abc。限定詞與?組合還有一些其他的用法,如表6-6所示。

6-6 限定詞與?的組合


注意  表中的(?P<name>)(?P=name)Python中的寫法,其他的符號在各種程式設計語言中都是通用的。

6.2.2 使用re模組處理規則運算式

Pythonre模組具有規則運算式匹配的功能。re模組提供了一些根據規則運算式進行查找、替換、分隔字串的函數,這些函數使用一個規則運算式作為第一個參數。re模組常用的函數如表6-7所示。

6-7 re模組的常用函數


注意  函數match()必須從字串的第0個索引位置處開始搜索。如果第0個索引位置的字元不匹配,match()的匹配失敗。

re模組的一些函數中都有一個flags參數,該參數用於設置匹配的附加選項。例如,是否忽略大小寫、是否支持多行匹配等。表6-8列出了re模組的規則選項。

6-8 re模組的規則選項



re模組定義了一些常量來表示這些選項,使用前置字元re.加選項的簡寫或名稱的方式表示某個常量。例如,re.Ire.IGNORECASE表示忽略大小寫。

規則運算式中有3種間隔符號:^$\b^匹配字串首部的子串,$匹配結束部分的子串,而\b用於分隔單詞。下面這段代碼展示了這些間隔符在Python中的使用。


01      import re

02      # ^$的使用

03      s = "HELLO WORLD"

04      print (re.findall(r"^hello", s)) 

05      print (re.findall(r"^hello", s, re.I)) 

06      print (re.findall("WORLD$", s))

07      print (re.findall(r"wORld$", s, re.I))

08      print (re.findall(r"\b\w+\b", s))


【代碼說明】

·第4行代碼匹配以“hello”開始的字串。由於變數s中的“HELLO”採用的是大寫,所有匹配失敗。輸出結果為“[]”。

·第5行代碼添加了輔助參數flagsre.I表示匹配時忽略大小寫。輸出結果為“['HELLO']”。

·第6行代碼匹配以“WORLD”結尾的字串。輸出結果為“['WORLD']”。

·第7行代碼匹配以“WORLD”結尾的字串,並忽略大小寫。輸出結果為“['WORLD']”。

·第8行代碼匹配每個英文單詞。輸出結果為“['HELLO','WORLD']”。

前面介紹了replace()實現字串的替換,同樣可以使用re模組的sub()實現替換的功能。下面這段代碼演示了sub()替換字串的功能。


01      import re

02     

03      s = "hello world"

04      print (re.sub("hello", "hi", s))                    

05      print (re.sub("hello", "hi", s[-4:]))

06      print (re.sub("world", "China", s[-5:]))


【代碼說明】

·第4行代碼的輸出結果為“hi world”。

·第5行代碼在分片s[-4:]範圍內替換“hello”,即在字串“orld”中替換“hello”。由於沒有找到匹配的子串,所有sub()返回s[-4:]。輸出結果為“orld”。

·第6行代碼在分片s[-5:]範圍內替換“world”,即把字串“world”替換為“China”。輸出結果為“China”。

注意  sub()先創建變數s的拷貝,然後在拷貝中替換字串,並不會改變變數s的內容。

subn()的功能與sub()相同,但是多返回1個值,即匹配後的替換次數。下面這段代碼演示了subn()對字串的替換以及規則運算式中特殊字元的使用。


01      import re

02      # 特殊字元的使用

03      s = "你好 WORLD2"

04      print ("匹配字母數位:" + re.sub(r"\w", "hi", s))

05      print ("替換次數:" + str(re.subn(r"\w", "hi", s)[1]))

06      print ("匹配非字母數位的字元:" + re.sub(r"\W", "hi", s))

07      print ("替換次數:" + str(re.subn(r"\W", "hi", s)[1]))

08      print ("匹配空白字元:" + re.sub(r"\s", "*", s))

09      print ("替換次數:" + str(re.subn(r"\s", "*", s)[1]))

10      print ("匹配非空白字元:" + re.sub(r"\S", "hi", s))

11      print ("替換次數:" + str(re.subn(r"\S", "hi", s) [1]))

12      print ("匹配數字:" + re.sub(r"\d", "2.0", s))

13      print ("替換次數:" + str(re.subn(r"\d", "2.0", s)[1]))

14      print ("匹配非數字:" + re.sub(r"\D", "hi", s))

15      print ("替換次數:" + str(re.subn(r"\D", "hi", s)[1]))

16      print ("匹配任意字元:" + re.sub(r".", "hi", s))

17      print ("替換次數:" + str(re.subn(r".", "hi", s)[1]))


【代碼說明】

·第4行代碼,“\w”並不能匹配漢字。輸出結果:


匹配非字母數位的字元:你好hihihihihihi


·第5行代碼輸出替換次數。替換次數存放在subn()返回元組的第2個元素中。字串“WORLD2”有6個字元,所以被替換為6個“hi”。輸出結果:


替換次數:6


·第6行代碼替換非字母、數位、底線的字元。輸出結果:


匹配非字母數位的字元:hihihihihiWORLD2


·第7行代碼,漢字“你好”和後面的空格被替換為5個“hi”,每個漢字占2個字元。輸出結果:


替換次數:5


·第8行代碼匹配空白字元,空格、定位字元等都屬於空白字元。輸出結果:


匹配空白字元:你好*WORLD2


·第9行代碼,由於只有一個空格,所以替換次數為1。輸出結果:


替換次數:1


·第10行代碼替換非空格字元。輸出結果:


匹配非空白字元:hihihihi hihihihihihi


·第11行代碼,除空格外字串共佔用了10個字元。輸出結果:


替換次數:10


·第12行代碼替換數字。輸出結果:


匹配數字:你好 WORLD2.0


·第13行代碼把數字2替換為“2.0”。輸出結果:


替換次數:1


·第14行代碼替換每個英文字元。輸出結果:


匹配非數字:hihihihihihihihihihi2


·第15行代碼替換了10個非數位字元。輸出結果:


替換次數:10


·第16行代碼,“.”替換任意字元。輸出結果:


匹配任意字元:hihihihihihihihihihihi


·第17行代碼,所有11個字元均被替換。輸出結果:


替換次數:11


前面提到了解析電話號碼的規則運算式,下面通過Python程式來實現電話號碼的匹配。


01      import re

02      # 限定詞的使用

03      tel1 = "0791-1234567"

04      print (re.findall(r"\d{3}-\d{8}|\d{4}-\d{7}", tel1))

05      tel2 = "010-12345678"

06      print (re.findall(r"\d{3}-\d{8}|\d{4}-\d{7}", tel2))

07      tel3 = "(010)12345678"

08      print (re.findall(r"[\( ]?\d{3}[\]-]?\d{8}|[\( ]?\d{4}[\]-]?\d{7}", tel3))


【代碼說明】

·第4行代碼匹配區號為3位的8位數電話號碼或區號為4位的7位數電話號碼。輸出結果為“['0791-1234567']”。

·第6行代碼匹配區號為3位的8位數電話號碼或區號為4位的7位數電話號碼,區號和電話號碼之間用“-”連接。輸出結果為“['010-12345678']”。

·第8行代碼匹配區號為3位的8位數電話號碼或區號為4位的7位數電話號碼,區號和電話號碼之間可以採用3種方式書寫。一是直接使用“-”連接,二是在區號兩側添加圓括號再連接電話號碼,三是區號和電話號碼連在一起書寫。輸出結果為“['(010)12345678']”。

規則運算式的解析非常費時。如果多次使用findall()的方式匹配字串,搜索效率可能比較低。如果多次使用同一規則匹配字串,可以使用compile()進行預編譯,compile函數返回1pattern物件。該物件擁有一系列方法用於查找、替換或擴充字元串,從而提高字串的匹配速度。表6-9列出了pattern物件的屬性和方法。

6-9 pattern物件的屬性和方法


下面這段代碼在1個字串中查找多個數位,使用compile()提高查找的效率。


01      import re

02      # compile()預編譯

03      s = "1abc23def45"

04      p = re.compile(r"\d+")

05      print (p.findall(s))

06      print (p.pattern)


【代碼說明】

·第4行代碼返回1個規則運算式物件p,匹配變數s中的數位。

·第5行代碼調用pfindall()方法,匹配的結果存放在列表中。輸出結果為“['1','23','45']”。

·第6行代碼輸出當前使用的規則運算式。輸出結果為“\d+”。

函數compile()通常與match()search()group()一起使用,對含有分組的規則運算式進行解析。規則運算式的分組從左往右開始計數,第1個出現的圓括號標記為第1組,依次類推。此外還有0號組,0號組用於存儲匹配整個規則運算式的結果。match()search()將返回1match物件,match物件提供了一系列的方法和屬性來管理匹配的結果。表6-10列出了match物件的方法和屬性。

6-10 match物件的方法和屬性


下面這段代碼演示了對規則運算式分組的解析。


01      import re

02      # 分組

03      p = re.compile(r"(abc)\1")

04      m = p.match("abcabcabc")

05      print (m.group(0))

06      print(m.group(1))

07      print (m.group())

08     

09      p = re.compile(r"(?P<one>abc)(?P=one)")

10      m = p.search("abcabcabc")

11      print (m.group("one"))

12      print (m.groupdict().keys())

13      print (m.groupdict().values())

14      print (m.re.pattern)


【代碼說明】

·第3行代碼定義了1個分組“(abc)”,在後面使用“\1”再次調用該分組。即compile()返回1個包含2個分組的規則運算式物件p

·第4行代碼,p.match()對字串“abcabcab”進行搜索,返回1match對象m

·第5行代碼調用match物件的group(0)方法,匹配0號組。輸出結果為“abcabc”。

·第6行代碼調用match物件的group(1)方法,匹配1號組。輸出結果為“abc”。

·第7行代碼,預設情況下,返回分組0的結果。輸出結果為“abcabc”。

·第9行代碼,給分組命名,“?P”中的“one”表示分組的名稱。“(?P=one)”調用分組“one”,相當於“\1”。

·第11行代碼輸出分組“one”的結果。輸出結果為“abc”。

·第12行代碼獲取規則運算式中分組的名稱。輸出結果為“['one']”。

·第13行代碼獲取規則運算式中分組的內容。輸出結果為“['abc']”。

·第14行代碼獲取當前使用的規則運算式。輸出結果為“(?Pabc)(?P=one)”。

如果使用match()匹配的源字串abcabcabc改為bcabcabc,則Python將提示如下錯誤。


AttributeError: 'NoneType' object has no attribute 'group'


這種情況可以用search()替換match()search()可以匹配出正確的結果。

 

6.3 小結

本章講解了Python中字串的操作,包括字串的格式化、合併、截取、比較、查找、替換等操作。對比了PythonJava對字串操作處理的異同,重點講解了字串的幾種截取方法、字串與日期類型之間的轉換、規則運算式的語法以及Pythonre模組。規則運算式是文本匹配的工具,通常用於對規則資料的驗證,例如電話號碼、電子郵寄地址等。re模組提供了sub()findall()search()complie()等函數對規則運算式進行解析,結合pattern物件、match物件可以更好地處理和控制規則運算式和匹配結果。

下一章將介紹檔的處理,包括檔的創建、讀寫、刪除等基本操作,並講解對目錄進行控制、遍歷等內容。檔和目錄的處理是系統開發的重要課程,也是開發中經常面對的內容。

 

6.4 習題

1.存在字串I,love,python,取出love並輸出。

2.存在字串aabbccddee,將dd替換為ff

3.存在字串ab2b3n5n2n67mm4n2,程式設計實現下面要求:

1)使用re取出字串中所有的數位,並組合成一個新的字串輸出。

2)統計字串中字母n出現的次數。

3)統計每個字元出現的次數,使用字典輸出,如{a:1,b:2}

 

 

0 留言:

發佈留言