正則表達式和python的re模塊
9月 4 2014 更新日期:9月 4 2014
文章目錄- 1. 什麽是正則表達式
- 2. 元字符使用一覽表:
- 3. 字符轉義
- 4. 重復
- 5. 字符類
- 6. 分支條件
- 7. 分組
- 8. re模塊
- 8.0.1. compile
- 8.0.2. match和search
- 8.0.3. split
- 8.0.4. findall
- 8.0.5. finditer
- 8.0.6. sub
- 8.0.7. subn
什麽是正則表達式
在編寫處理字符串的程序或網頁時,經常會有查找符合某些復雜規則的字符串的需要。正則表達式就是用於描述這些規則的工具。正則表達式就是記錄文本規則的代碼,換句話說,正則表達式是一種文本模式,包括普通字符(例如,a 到 z 之間的字母)和特殊字符(稱為“元字符”)。模式描述在搜索文本時要匹配的一個或多個字符串。
字符是計算機軟件處理文字時最基本的單位,可能是字母,數字,標點符號,空格,換行符,漢字等等。字符串是0個或更多個字符的序列。文本也就是文字,字符串。說某個字符串匹配某個正則表達式,通常是指這個字符串裏有一部分(或幾部分分別)能滿足表達式給出的條件。
假設你要在英文小說中查找Hi,那麽使用的正則表達式就是Hi
,這個很簡單吧,不過,通常處理正則表達式的工具(例如後面會提到的python的re模塊)會提供忽略大小寫的選項。
不幸的是,很多單詞裏都包含了很多單詞裏包含hi這兩個連續的字符,比如him,history,high等等。用hi來查找的話,這裏邊的hi也會被找出來。如果要精確地查找hi這個單詞的話,我們應該使用\bhi\b
。
\b是正則表達式規定的一個特殊代碼(這裏稱為元字符,metacharacter),代表著單詞的開頭或結尾,也就是單詞的分界處。
假設你要找的Hi後面不遠處有一個ZH,那麽可以用\bhi\b.*\bZH\b
.
這裏,.是另一個元字符,匹配除了換行符以外的任意字符。*同樣是元字符,不過它代表的不是字符,也不是位置,而是數量——它指定*前邊的內容可以連續重復使用任意次以使整個表達式得到匹配,例如,zo* 能匹配 "z" 以及 "zoo"等 。
如果同時使用其它元字符,我們就能構造出功能更強大的正則表達式。比如下面這個例子:0\d\d-\d\d\d\d\d\d\d\d
匹配這樣的字符串:以0開頭,然後是兩個數字,然後是一個連字號“-”,最後是8個數字(也就是中國的電話號碼。當然,這個例子只能匹配區號為3位的情形)。
現在你已經知道幾個很有用的元字符了,如\b,.,*,還有\d.正則表達式裏還有更多的元字符,比如\s匹配任意的空白符,包括空格,制表符(Tab),換行符,中文全角空格等。\w匹配字母或數字或下劃線或漢字等。
下面來看看更多的例子:\ba\w*\b
匹配以字母a開頭的單詞——先是某個單詞開始處(\b
),然後是字母a,然後是任意數量的字母或數字(\w*
),最後是單詞結束處(\b
)。
好吧,現在我們說說正則表達式裏的單詞是什麽意思吧:就是不少於一個的連續的\w
。不錯,這與學習英文時要背的成千上萬個同名的東西的確關系不大 \d+
匹配1個或更多連續的數字。這裏的+
是和*
類似的元字符,不同的是*匹配重復任意次(可能是0次),而+
則匹配重復1次或更多次。\b\w{6}\b
匹配剛好6個字符的單詞。
元字符使用一覽表:
上面介紹了部分的metacharacter,下面給出元字符一覽表,使用時可以查找。
字符
說明
\
將下一字符標記為特殊字符、文本、反向引用或八進制轉義符。例如,“n”匹配字符“n”。“\n”匹配換行符。序列“\”匹配“\”,“(”匹配“(”。
^
匹配輸入字符串開始的位置。如果設置了 RegExp 對象的 Multiline 屬性,^ 還會與“\n”或“\r”之後的位置匹配。
$
匹配輸入字符串結尾的位置。如果設置了 RegExp 對象的 Multiline 屬性,$ 還會與“\n”或“\r”之前的位置匹配。
零次或多次匹配前面的字符或子表達式。例如,zo 匹配“z”和“zoo”。 等效於 {0,}。
+
一次或多次匹配前面的字符或子表達式。例如,“zo+”與“zo”和“zoo”匹配,但與“z”不匹配。+ 等效於 {1,}。
?
零次或一次匹配前面的字符或子表達式。例如,“do(es)?”匹配“do”或“does”中的“do”。? 等效於 {0,1}。
{n}
n 是非負整數。正好匹配 n 次。例如,“o{2}”與“Bob”中的“o”不匹配,但與“food”中的兩個“o”匹配。
{n,}
n 是非負整數。至少匹配 n 次。例如,“o{2,}”不匹配“Bob”中的“o”,而匹配“foooood”中的所有 o。“o{1,}”等效於“o+”。“o{0,}”等效於“o ”。
{n,m}
M 和 n 是非負整數,其中 n <= m。匹配至少 n 次,至多 m 次。例如,“o{1,3}”匹配“fooooood”中的頭三個 o。’o{0,1}’ 等效於 ‘o?’。註意:您不能將空格插入逗號和數字之間。
?
當此字符緊隨任何其他限定符(*、+、?、{n}、{n,}、{n,m})之後時,匹配模式是“非貪心的”。“非貪心的”模式匹配搜索到的、盡可能短的字符串,而默認的“貪心的”模式匹配搜索到的、盡可能長的字符串。例如,在字符串“oooo”中,“o+?”只匹配單個“o”,而“o+”匹配所有“o”。
.
匹配除“\n”之外的任何單個字符。若要匹配包括“\n”在內的任意字符,請使用諸如“[\s\S]”之類的模式。
(pattern)
匹配 pattern 並捕獲該匹配的子表達式。可以使用 $0…$9 屬性從結果“匹配”集合中檢索捕獲的匹配。若要匹配括號字符 ( ),請使用“(”或者“)”。
(?:pattern)
匹配 pattern 但不捕獲該匹配的子表達式,即它是一個非捕獲匹配,不存儲供以後使用的匹配。這對於用“or”字符 (|) 組合模式部件的情況很有用。例如,’industr(?:y|ies) 是比 ‘industry|industries’ 更經濟的表達式。
(?=pattern)
執行正向預測先行搜索的子表達式,該表達式匹配處於匹配 pattern 的字符串的起始點的字符串。它是一個非捕獲匹配,即不能捕獲供以後使用的匹配。例如,’Windows (?=95|98|NT|2000)’ 匹配“Windows 2000”中的“Windows”,但不匹配“Windows 3.1”中的“Windows”。預測先行不占用字符,即發生匹配後,下一匹配的搜索緊隨上一匹配之後,而不是在組成預測先行的字符後。
(?!pattern)
執行反向預測先行搜索的子表達式,該表達式匹配不處於匹配 pattern 的字符串的起始點的搜索字符串。它是一個非捕獲匹配,即不能捕獲供以後使用的匹配。例如,’Windows (?!95|98|NT|2000)’ 匹配“Windows 3.1”中的 “Windows”,但不匹配“Windows 2000”中的“Windows”。預測先行不占用字符,即發生匹配後,下一匹配的搜索緊隨上一匹配之後,而不是在組成預測先行的字符後。
x|y
匹配 x 或 y。例如,’z|food’ 匹配“z”或“food”。’(z|f)ood’ 匹配“zood”或“food”。
[xyz]
字符集。匹配包含的任一字符。例如,“[abc]”匹配“plain”中的“a”。
[^xyz]
反向字符集。匹配未包含的任何字符。例如,“[^abc]”匹配“plain”中的“p”。
[a-z]
字符範圍。匹配指定範圍內的任何字符。例如,“[a-z]”匹配“a”到“z”範圍內的任何小寫字母。
[^a-z]
反向範圍字符。匹配不在指定的範圍內的任何字符。例如,“[^a-z]”匹配任何不在“a”到“z”範圍內的任何字符。
\b
匹配一個字邊界,即字與空格間的位置。例如,“er\b”匹配“never”中的“er”,但不匹配“verb”中的“er”。
\B
非字邊界匹配。“er\B”匹配“verb”中的“er”,但不匹配“never”中的“er”。
\cx
匹配 x 指示的控制字符。例如,\cM 匹配 Control-M 或回車符。x 的值必須在 A-Z 或 a-z 之間。如果不是這樣,則假定 c 就是“c”字符本身。
\d
數字字符匹配。等效於 [0-9]。
\D
非數字字符匹配。等效於 [^0-9]。
\f
換頁符匹配。等效於 \x0c 和 \cL。
\n
換行符匹配。等效於 \x0a 和 \cJ。
\r
匹配一個回車符。等效於 \x0d 和 \cM。
\s
匹配任何空白字符,包括空格、制表符、換頁符等。與 [ \f\n\r\t\v] 等效。
\S
匹配任何非空白字符。與 [^ \f\n\r\t\v] 等效。
\t
制表符匹配。與 \x09 和 \cI 等效。
\v
垂直制表符匹配。與 \x0b 和 \cK 等效。
\w
匹配任何字類字符,包括下劃線。與“[A-Za-z0-9]”等效。
\W
與任何非單詞字符匹配。與“[^A-Za-z0-9]”等效。
\xn
匹配 n,此處的 n 是一個十六進制轉義碼。十六進制轉義碼必須正好是兩位數長。例如,“\x41”匹配“A”。“\x041”與“\x04”&“1”等效。允許在正則表達式中使用 ASCII 代碼。
\num
匹配 num,此處的 num 是一個正整數。到捕獲匹配的反向引用。例如,“(.)\1”匹配兩個連續的相同字符。
\n
標識一個八進制轉義碼或反向引用。如果 \n 前面至少有 n 個捕獲子表達式,那麽 n 是反向引用。否則,如果 n 是八進制數 (0-7),那麽 n 是八進制轉義碼。
\nm
標識一個八進制轉義碼或反向引用。如果 \nm 前面至少有 nm 個捕獲子表達式,那麽 nm 是反向引用。如果 \nm 前面至少有 n 個捕獲,則 n 是反向引用,後面跟有字符 m。如果兩種前面的情況都不存在,則
\nm 匹配八進制值 nm,其中 n 和 m 是八進制數字 (0-7)。
\nml
當 n 是八進制數 (0-3),m 和 l 是八進制數 (0-7) 時,匹配八進制轉義碼 nml。
\un
匹配 n,其中 n 是以四位十六進制數表示的 Unicode 字符。例如,\u00A9 匹配版權符號 (?)。
字符轉義
如果你想查找元字符本身的話,比如你查找.,或者,就出現了問題:你沒辦法指定它們,因為它們會被解釋成別的意思。這時你就得使用\來取消這些字符的特殊意義。因此,你應該使用.和\。當然,要查找\本身,你也得用\.
例如:deerchao\.net
匹配deerchao.net
,C:\\Windows
匹配C:\Windows
。
重復
正則表達式第一件能做的事是能夠匹配不定長的字符集,而這是其它能作用在字符串上的方法所不能做到的。 不過,如果那是正則表達式唯一的附加功能的話,那麽它們也就不那麽優秀了。它們的另一個功能就是你可以指定正則表達式的一部分的重復次數。
就像前面介紹的元字符 *
. *
並不匹配字母字符 “*”;相反,它指定前一個字符可以被匹配零次或更多次,而不是只有一次。
上面我們的元字符表把大部分元字符都說了,這裏我們抽取出重復限定符。
代碼/語法 說明 * 重復零次或更多次 + 重復一次或更多次 ? 重復零次或一次 {n} 重復n次 {n,} 重復n次或更多次 {n,m} 重復n到m次再舉個例子,ca?t 將匹配 “ct” (0 個 “a” 字符) 或 “cat” (1 個 “a”);
有了上面的這些限定元字符,可以很好得處理重復情況,只要運用得當。
字符類
要想查找數字,字母或數字,空白是很簡單的,因為已經有了對應這些字符集合的元字符,但是如果你想匹配沒有預定義元字符的字符集合(比如元音字母a,e,i,o,u),應該怎麽辦?
很簡單,你只需要在方括號裏列出它們就行了,像[aeiou]就匹配任何一個英文元音字母,[.?!]匹配標點符號(.或?或!)。
我們也可以輕松地指定一個字符範圍,像[0-9]代表的含意與\d就是完全一致的:一位數字;同理[a-z0-9A-Z_]也完全等同於\w(如果只考慮英文的話)。
下面是一個更復雜的表達式:(?0\d{2}[) -]?\d{8}。
“(”和“)”也是元字符,後面的分組節裏會提到,所以在這裏需要使用轉義。
這個表達式可以匹配幾種格式的電話號碼,像(010)88886666,或022-22334455,或02912345678等。我們對它進行一些分析吧:首先是一個轉義字符(,它能出現0次或1次(?),然後是一個0,後面跟著2個數字(\d{2}),然後是)或-或空格中的一個,它出現1次或不出現(?),最後是8個數字(\d{8})。
分支條件
不幸的是,剛才那個表達式也能匹配010)12345678或(022-87654321這樣的“不正確”的格式。要解決這個問題,我們需要用到分枝條件。正則表達式裏的分枝條件指的是有幾種規則,如果滿足其中任意一種規則都應該當成匹配,具體方法是用|把不同的規則分隔開。聽不明白?沒關系,看例子:
0\d{2}-\d{8}|0\d{3}-\d{7}
這個表達式能匹配兩種以連字號分隔的電話號碼:一種是三位區號,8位本地號(如010-12345678),一種是4位區號,7位本地號(0376-2233445)。
\(?0\d{2}\)?[- ]?\d{8}|0\d{2}[- ]?\d{8}
這個表達式匹配3位區號的電話號碼,其中區號可以用小括號括起來,也可以不用,區號與本地號間可以用連字號或空格間隔,也可以沒有間隔。你可以試試用分枝條件把這個表達式擴展成也支持4位區號的。
\d{5}-\d{4}|\d{5}
這個表達式用於匹配美國的郵政編碼。美國郵編的規則是5位數字,或者用連字號間隔的9位數字。之所以要給出這個例子是因為它能說明一個問題:使用分枝條件時,要註意各個條件的順序。如果你把它改成\d{5}|\d{5}-\d{4}
的話,那麽就只會匹配5位的郵編(以及9位郵編的前5位)。原因是匹配分枝條件時,將會從左到右地測試每個條件,如果滿足了某個分枝的話,就不會去再管其它的條件了。
分組
我們已經提到了怎麽重復單個字符(直接在字符後面加上限定符就行了);但如果想要重復多個字符又該怎麽辦?你可以用小括號來指定子表達式(也叫做分組),然後你就可以指定這個子表達式的重復次數了,你也可以對子表達式進行其它一些操作(後面會有介紹)。
(\d{1,3}.){3}\d{1,3}是一個簡單的IP地址匹配表達式。要理解這個表達式,請按下列順序分析它:\d{1,3}匹配1到3位的數字,(\d{1,3}.){3}匹配三位數字加上一個英文句號(這個整體也就是這個分組)重復3次,最後再加上一個一到三位的數字(\d{1,3})。
IP地址中每個數字都不能大於255. 經常有人問我, 01.02.03.04 這樣前面帶有0的數字, 是不是正確的IP地址呢? 答案是: 是的, IP 地址裏的數字可以包含有前導 0 (leading zeroes).
不幸的是,它也將匹配256.300.888.999這種不可能存在的IP地址。如果能使用算術比較的話,或許能簡單地解決這個問題,但是正則表達式中並不提供關於數學的任何功能,所以只能使用冗長的分組,選擇,字符類來描述一個正確的IP地址:((2[0-4]\d|25[0-5]|[01]?\d\d?).){3}(2[0-4]\d|25[0-5]|[01]?\d\d?)。
理解這個表達式的關鍵是理解2[0-4]\d|25[0-5]|[01]?\d\d?,這裏我就不細說了,你自己應該能分析得出來它的意義。
re模塊
Python中得正則表達式(regular expression)模塊,即re模塊,功能還是很強大的。在介紹re之前,先看下面這部分。
正則表達式使用反斜杠” \ “來代表特殊形式或用作轉義字符,這裏跟Python的語法沖突,因此,Python用” \\ “表示正則表達式中的” \ “,因為正則表達式中如果要匹配” \ “,需要用\來轉義,變成” \ “,而Python語法中又需要對字符串中每一個\進行轉義,所以就變成了” \\ “。
上面的寫法是不是覺得很麻煩,為了使正則表達式具有更好的可讀性,Python特別設計了原始字符串(raw string),需要提醒你的是,在寫文件路徑的時候就不要使用raw string了,這裏存在陷阱。raw string就是用’r’作為字符串的前綴,如 r”\n”:表示兩個字符”\”和”n”,而不是換行符了。Python中寫正則表達式時推薦使用這種形式。
下面來看一下re模塊的幾個函數:
compile
re.compile(strPattern[, flag]):
這個方法是Pattern類的工廠方法,用於將字符串形式的正則表達式編譯為Pattern對象。
第二個參數flag是匹配模式,取值可以使用按位或運算符’|’表示同時生效,比如re.I | re.M。
另外,你也可以在regex字符串中指定模式,
比如re.compile(‘pattern’, re.I | re.M)與re.compile(‘(?im)pattern’)是等價的。
可選值有:
re.I(IGNORECASE): 忽略大小寫(括號內是完整寫法,下同)
re.M(MULTILINE): 多行模式,改變'^'和'$'的行為(參見上圖) re.S(DOTALL): 點任意匹配模式,改變'.'的行為 re.L(LOCALE): 使預定字符類 \w \W \b \B \s \S 取決於當前區域設定 re.U(UNICODE): 使預定字符類 \w \W \b \B \s \S \d \D 取決於unicode定義的字符屬性 re.X(VERBOSE): 詳細模式。這個模式下正則表達式可以是多行,忽略空白字符,並可以加入註釋。
其實compile方法的作用,不是很明顯,因為下面的兩個得到的結果等價。調用compile後,返回RegexObject對象,可以用該對象調用macth()和search()等匹配方法。
import re pattern = r"hi"; string = "hi,jack"; prog = re.compile(pattern); result1 = prog.match(string); print "result1: ", result1.group(); result2 = re.match(pattern, string); print "result2: ",result2.group();輸出的結果是一樣的。這裏match下面會介紹,它是一個匹配的方法,group方法後面也會介紹,它這裏輸出的時匹配的內容。可以試驗一下,就明白他兩是等效的。
match和search
兩個方法都是進行匹配時調用的。但他們有不同之處。
Python提供了兩種不同的原始操作:match和search。match是從字符串的起點開始做匹配,而search(perl默認)是從字符串做任意匹配。
例子1
import re result1 = re.match("c","abcde"); if(result1): print("result1:"+result1.group()); else: print("nothing"); result2 = re.search("c","abcde"); if(result2): print("result2:"+result2.group());輸出:
nothing
result2:c例子2
import re result1 = re.match("a","abcde"); if(result1): print("result1:"+result1.group()); else: print("nothing"); result2 = re.search("a","abcde"); if(result2): print("result2:"+result2.group());輸出:
result1:a
result2:a例子3
match函數可以設置匹配開始的位置,下面分別從0,1,2位置開始匹配。當然也可以設置終止的位置,具體可以查API文檔。
import re pattern = re.compile("c"); result1 = pattern.match("abcde",0); if(result1): print("result1:"+result1.group()); else: print("result1: nothing"); result2 = pattern.match("abcde",1); if(result2): print("result2:"+result2.group()); else: print("result2: nothing"); result3 = pattern.match("abcde",2); if(result3): print("result3:"+result3.group()); else: print("result3: nothing");輸出:
result1: nothing
result2: nothing
result3:csplit
re.split(pattern, string, maxsplit=0)
通過正則表達式將字符串分離。如果用括號將正則表達式括起來,那麽匹配的字符串也會被列入到list中返回。maxsplit是分離的次數,maxsplit=1分離一次,默認為0,不限制次數。
看一下例子:
import re print re.split('\W+', 'Words, words, words.')輸出:
[‘Words’, ‘words’, ‘words’, ‘’]思考一下為什麽輸出會是這個? 查看一下
\W
的作用,註意W是大寫的。findall
re.findall(pattern, string, flags=0)
找到 RE 匹配的所有子串,並把它們作為一個列表返回。這個匹配是從左到右有序地返回。如果無匹配,返回空列表。
import re print re.findall("\d","1a2b3c4d");輸出:
[‘1’, ‘2’, ‘3’, ‘4’]finditer
re.finditer(pattern, string, flags=0)
找到 RE 匹配的所有子串,並把它們作為一個叠代器返回。這個匹配是從左到右有序地返回。如果無匹配,返回空列表。
import re it = re.finditer(r"\d+","123abc456efg789hij") for match in it: print match.group()輸出:
123
456
789sub
sub(pattern, repl, string, count=0, flags=0)
其用途是用來替換匹配成功的字串,被替換成repl。值得一提的時,這裏的repl不僅僅可以是字符串,也可以是方法。
首先看下字符串的時候,被匹配的字符串就會被替換成為repl。
import re print re.sub(r'\sAND\s', ' & ', 'Baked Beans And Spam', flags=re.IGNORECASE)輸出:
Baked Beans & Spam
可以使用\id或\g<id>、\g<name>引用分組
.當repl是方法的時候。這個方法應當只接受一個參數(Match對象),並返回一個字符串用於替換(返回的字符串中不能再引用分組)。
import re def dashrepl(matchobj): if matchobj.group(0) == '-': return ' ' else: return '-' print re.sub('-{1,2}', dashrepl, 'pro----gram-files') print re.sub('-{1,2}', dashrepl, 'pro----gram-files')輸出:
pro—gram filessubn
subn(repl, string[, count]) |re.sub(pattern, repl, string[, count]):
多返回 (sub(repl, string[, count]), 替換次數)。import re print re.subn(r'\sAND\s', ' & ', 'Baked Beans And Spam', flags=re.IGNORECASE)
參考資料
- 正則表達式30分鐘入門教程 (推薦)
- 正則表達式語法
- Python-re模塊
- 深入淺出之正則表達式 (推薦)
- 正則表達式re模塊詳解 (推薦)
- Python正則表達式操作指南 (推薦)
- python正則表達式教程
Tags: 計算機軟件 標點符號 參考資料 history 英文小說文章來源: