正則表達式和python的re模塊

分類:編程 時間:2017-03-08

正則表達式和python的re模塊

9月 4 2014 更新日期:9月 4 2014

文章目錄

  1. 1. 什麽是正則表達式
  2. 2. 元字符使用一覽表:
  3. 3. 字符轉義
  4. 4. 重復
  5. 5. 字符類
  6. 6. 分支條件
  7. 7. 分組
  8. 8. re模塊
    1. 8.0.1. compile
    2. 8.0.2. match和search
    3. 8.0.3. split
    4. 8.0.4. findall
    5. 8.0.5. finditer
    6. 8.0.6. sub
    7. 8.0.7. subn
  • 9. 參考資料
  • 什麽是正則表達式

    在編寫處理字符串的程序或網頁時,經常會有查找符合某些復雜規則的字符串的需要。正則表達式就是用於描述這些規則的工具。正則表達式就是記錄文本規則的代碼,換句話說,正則表達式是一種文本模式,包括普通字符(例如,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}

    Mn 是非負整數,其中 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

    匹配 xy。例如,’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),ml 是八進制數 (0-7) 時,匹配八進制轉義碼 nml

    \un

    匹配 n,其中 n 是以四位十六進制數表示的 Unicode 字符。例如,\u00A9 匹配版權符號 (?)。


    字符轉義

    如果你想查找元字符本身的話,比如你查找.,或者,就出現了問題:你沒辦法指定它們,因為它們會被解釋成別的意思。這時你就得使用\來取消這些字符的特殊意義。因此,你應該使用.和\。當然,要查找\本身,你也得用\.

    例如:deerchao\.net匹配deerchao.netC:\\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:c

    split

    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

    789

    sub

    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 files

    subn

    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)


    參考資料

    1. 正則表達式30分鐘入門教程 (推薦)
    2. 正則表達式語法
    3. Python-re模塊
    4. 深入淺出之正則表達式 (推薦)
    5. 正則表達式re模塊詳解 (推薦)
    6. Python正則表達式操作指南 (推薦)
    7. python正則表達式教程

    Tags: 計算機軟件 標點符號 參考資料 history 英文小說

    文章來源:


    ads
    ads

    相關文章
    ads

    相關文章

    ad