1. 程式人生 > >15-python基礎知識-正則表示式

15-python基礎知識-正則表示式

正則表示式

應用場景

  • 特定規律字串的查詢,切割、替換等
  • 特定格式(郵箱、手機號、IP、URL等)的校驗
  • 爬蟲專案中,提取特定內容

使用原則

  • 只要使用字串函式能夠解決的問題就不要使用正則
  • 正則的效率比較低,同時會降低程式碼的可讀性
  • 世界上最難理解的三樣東西:醫生的處方、道士的神符、碼農的正則
  • 提醒:正則是用來寫的,不是用來讀的;在不清楚功能的情況下,不要閱讀別人的正則

基本使用

  • 說明:正則的解析不是我們來做的,需要藉助re模組

  • 相關函式:

    • match:只從開頭進行匹配,匹配到就返回結果物件,沒有找到返回None
    • search:從任意位置匹配,功能同上,都是單次匹配(找到就停)
    m = re.search('abc', 'hadajabcadjlae')
    
    if m:
        # 返回匹配的內容
        print(m.group())
        # 返回匹配內容的位置
        print(m.span())
    
    • findall:全部匹配,返回所有匹配的到結果列表,沒有找到返回空列表
    f = re.findall('abcd', 'abcasjdlaabcaksjd;abcasdjla')
    print(f)
    
    • compile:建立正則表示式物件,可以讓建立正則物件和內容匹配分開操作
    # 可以先生成正則表示式物件,然後再進行匹配
    c = re.compile('cba')
    # print(type(c))
    
    # 從開頭匹配
    # m = c.match('cbasdhaj;acbaalsdk')
    # 從任意位置匹配
    m = c.search('casdhaj;acbaalsdk')
    
    if m:
        print(m.group())
    
    # 匹配所有
    f = c.findall('ahkjdcbasdkjalcbasakldjacba')
    print(f)
    

    此方式可以分開操作,比較靈活

