python正則表達式詳解

  正則表達式是一個很強大的字符串處理工具,幾乎任何關於字符串的操做均可以使用正則表達式來完成,做爲一個爬蟲工做者,天天和字符串打交道,正則表達式更是不可或缺的技能,正則表達式的在不一樣的語言中使用方式可能不同,不過只要學會了任意一門語言的正則表達式用法,其餘語言中大部分也只是換了個函數的名稱而已,本質都是同樣的。下面,我來介紹一下python中的正則表達式是怎麼使用的。html

  首先,python中的正則表達式大體分爲如下幾部分:python

    1. 元字符
    2. 模式
    3. 函數
    4. re 內置對象用法
    5. 分組用法
    6. 環視用法

  全部關於正則表達式的操做都使用 python 標準庫中的 re 模塊。正則表達式

1、元字符 (參見 python 模塊 re 文檔)緩存

    • .                    匹配任意字符(不包括換行符)
    • ^                    匹配開始位置,多行模式下匹配每一行的開始
    • $                    匹配結束位置,多行模式下匹配每一行的結束
    • *                    匹配前一個元字符0到屢次
    • +                    匹配前一個元字符1到屢次
    • ?                    匹配前一個元字符0到1次
    • {m,n}                匹配前一個元字符m到n次
    • \\                   轉義字符,跟在其後的字符將失去做爲特殊元字符的含義,例如\\.只能匹配.,不能再匹配任意字符
    • []                   字符集,一個字符的集合,可匹配其中任意一個字符
    • |                    邏輯表達式 或 ,好比 a|b 表明可匹配 a 或者 b
    • (...)                分組,默認爲捕獲,即被分組的內容能夠被單獨取出,默認每一個分組有個索引,從 1 開始,按照"("的順序決定索引值
    • (?iLmsux)            分組中能夠設置模式,iLmsux之中的每一個字符表明一個模式,用法參見 模式 I
    • (?:...)              分組的不捕獲模式,計算索引時會跳過這個分組
    • (?P<name>...)        分組的命名模式,取此分組中的內容時可使用索引也可使用name
    • (?P=name)            分組的引用模式,可在同一個正則表達式用引用前面命名過的正則
    • (?#...)              註釋,不影響正則表達式其它部分,用法參見 模式 I
    • (?=...)              順序確定環視,表示所在位置右側可以匹配括號內正則
    • (?!...)              順序否認環視,表示所在位置右側不能匹配括號內正則
    • (?<=...)             逆序確定環視,表示所在位置左側可以匹配括號內正則
    • (?<!...)             逆序否認環視,表示所在位置左側不能匹配括號內正則
    • (?(id/name)yes|no)   若前面指定id或name的分區匹配成功則執行yes處的正則,不然執行no處的正則
    • \number              匹配和前面索引爲number的分組捕獲到的內容同樣的字符串
    • \A                   匹配字符串開始位置,忽略多行模式
    • \Z                   匹配字符串結束位置,忽略多行模式
    • \b                   匹配位於單詞開始或結束位置的空字符串
    • \B                   匹配不位於單詞開始或結束位置的空字符串
    • \d                   匹配一個數字, 至關於 [0-9]
    • \D                   匹配非數字,至關於 [^0-9]
    • \s                   匹配任意空白字符, 至關於 [ \t\n\r\f\v]
    • \S                   匹配非空白字符,至關於 [^ \t\n\r\f\v]
    • \w                   匹配數字、字母、下劃線中任意一個字符, 至關於 [a-zA-Z0-9_]
    • \W                   匹配非數字、字母、下劃線中的任意字符,至關於 [^a-zA-Z0-9_]

2、模式函數

    • I    IGNORECASE, 忽略大小寫的匹配模式, 樣例以下
      s = 'hello World!'
      
      regex = re.compile("hello world!", re.I)
      print regex.match(s).group()
      #output> 'Hello World!'
      
      #在正則表達式中指定模式以及註釋
      regex = re.compile("(?#註釋)(?i)hello world!")
      print regex.match(s).group()
      #output> 'Hello World!'
    • L    LOCALE, 字符集本地化。這個功能是爲了支持多語言版本的字符集使用環境的,好比在轉義符\w,在英文環境下,它表明[a-zA-Z0-9_],即因此英文字符和數字。若是在一個法語環境下使用,缺省設置下,不能匹配"é" 或   "ç"。加上這L選項和就能夠匹配了。不過這個對於中文環境彷佛沒有什麼用,它仍然不能匹配中文字符。
    • M    MULTILINE,多行模式, 改變 ^ 和 $ 的行爲
      s = '''first line second line third line'''
      
      # ^
      regex_start = re.compile("^\w+") print regex_start.findall(s) # output> ['first']
       regex_start_m = re.compile("^\w+", re.M) print regex_start_m.findall(s) # output> ['first', 'second', 'third']
      
      #$
      regex_end = re.compile("\w+$") print regex_end.findall(s) # output> ['line']
       regex_end_m = re.compile("\w+$", re.M) print regex_end_m.findall(s) # output> ['line', 'line', 'line']
    • S   DOTALL,此模式下 '.' 的匹配不受限制,可匹配任何字符,包括換行符
      s = '''first line second line third line'''
      # regex = re.compile(".+") print regex.findall(s) # output> ['first line', 'second line', 'third line']
      
      # re.S
      regex_dotall = re.compile(".+", re.S) print regex_dotall.findall(s) # output> ['first line\nsecond line\nthird line']
    • X    VERBOSE,冗餘模式, 此模式忽略正則表達式中的空白和#號的註釋,例如寫一個匹配郵箱的正則表達式
      email_regex = re.compile("[\w+\.]+@[a-zA-Z\d]+\.(com|cn)")
      
      email_regex = re.compile("""[\w+\.]+  # 匹配@符前的部分
                                  @  # @符
                                  [a-zA-Z\d]+  # 郵箱類別
                                  \.(com|cn)   # 郵箱後綴  """, re.X)

 

    • U    UNICODE,使用 \w, \W, \b, \B 這些元字符時將按照 UNICODE 定義的屬性.

正則表達式的模式是能夠同時使用多個的,在 python 裏面使用按位或運算符 | 同時添加多個模式工具

如 re.compile('', re.I|re.M|re.S)post

每一個模式在 re 模塊中其實就是不一樣的數字性能

print re.I # output> 2
print re.L # output> 4
print re.M # output> 8
print re.S # output> 16
print re.X # output> 64
print re.U # output> 32

、函數 (參見 python 模塊 re 文檔)學習

  python 的 re 模塊提供了不少方便的函數使你可使用正則表達式來操做字符串,每種函數都有它本身的特性和使用場景,熟悉以後對你的工做會有很大幫助優化

    • compile(pattern, flags=0)   

給定一個正則表達式 pattern,指定使用的模式 flags 默認爲0 即不使用任何模式,而後會返回一個 SRE_Pattern (參見 第四小節 re 內置對象用法) 對象

regex = re.compile(".+") print regex # output> <_sre.SRE_Pattern object at 0x00000000026BB0B8>

這個對象能夠調用其餘函數來完成匹配,通常來講推薦使用 compile 函數預編譯出一個正則模式以後再去使用,這樣在後面的代碼中能夠很方便的複用它,固然大部分函數也能夠不用 compile 直接使用,具體見 findall 函數

s = '''first line second line third line'''
# regex = re.compile(".+") # 調用 findall 函數 print regex.findall(s) # output> ['first line', 'second line', 'third line']
# 調用 search 函數
print regex.search(s).group() # output> first lin
    • escape(pattern)   

轉義 若是你須要操做的文本中含有正則的元字符,你在寫正則的時候須要將元字符加上反斜扛 \ 去匹配自身, 而當這樣的字符不少時,寫出來的正則表達式就看起來很亂並且寫起來也挺麻煩的,這個時候你可使用這個函數,用法以下

s = ".+\d123"
# regex_str = re.escape(".+\d123") # 查看轉義後的字符
print regex_str # output> \.\+\\d123

# 查看匹配到的結果
for g in re.findall(regex_str, s): print g # output> .+\d123
    • findall(pattern, string, flags=0)   

參數 pattern 爲正則表達式, string 爲待操做字符串, flags 爲所用模式,函數做用爲在待操做字符串中尋找全部匹配正則表達式的字串,返回一個列表,若是沒有匹配到任何子串,返回一個空列表。

s = '''first line second line third line'''

# compile 預編譯後使用 findall
regex = re.compile("\w+") print regex.findall(s) # output> ['first', 'line', 'second', 'line', 'third', 'line']

# 不使用 compile 直接使用 findall
print re.findall("\w+", s) # output> ['first', 'line', 'second', 'line', 'third', 'line']
    • finditer(pattern, string, flags=0)   

參數和做用與 findall 同樣,不一樣之處在於 findall 返回一個列表, finditer 返回一個迭代器(參見 http://www.cnblogs.com/huxi/archive/2011/07/01/2095931.html ), 並且迭代器每次返回的值並非字符串,而是一個 SRE_Match (參見 第四小節 re 內置對象用法) 對象,這個對象的具體用法見 match 函數。

s = '''first line second line third line''' regex = re.compile("\w+") print regex.finditer(s) # output> <callable-iterator object at 0x0000000001DF3B38>
for i in regex.finditer(s): print i # output> <_sre.SRE_Match object at 0x0000000002B7A920> # <_sre.SRE_Match object at 0x0000000002B7A8B8> # <_sre.SRE_Match object at 0x0000000002B7A920> # <_sre.SRE_Match object at 0x0000000002B7A8B8> # <_sre.SRE_Match object at 0x0000000002B7A920> # <_sre.SRE_Match object at 0x0000000002B7A8B8>
    • match(pattern, string, flags=0)   

使用指定正則去待操做字符串中尋找能夠匹配的子串, 返回匹配上的第一個字串,而且再也不繼續找,須要注意的是 match 函數是從字符串開始處開始查找的,若是開始處不匹配,則再也不繼續尋找,返回值爲 一個 SRE_Match (參見 第四小節 re 內置對象用法) 對象,找不到時返回 None

s = '''first line second line third line'''

# compile
regex = re.compile("\w+") m = regex.match(s) print m # output> <_sre.SRE_Match object at 0x0000000002BCA8B8>
print m.group() # output> first

# s 的開頭是 "f", 但正則中限制了開始爲 i 因此找不到
regex = re.compile("^i\w+") print regex.match(s) # output> None
    • purge()   

當你在程序中使用 re 模塊,不管是先使用 compile 仍是直接使用好比 findall 來使用正則表達式操做文本,re 模塊都會將正則表達式先編譯一下, 而且會將編譯事後的正則表達式放到緩存中,這樣下次使用一樣的正則表達式的時候就不須要再次編譯, 由於編譯實際上是很費時的,這樣能夠提高效率,而默認緩存的正則表達式的個數是 100, 當你須要頻繁使用少許正則表達式的時候,緩存能夠提高效率,而使用的正則表達式過多時,緩存帶來的優點就不明顯了 (參考 《python re.compile對性能的影響http://blog.trytofix.com/article/detail/13/), 這個函數的做用是清除緩存中的正則表達式,可能在你須要優化佔用內存的時候會用到。

    • search(pattern, string, flags=0)   

函數相似於 match,不一樣之處在於不限制正則表達式的開始匹配位置

s = '''first line second line third line'''

# 須要從開始處匹配 因此匹配不到 
print re.match('i\w+', s) # output> None

# 沒有限制起始匹配位置
print re.search('i\w+', s) # output> <_sre.SRE_Match object at 0x0000000002C6A920>

print re.search('i\w+', s).group() # output> irst
    • split(pattern, string, maxsplit=0, flags=0)   

參數 maxsplit 指定切分次數, 函數使用給定正則表達式尋找切分字符串位置,返回包含切分後子串的列表,若是匹配不到,則返回包含原字符串的一個列表

s = '''first 111 line second 222 line third 333 line'''

# 按照數字切分
print re.split('\d+', s) # output> ['first ', ' line\nsecond ', ' line\nthird ', ' line']

# \.+ 匹配不到 返回包含自身的列表
print re.split('\.+', s, 1) # output> ['first 111 line\nsecond 222 line\nthird 333 line']

# maxsplit 參數
print re.split('\d+', s, 1) # output> ['first ', ' line\nsecond 222 line\nthird 333 line']

 

    • sub(pattern, repl, string, count=0, flags=0)   

替換函數,將正則表達式 pattern 匹配到的字符串替換爲 repl 指定的字符串,  參數 count 用於指定最大替換次數

s = "the sum of 7 and 9 is [7+9]."

# 基本用法 將目標替換爲固定字符串
print re.sub('\[7\+9\]', '16', s) # output> the sum of 7 and 9 is 16.

# 高級用法 1 使用前面匹配的到的內容 \1 表明 pattern 中捕獲到的第一個分組的內容
print re.sub('\[(7)\+(9)\]', r'\2\1', s) # output> the sum of 7 and 9 is 97.


# 高級用法 2 使用函數型 repl 參數, 處理匹配到的 SRE_Match 對象
def replacement(m): p_str = m.group() if p_str == '7': return '77'
    if p_str == '9': return '99'
    return ''
print re.sub('\d', replacement, s) # output> the sum of 77 and 99 is [77+99].


# 高級用法 3 使用函數型 repl 參數, 處理匹配到的 SRE_Match 對象 增長做用域 自動計算
scope = {} example_string_1 = "the sum of 7 and 9 is [7+9]." example_string_2 = "[name = 'Mr.Gumby']Hello,[name]"

def replacement(m): code = m.group(1) st = ''
    try: st = str(eval(code, scope)) except SyntaxError: exec code in scope return st # 解析: code='7+9' # str(eval(code, scope))='16'
print re.sub('\[(.+?)\]', replacement, example_string_1) # output> the sum of 7 and 9 is 16.

# 兩次替換 # 解析1: code="name = 'Mr.Gumby'" # eval(code) # raise SyntaxError # exec code in scope # 在命名空間 scope 中將 "Mr.Gumby" 賦給了變量 name # 解析2: code="name" # eval(name) 返回變量 name 的值 Mr.Gumby print re.sub('\[(.+?)\]', replacement, example_string_2) # output> Hello,Mr.Gumby
    • subn(pattern, repl, string, count=0, flags=0)   

做用與函數 sub 同樣, 惟一不一樣之處在於返回值爲一個元組,第一個值爲替換後的字符串,第二個值爲發生替換的次數

    • template(pattern, flags=0)   

這個吧,咋一看和 compile 差很少,不過不支持 +、?、*、{} 等這樣的元字符,只要是須要有重複功能的元字符,就不支持,查了查資料,貌似沒人知道這個函數究竟是幹嗎的...

 

  4、re 內置對象用法

    • SRE_Pattern    這個對象是一個編譯後的正則表達式,編譯後不只可以複用和提高效率,同時也可以得到一些其餘的關於正則表達式的信息

屬性:

  • flags         編譯時指定的模式
  • groupindex    以正則表達式中有別名的組的別名爲鍵、以該組對應的編號爲值的字典,沒有別名的組不包含在內。
  • groups        正則表達式中分組的數量
  • pattern       編譯時用的正則表達式
    s = 'Hello, Mr.Gumby : 2016/10/26'
    p = re.compile('''(?:        # 構造一個不捕獲分組 用於使用 |
                  (?P<name>\w+\.\w+)    # 匹配 Mr.Gumby
                  |     # 或
                  (?P<no>\s+\.\w+) # 一個匹配不到的命名分組
                  )
                  .*? # 匹配  : 
                  (\d+) # 匹配 2016
                  ''', re.X)
    
    #
    print p.flags
    # output> 64
    print p.groupindex
    # output> {'name': 1, 'no': 2}
    print p.groups
    # output> 3
    print p.pattern
    # output> (?:        # 構造一個不捕獲分組 用於使用 |
    #              (?P<name>\w+\.\w+)    # 匹配 Mr.Gumby
    #              |     # 或
    #              (?P<no>\s+\.\w+) # 一個匹配不到的命名分組
    #              )
    #              .*? # 匹配  : 
    #              (\d+) # 匹配 2016

     

函數:可以使用 findall、finditer、match、search、split、sub、subn 等函數

 

    • SRE_Match    這個對象會保存本次匹配的結果,包含不少關於匹配過程以及匹配結果的信息

屬性:

  • endpos       本次搜索結束位置索引
  • lastgroup    本次搜索匹配到的最後一個分組的別名
  • lastindex    本次搜索匹配到的最後一個分組的索引
  • pos          本次搜索開始位置索引
  • re           本次搜索使用的 SRE_Pattern 對象
  • regs         列表,元素爲元組,包含本次搜索匹配到的全部分組的起止位置
  • string       本次搜索操做的字符串
    s = 'Hello, Mr.Gumby : 2016/10/26'
    m = re.search(', (?P<name>\w+\.\w+).*?(\d+)', s)
    # 本次搜索的結束位置索引
    print m.endpos
    # output> 28

    # 本次搜索匹配到的最後一個分組的別名
    # 本次匹配最後一個分組沒有別名
    print m.lastgroup
    # output> None

    # 本次搜索匹配到的最後一個分組的索引
    print m.lastindex
    # output> 2

    # 本次搜索開始位置索引
    print m.pos
    # output> 0

    # 本次搜索使用的 SRE_Pattern 對象
    print m.re
    # output> <_sre.SRE_Pattern object at 0x000000000277E158>

    # 列表,元素爲元組,包含本次搜索匹配到的全部分組的起止位置 第一個元組爲正則表達式匹配範圍
    print m.regs
    # output> ((7, 22), (7, 15), (18, 22))

    # 本次搜索操做的字符串
    print m.string
    # output> Hello, Mr.Gumby : 2016/10/26

     

函數:

  • end([group=0])               返回指定分組的結束位置,默認返回正則表達式所匹配到的最後一個字符的索引
  • expand(template)             根據模版返回相應的字符串,相似與 sub 函數裏面的 repl, 可以使用 \1 或者 \g<name> 來選擇分組
  • group([group1, ...])         根據提供的索引或名字返回響應分組的內容,默認返回 start() 到 end() 之間的字符串, 提供多個參數將返回一個元組
  • groupdict([default=None])    返回 返回一個包含全部匹配到的命名分組的字典,沒有命名的分組不包含在內,key 爲組名, value 爲匹配到的內容,參數 default 爲沒有參與本次匹配的命名分組提供默認值
  • groups([default=None])       以元組形式返回每個分組匹配到的字符串,包括沒有參與匹配的分組,其值爲 default
  • span([group])                返回指定分組的起止位置組成的元組,默認返回由 start() 和 end() 組成的元組
  • start([group])               返回指定分組的開始位置,默認返回正則表達式所匹配到的第一個字符的索引
    s = 'Hello, Mr.Gumby : 2016/10/26' m = re.search('''(?: # 構造一個不捕獲分組 用於使用 | (?P<name>\w+\.\w+) # 匹配 Mr.Gumby | # 或 (?P<no>\s+\.\w+) # 一個匹配不到的命名分組 ) .*? # 匹配 : (\d+) # 匹配 2016 ''', s, re.X) # 返回指定分組的結束位置,默認返回正則表達式所匹配到的最後一個字符的索引
    print m.end() # output> 22
    
    # 根據模版返回相應的字符串,相似與 sub 函數裏面的 repl, 可以使用 \1 或者 \g<name> 來選擇分組
    print m.expand("my name is \\1") # output> my name is Mr.Gumby
    
    # 根據提供的索引或名字返回響應分組的內容,默認返回 start() 到 end() 之間的字符串, 提供多個參數將返回一個元組
    print m.group() # output> Mr.Gumby : 2016
    print m.group(1,2) # output> ('Mr.Gumby', None)
    
    # 返回 返回一個包含全部匹配到的命名分組的字典,沒有命名的分組不包含在內,key 爲組名, value 爲匹配到的內容,參數 default 爲沒有參與本次匹配的命名分組提供默認值
    print m.groupdict('default_string') # output> {'name': 'Mr.Gumby', 'no': 'default_string'}
    
    # 以元組形式返回每個分組匹配到的字符串,包括沒有參與匹配的分組,其值爲 default
    print m.groups('default_string') # output> ('Mr.Gumby', 'default_string', '2016')
    
    # 返回指定分組的起止未知組成的元組,默認返回由 start() 和 end() 組成的元組
    print m.span(3) # output> (18, 22)
    
    # 返回指定分組的開始位置,默認返回正則表達式所匹配到的第一個字符的索引
    print m.start(3) # output> 18

5、分組用法

    python 的正則表達式中用小括號 "(" 表示分組,按照每一個分組中前半部分出現的順序 "(" 斷定分組的索引,索引從 1 開始,每一個分組在訪問的時候可使用索引,也可使用別名

s = 'Hello, Mr.Gumby : 2016/10/26'
p = re.compile("(?P<name>\w+\.\w+).*?(\d+)(?#comment)")
m = p.search(s)

# 使用別名訪問
print m.group('name')
# output> Mr.Gumby
# 使用分組訪問
print m.group(2)
# output> 2016

    有時候可能只是爲了把正則表達式分組,而不須要捕獲其中的內容,這時候可使用非捕獲分組

s = 'Hello, Mr.Gumby : 2016/10/26'
p = re.compile("""
                (?:  # 非捕獲分組標誌 用於使用 |
                    (?P<name>\w+\.\w+)
                    |
                    (\d+/)
                )
                """, re.X)
m = p.search(s)
# 使用非捕獲分組
# 此分組將不計入 SRE_Pattern 的 分組計數
print p.groups
# output> 2

# 不計入 SRE_Match 的分組
print m.groups()
# output> ('Mr.Gumby', None)

    若是你在寫正則的時候須要在正則裏面重複書寫某個表達式,那麼你可使用正則的引用分組功能,須要注意的是引用的不是前面分組的 正則表達式 而是捕獲到的 內容,而且引用的分組不算在分組總數中.

s = 'Hello, Mr.Gumby : 2016/2016/26'
p = re.compile("""
                (?:  # 非捕獲分組標誌 用於使用 |
                    (?P<name>\w+\.\w+)
                    |
                    (\d+/)
                )
                .*?(?P<number>\d+)/(?P=number)/
                """, re.X)
m = p.search(s)
# 使用引用分組
# 此分組將不計入 SRE_Pattern 的 分組計數
print p.groups
# output> 3

# 不計入 SRE_Match 的分組
print m.groups()
# output> ('Mr.Gumby', None, '2016')

# 查看匹配到的字符串
print m.group()
# output> Mr.Gumby : 2016/2016/

 

6、環視用法

環視還有其餘的名字,例如 界定、斷言、預搜索等,叫法不一。

環視是一種特殊的正則語法,它匹配的不是字符串,而是 位置,其實就是使用正則來講明這個位置的左右應該是什麼或者應該不是什麼,而後去尋找這個位置。

環視的語法有四種,見第一小節元字符,基本用法以下。

s = 'Hello, Mr.Gumby : 2016/10/26  Hello,r.Gumby : 2016/10/26'

# 不加環視限定
print re.compile("(?P<name>\w+\.\w+)").findall(s)
# output> ['Mr.Gumby', 'r.Gumby']

# 環視表達式所在位置 左邊爲 "Hello, "
print re.compile("(?<=Hello, )(?P<name>\w+\.\w+)").findall(s)
# output> ['Mr.Gumby']

# 環視表達式所在位置 左邊不爲 ","
print re.compile("(?<!,)(?P<name>\w+\.\w+)").findall(s)
# output> ['Mr.Gumby']

# 環視表達式所在位置 右邊爲 "M"
print re.compile("(?=M)(?P<name>\w+\.\w+)").findall(s)
# output> ['Mr.Gumby']

# 環視表達式所在位置 右邊不爲 r
print re.compile("(?!r)(?P<name>\w+\.\w+)").findall(s)
# output> ['Mr.Gumby']

高級一些的例子參見《正則基礎之——環視(Lookaround)》(http://www.cnblogs.com/kernel0815/p/3375249.html)

 

參考文章:

Python正則表達式指南》(http://www.cnblogs.com/huxi/archive/2010/07/04/1771073.html)

Python 正則式學習筆記 》(http://blog.csdn.net/whycadi/article/details/2011046)

相關文章
相關標籤/搜索