Python:正規表示式

thoustree發表於2021-04-22

1. 正規表示式概述 

  正規表示式(簡稱為 regex)是一些由字元和特殊符號組成的字串, 描述了模式的重複或者表述多個字元。

  正規表示式能按照某種模式匹配一系列有相似特徵的字串。

  換句話說, 它們能夠匹配多個字串。

  不同語言的正規表示式有差異,本文敘述是Python的正規表示式。

  解釋程式碼大多摘自《Python程式設計快速上手  讓繁瑣工作自動化》

2. 正規表示式書寫

  正規表示式就是一個字串,與普通字串不同的是,正規表示式包含了0個或多個表示式符號以及特殊字元,詳見《Python核心程式設計》1.2節

# 正規表示式書寫

'hing'
'\wing'
'123456'
'\d\d\d\d\d\d'
'regex.py'
'.*\.py'

3. 建立正規表示式物件

  孤立的一個正規表示式並不能起到匹配字串的作用,要讓其能夠匹配目標字元,需要建立一個正規表示式物件。通常向compile()函式傳入一個原始字元形式的正規表示式,即 r'.....'

>>> # re模組的compile()函式將返回(建立)一個Regex模式物件
>>> import re
>>> phoneNumRegex = re.compile(r'\d\d\d-\d\d\d-\d\d\d\d')

4. 常用的正規表示式模式

4.1  括號分組

>>> Regex = re.compile(r'(\d\d\d)-(\d\d\d-\d\d\d\d)')
>>> mo = Regex.search('My number is 415-555-4242.')
>>> Regex = re.compile(r'(\d\d\d)-(\d\d\d-\d\d\d\d)') # 建立Regex物件
>>> mo = Regex.search('My number is 415-555-4242.')   # 返回Match物件
>>> mo.group()         # 呼叫Regex物件的group()方法將返回整個匹配文字
'415-555-4242'
>>> mo.group(1)
'415'
>>> mo.group(2)
'555-4242'
>>> mo.group(0)
'415-555-4242'
>>> mo.groups()
('415', '555-4242')
>>> a,b = mo.groups()   # groups()方法返回多個值得元組
>>> a
'415'
>>> b
'555-4242'
>>> 

4.2  用管道匹配多個分組

>>> heroRegex = re.compile (r'Batman|Tina Fey')
>>> mo1 = heroRegex.search('Batman and Tina Fey.')
>>> mo1.group()
'Batman'
>>> mo2 = heroRegex.search('Tina Fey and Batman.')
>>> mo2.group()
'Tina Fey

4.3  用問號實現可選匹配

>>> batRegex = re.compile(r'Bat(wo)?man')   # 如果'wo'沒有用括號括起來,則可選的字元將是Batwo
>>> mo1 = batRegex.search('The Adventures of Batman')
>>> mo1.group()
'Batman'
>>> mo2 = batRegex.search('The Adventures of Batwoman')
>>> mo2.group()
'Batwoman'

4.4 用星號匹配零次或多次

>>> batRegex = re.compile(r'Bat(wo)*man') # 如果要匹配'*'號則用\*
>>> mo1 = batRegex.search('The Adventures of Batman')
>>> mo1.group()
'Batman'
>>> mo2 = batRegex.search('The Adventures of Batwoman')
>>> mo2.group()
'Batwoman'
>>> mo3 = batRegex.search('The Adventures of Batwowowowoman')
>>> mo3.group()
'Batwowowowoman

4.5 用加號匹配一次或多次

>>> batRegex = re.compile(r'Bat(wo)+man')  # 如果要匹配+號用\+
>>> mo1 = batRegex.search('The Adventures of Batwoman')
>>> mo1.group()
'Batwoman'
>>> mo2 = batRegex.search('The Adventures of Batwowowowoman')
>>> mo2.group()
'Batwowowowoman'
>>> mo3 = batRegex.search('The Adventures of Batman')
>>> mo3 == None
True

4.6 用花括號匹配特定次數

  下面程式碼的 “?” 表示非貪心匹配。問號在正規表示式中可能有兩種含義: 宣告非貪心匹配或表示可選的分組。這兩種含義是完全無關的。

>>> greedyHaRegex = re.compile(r'(Ha){3,5}') # 若果要匹配{,則用\{
>>> mo1 = greedyHaRegex.search('HaHaHaHaHa')
>>> mo1.group()
'HaHaHaHaHa'
>>> nongreedyHaRegex = re.compile(r'(Ha){3,5}?')
>>> mo2 = nongreedyHaRegex.search('HaHaHaHaHa')
>>> mo2.group()
'HaHaHa'

5. 貪心和非貪心匹配

  利用非貪心匹配的目的往往在於不想讓萬用字元(.)連萬用字元之外的匹配字元也被匹配,程式碼如下。當然3.6也是非貪心匹配的一個例子

>>> nongreedyRegex = re.compile(r'<.*?>')
>>> mo = nongreedyRegex.search('<To serve man> for dinner.>')
>>> mo.group()
'<To serve man>'
>>> greedyRegex = re.compile(r'<.*>')
>>> mo = greedyRegex.search('<To serve man> for dinner.>')
>>> mo.group()
'<To serve man> for dinner.>'