正則語法

  • 單個字元

    普通字元:簡單理解就是一對一的完全匹配
    []:中間的任意一個字元
    	[abc]:abc的任意一個字元
    	[0-9]:任意的數字字元
    	[a-zA-Z]:任意的字母
    	[^0-9]:非數字字元
    . :除'\n'以外的任意字元
    \d:數字字元,等價於[0-9]
    \D:非數字字元,等價於[^0-9]
    \w:匹配字(數字、字母、下劃線)
    \W:匹配非字(\w相反的內容)
    \s:空白字元(\n、\r、\t、空格)
    \S:非空白字元(\s相反的內容)
    \b:詞邊界(開頭、結尾、空格、標點)
    \B:非詞邊界(\b相反的內容)
    
  • 次數限定:修飾前面的單個字元出現的次數

    *:任意次
    +:至少一次
    ?:至多一次
    {m}:指定m次
    {m,n}:m <= 次數 <=n
    {m,}:至少m次
    {,m}:至多m次
    
  • 邊界限定

    • ^:以指定的內容開頭
    • $:以指定的內容結尾
    • 示例:
    import re
    
    # 只從開頭匹配
    # f = re.findall('^hello', 'asjdhelloaskd')
    # 只從結尾匹配
    f = re.findall('world$', 'asjdhelloaskworld')
    # 同時限制開頭和結尾
    f = re.findall('^\w*$', 'asjdhelloaskworld')
    print(f)
    
  • 優先順序與整體

    import re
    
    # |:表示或,擁有最低的優先順序
    # ():可以表示一個整體
    s = re.search('hell(o|w)orld', 'akdhahellworld')
    
    if s:
        print(s.group())
    
  • 分組匹配

    • 示例1:
    import re
    
    c = re.compile('(\d+)([a-z]+)(\d+)')
    
    s = c.search('agaj2635sdhasda237adjsd')
    
    if s:
        # 0:表示完整匹配內容,之後的數字表示第幾組,也就是第幾個()匹配的內容
        print(s.group(0), s.span(0))
        print(s.group(1), s.span(1))
        print(s.group(2), s.span(2))
        print(s.group(3), s.span(3))
    
    • 示例2:
    import re
    
    # 固定匹配
    # c = re.compile('<div><a>\w+</a></div>')
    # 動態匹配:匹配兩次巢狀的標籤
    # c = re.compile('<[a-z]+><[a-z]+>\w+</[a-z]+></[a-z]+>')
    # 無名分組:\1、\2分別表示前面的第一組、第二組匹配的內容
    # c = re.compile(r'<([a-z]+)><([a-z]+)>\w+</\2></\1>')
    # 命名分組:給分組()起名字
    c = re.compile(r'<(?P<one>[a-z]+)><(?P<two>[a-z]+)>\w+</(?P=two)></(?P=one)>')
    
    s = c.search('<div><a>百度一下</a></div>')
    
    if s:
        print(s.group())
        # 返回所有組的資訊,是一個元組
        print(s.groups())
        # 返回分組的字典,鍵是組的名字,值時匹配的內容
        print(s.groupdict())
    
  • findall

    import re
    
    # 按照正則進行匹配,但是新增()後,結果只顯示()匹配的內容
    f = re.findall('A(abc)A', 'asdjAabcAasdjAabcAsdkabca')
    
    print(f)
    
  • 貪婪匹配

    • 貪婪:最大限度的匹配。正則的匹配預設是貪婪的
    • 非貪婪:只要滿足條件,能少匹配就少匹配。可以使用’?'取消貪婪
    • 示例:
    import re
    
    # .+?:取消至少一次的貪婪匹配
    # c = re.compile('a.+?b')
    
    # .*?:取消任意多次的貪婪匹配
    c = re.compile('a.*?b')
    
    s = c.search('sdhaasdajasksdbsdjbsdk')
    
    if s:
        print(s.group())
    
  • 匹配模式

    • 說明:匹配模式就是對預設的匹配原則進行整體的修飾
    • 示例:
    import re
    
    # re.I:表示忽略大小寫
    # s = re.search(r'hello', 'Hello', re.I)
    
    # re.M:多行處理,預設會把字元當做一行處理
    s = re.search(r'^hello', 'asdkasj\nhelloajsdhkas', re.M)
    
    # string = '<div>hello</div>'
    string = '''<div>
    hello
    </div>'''
    # re.S:是.可以匹配任意,作為單行處理(忽略\n)
    s = re.search(r'<div>.*?</div>', string, re.S)
    
    if s:
        print(s.group())
    
  • 字元轉義

    • 匹配凡是跟正則語法相關的字元都需要需要進轉義
    • 示例:
    import re
    
    # python    \\\d    \\\\d   r'\\d'
    # re        \\d     \\d     \\d
    # 實際        \d      \d      \d
    
    # 推薦使用,否則需要寫很多\
    c = re.compile(r'\\d')
    
    s = c.search('\d')
    
    if s:
        print(s.group())
     
    # 匹配'\b'字串
    c = re.compile(r'\b')
    
    # \b本身有特殊含義
    s = c.search(r'\b')
    if s:
    	print('成功', s.group())	   
    

    在定義字串的開頭新增’r’表示原始字串,可以輕鬆解決很多關於轉義的問題

  • 正則切割

    import re
    
    c = re.compile(r'\d')
    
    string = '正則其實不難1但是學完之後2發現什麼也不出來3是這樣吧'
    
    # 字串是固定切割,不能解決某類的匹配切割問題
    # print(string.split('1'))
    # 按照正則進行切割
    ret = c.split(string)
    print(ret)
    
    # 整體方案
    print(re.split(r'\d', string))
    
  • 正則替換

    string = 'helloadjasdhelloskdjalkhellosdjka'
    # 字串替換
    new_string = string.replace('hello', 'world', 2)
    print(new_string)
    
    import re
    
    s = 'how1are2you'
    # 正則替換
    # s2 = re.sub(r'\d', ' ', s)
    # 替換時可以傳遞一個函式,使用函式的返回值進行替換
    def double(s):
        return str(int(s.group()) * 2)
        # return 'xxx'
    # 使用專門的處理函式,可以認為的干預替換過程
    s2 = re.sub(r'\d', double, s)
    print(s2)