申明: 本站飛宇網 https://feiyetopro.blogspot.com/。自網路收集整理之書籍、文章、影音僅供預覽交流學習研究,其[書籍、文章、影音]情節內容, 評論屬其個人行為, 與本網站無關。版權歸原作者和出版社所有,請在下載 24 小時內刪除,不得用作商業用途;如果您喜歡其作品,請支持訂閱購買[正版]。謝謝!
本章將介紹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行代碼分別用變數str1、num的值替換%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行代碼,把變數str1、str2、str3的值連接起來,並把結果存放在變數result中。
·第7行代碼,使用運算子“+=”連接變數result和str4。
·第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行代碼比較str1和str2的值。由於str1和str2的類型不同,所以兩者的內容也不相同。輸出結果為“不相同”。
·第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
# 使用list的reverse()
02
def reverse(s):
03 li = list(s)
04 li.reverse()
05 s = "".join(li)
06 return s
【代碼說明】
·第4行代碼調用列表的reverse()實現了for…in…迴圈的功能。
·第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]]]]])
【代碼說明】
·參數year、month、day分別表示年、月、日,這3個參數必不可少。
·函數返回1個datetime類型的變數。
下面這段代碼演示了時間到字串、字串到時間的轉換過程。
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]”將匹配包含“o”或“k”的字元。同時“[]”可以與\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-12345678、01012345678、(010)12345678。
注意 “(”和“)”是規則運算式中的特殊字元,如果要把它們作為普通字元處理,需要在前面添加轉義字元“\”。
如果要對規則運算式進行嵌套,就需要使用分組“()”。例如,對3位元數位重複3次,可以使用如下的規則運算式表示。
(\d\d\d){2}
如果對字串“123456789”進行匹配,則匹配到子串“123456”。如果規則運算式寫為如下的形式,將匹配到不同的結果。
\d\d\d{2}
該運算式相當於“\d\d\d\d”,匹配的結果為“1234”和“5678”。
規則運算式的每個分組會自動擁有一個組號。從左往右第1個出現的圓括號為第1個分組,表示為“\1”;第2個出現的圓括號為第2個分組,表示為“\2”,依次類推。組號可以用於重複匹配某個分組。例如,對字串“abc”重複兩次可以表示為如下的規則運算式。
(abc)\1
如果對字串“abcabcab”進行匹配,則匹配的結果為“abcabc”。
預設情況下,規則運算式將匹配最長的字串作為結果。可以通過在限定詞後面添加“?”的方式,獲取最短匹配的結果。例如,對字元“a”到字元“c”之間的字元進行匹配。
a.*c
如果對字串“abcabc”進行匹配,則匹配結果為源字串“abcabc”,即最長匹配。如果把規則運算式改為如下的方式,則將進行最短匹配。
a.*?c
在“*”後添加了一個“?”,匹配結果為“abc”和“abc”。當匹配到第1個字元“c”後,匹配立刻中斷,並返回匹配結果。然後繼續進行新的匹配,即返回了第2個“abc”。限定詞與“?”組合還有一些其他的用法,如表6-6所示。
表6-6 限定詞與?的組合

注意 表中的(?P<name>…)和(?P=name)是Python中的寫法,其他的符號在各種程式設計語言中都是通用的。
6.2.2 使用re模組處理規則運算式
Python的re模組具有規則運算式匹配的功能。re模組提供了一些根據規則運算式進行查找、替換、分隔字串的函數,這些函數使用一個規則運算式作為第一個參數。re模組常用的函數如表6-7所示。
表6-7 re模組的常用函數

注意 函數match()必須從字串的第0個索引位置處開始搜索。如果第0個索引位置的字元不匹配,match()的匹配失敗。
re模組的一些函數中都有一個flags參數,該參數用於設置匹配的附加選項。例如,是否忽略大小寫、是否支持多行匹配等。表6-8列出了re模組的規則選項。
表6-8 re模組的規則選項


re模組定義了一些常量來表示這些選項,使用前置字元“re.”加選項的簡寫或名稱的方式表示某個常量。例如,re.I或re.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行代碼添加了輔助參數flags,re.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函數返回1個pattern物件。該物件擁有一系列方法用於查找、替換或擴充字元串,從而提高字串的匹配速度。表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行代碼調用p的findall()方法,匹配的結果存放在列表中。輸出結果為“['1','23','45']”。
·第6行代碼輸出當前使用的規則運算式。輸出結果為“\d+”。
函數compile()通常與match()、search()、group()一起使用,對含有分組的規則運算式進行解析。規則運算式的分組從左往右開始計數,第1個出現的圓括號標記為第1組,依次類推。此外還有0號組,0號組用於存儲匹配整個規則運算式的結果。match()和search()將返回1個match物件,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”進行搜索,返回1個match對象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中字串的操作,包括字串的格式化、合併、截取、比較、查找、替換等操作。對比了Python與Java對字串操作處理的異同,重點講解了字串的幾種截取方法、字串與日期類型之間的轉換、規則運算式的語法以及Python的re模組。規則運算式是文本匹配的工具,通常用於對規則資料的驗證,例如電話號碼、電子郵寄地址等。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 留言:
發佈留言