6. Regex 物件常用方法

  如上所述,compile()函式建立了一個Regex物件,Regex物件常用方法如下

6.1 search(), group(), groups()

>> Regex = re.compile(r'(\d\d\d)-(\d\d\d-\d\d\d\d)')
>>> mo = Regex.search('My number is 415-555-4242.')
>>> Regex = re.compile(r'(\d\d\d)-(\d\d\d-\d\d\d\d)') # 建立Regex物件
>>> mo = Regex.search('My number is 415-555-4242.')   # 返回Match物件
>>> mo.group()         # 呼叫Regex物件的group()方法將返回整個匹配文字
'415-555-4242'
>>> mo.group(1)
'415'
>>> mo.group(2)
'555-4242'
>>> mo.group(0)
'415-555-4242'
>>> mo.groups()
('415', '555-4242')
>>> a,b = mo.groups()   # groups()方法返回多個值得元組
>>> a
'415'
>>> b
'555-4242'
>>> 

6.2 findall()

  如果呼叫在一個沒有分組的正規表示式上,findall()將返回一個匹配字串的列表。

  如果呼叫在一個有分組的正規表示式上,findall()將返回一個字串的元組的列表(每個分組對應一個字串)

>>> Regex = re.compile(r'\d\d\d-\d\d\d-\d\d\d\d') # has no groups
>>> Regex.findall('Cell: 415-555-9999 Work: 212-555-0000')
['415-555-9999', '212-555-0000']
>>> Regex = re.compile(r'(\d\d\d)-(\d\d\d)-(\d\d\d\d)') # has groups
>>> Regex.findall('Cell: 415-555-9999 Work: 212-555-0000')
[('415', '555', '1122'), ('212', '555', '0000')]

6.3 sub()

>>> namesRegex = re.compile(r'Agent \w+')
>>> namesRegex.sub('CENSORED', 'Agent Alice gave the secret documents to Agent Bob.')
'CENSORED gave the secret documents to CENSORED.'
>>> namesRegex = re.compile(r'Agent \w+')
>>> namesRegex.sub('CENSORED', 'Agent Alice gave the secret documents to Agent Bob.' , 1)  # 匹配1次
'CENSORED gave the secret documents to Agent Bob.'

7. re.IGNOREC ASE、 re.DOTALL 和 re.VERBOSE

  要讓正規表示式不區分大小寫,可以向 re.compile()傳入 re.IGNORECASE 或 re.I,作為第二個引數。

  通過傳入 re.DOTALL 作為 re.compile()的第二個引數, 可以讓句點字元匹配所有字元, 包括換行字元。

  要在多行正規表示式中新增註釋,則向 re.compile()傳入變數 re.VERBOSE, 作為第二個引數。

>>> someRegexValue = re.compile('foo', re.IGNORECASE | re.DOTALL | re.VERBOSE)

8. (?:…)

>>> re.findall(r'http://(?:\w+\.)*(\w+\.com)', 'http://google.com http://www.google.com http://code.google.com')
['google.com', 'google.com', 'google.com']
>>> 

9.程式碼實踐

# (檔案讀寫)瘋狂填詞2.py

'''
建立一個瘋狂填詞( Mad Libs)程式,它將讀入文字檔案, 並讓使用者在該文字檔案中出現 
ADJECTIVE、 NOUN、 ADVERB 或 VERB 等單詞的地方, 加上他們自己的文字。例如,一個文字檔案可能看起來像這樣:
The ADJECTIVE panda walked to the NOUN and then VERB. A nearby NOUN was
unaffected by these events.
程式將找到這些出現的單詞, 並提示使用者取代它們。
Enter an adjective:
silly
Enter a noun:
chandelier
Enter a verb:
screamed
Enter a noun:
pickup truck
以下的文字檔案將被建立:
The silly panda walked to the chandelier and then screamed. A nearby pickup truck was unaffected by these events.
結果應該列印到螢幕上, 並儲存為一個新的文字檔案。
'''


import re

def mad_libs(filename_path, save_path):
    with open(filename_path,'r') as strings: # 相對路徑下的文件
        words = strings.read()
    Regex = re.compile(r'\w[A-Z]+')   # \w :匹配1個任何字母、數字或下劃線
    finds = Regex.findall(words)
    for i in finds:
        replace = input('輸入你想替換 {} 的單詞:\n'.format(i)) 
        Regex2 = re.compile(i)
        words = Regex2.sub(replace,words,1) # 這個變數必須要是words與上面一致否則只列印最後替換的一個,可以畫棧堆圖跟蹤這個變數的值
    print(words)
    
    # strings.close()  不用這一行,with 上下文管理器會自動關閉

    with open(save_path,'a') as txt: 
        txt.write(words + '\n') #分行寫
        txt.close()
        
    # save_txt = open('儲存瘋狂填詞文件.txt','a')
    # save_txt.write(words)
    # save_txt.close()

if __name__ == '__main__': 
    filename_path = input('輸入要替換的txt文字路徑:')    # '瘋狂填詞原始文件.txt'
    save_path = input('輸入要儲存的檔案路徑(包含檔名稱):') # '儲存瘋狂填詞文件.txt'
    mad_libs(filename_path, save_path)
              

            
        

 

 

 

 

 

 

相關文章