[Python學習筆記]正則表達式總結


常用縮寫字符及其含義表格查詢

縮寫字符分類 含義
\d 0-9的任意數字
\D 除0-9的數字以外的任何字符
\w 任何字母、數字或下划線字符(可以認為是匹配“單詞”字符)
\W 除字母、數字和下划線意外的任何字符
\s 空格、制表符或換行符(可以認為是匹配“空白”字符)
\S 除空格、制表符或換行符的任何字符
? 問號緊跟於分組后表示前面分組的內容是可有可無的
* 星號緊跟於分組后表示前面分組的內容匹配0次或多次
+ 加號緊跟於分組后表示前面分組的內容匹配至少1次
| 管道匹配多個分組
{} 花括號內加數字表示匹配特定字數
. 句點表示匹配任何字符(除了換行)

在Python中使用正則表達式的步驟

在Python中使用正則表達式主要有下面幾個步驟:

  1. import re導入正則表達式模塊;
  2. re.compile()函數創建一個Regex 對象;
  3. 向Regex 對象的search()findall()方法傳入目標字符串;
  4. 調用Matches 對象的group()方法,返回實際匹配文本的字符串。

Python正則表達式詳細說明

通過在字符串的第一個引號之前加r來向re.compile()傳遞原始字符串

Python中轉義字符使用倒斜杠"\"。假如我們需要打印出"\n",則需要輸入轉義字符\\,才能打印出一個倒斜杠。所以"\\n"表示一個倒斜杠加上一個小寫的n。但是,通過在字符串的第一個引號之前加上r,可以將該字符串標記為原始字符串,它不包括轉義字符。

利用括號分組

假如我們想要匹配下面文本中的電話號碼,並且將區號從中分離。則我們可以添加括號在正則表達式中創建“分組”:(\d\d\d)-(\d\d\d\d\d\d\d\d)。然后使用group()匹配對象方法,從第二個分組中獲取需要的文本。正則表達式字符串中的第一對括號是第一組,第二對括號是第二組。向group()匹配對象方法傳入整數1或2,就可以取得匹配文本的不同部分。向group()方法傳入0或不傳參數,將返回整個匹配的文本,groups() 方法可以一次獲取所有的分組。(注意groups()和group(0)的差異)例如:

>>> import re
>>> phoneNumRegex = re.compile(r"(\d\d\d)-(\d\d\d\d\d\d\d\d)")
>>> mo = phoneNumRegex.search("My number is 029-88888888. ")
>>> mo.group(1)
'029'
>>> mo.group(2)
'88888888'
>>> mo.group(0)
'029-88888888'
>>> mo.group()
'029-88888888'
>>> mo.groups()
('029', '88888888')

用管道匹配多個分組

字符“|”稱為“管道”。希望匹配許多表達式中的一個時,就可以使用它。例如,正則表達式r"Kitty|Tiny"將匹配“Kitty”或“Tiny”。如果二者都在文本中,則第一個出現的將作為Match對象返回。例如:

>>> nameRegex = re.compile(r"Kitty|Tiny")
>>> mo1 = nameRegex.search("Tiny and Rose and Kitty. ")
>>> mo1.group()
'Tiny'

假如我們希望匹配Batman、Batwoman、Batcar、Batmobile中的任意一個,因為這些單詞都是以Bat開始,所以如果只指定一次前綴就會很方便。例如:

>>> nameRegex_2 = re.compile(r"Bat(man|woman|car|mobile)")
>>> mo2 = nameRegex_2.search("Batt、Batmobile、Batwoman、Batcar、Batman")
>>> mo2.group()
'Batmobile'
>>> mo2.group(1)
'mobile'

問號緊跟於分組后表示前面分組的內容是可有可無的

例如在匹配電話號碼的例子中,假如遇到沒有區號的電話號碼,前面例子中的代碼是無法匹配的,我們可以通過在分組后加問號來解決。例如:

>>> phoneNumRegex_2 = re.compile(r"(\d\d\d-)?(\d\d\d\d\d\d\d\d)")
>>> mo = phoneNumRegex_2.search("My phone number is 12356897. ")
>>> mo.group()
'12356897'

星號緊跟於分組后表示前面分組的內容匹配0次或多次

簡單,不用解釋,例如:

>>> emRegex = re.compile(r"e(m)*")
>>> mo = emRegex.search("emmmmmmmmmmm fuck trump. ")
>>> mo.group()
'emmmmmmmmmmm'

加號緊跟於分組后表示前面分組的內容匹配至少1次

簡單,不用解釋,例如:

>>> emRegex = re.compile(r"e(m)+")
>>> mo = emRegex.search("e fuck trump. ")
>>> mo.group()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'NoneType' object has no attribute 'group'  # 沒找到
>>> mo = emRegex.search("em fuck trump. ")
>>> mo.group()
'em'

用花括號匹配特定次數

如果想要查找一個分組重復特定次數的文本,則在該分組后緊跟一個花括號,其中的數字表示重復的次數。例如:(ha){3}匹配字符串“hahaha”,(ha){1,3}匹配字符串“ha”、“haha”或“hahaha”。這個花括號里面的規則和數組切片的那個規則是一樣的,如{3,}將匹配3次及以上實例;{,5}將匹配0到5次實例。例如:

>>> haRegex = re.compile(r"(ha){1,}")
>>> mo = haRegex.search("hahahahahahahahahahah. ")
>>> mo.group()
'hahahahahahahahahaha'

.*來匹配除了換行符之外的任意多個字符

.可以匹配任意字符(除了換行符),*表示之前分組重復0及以上次數。則.*組合可以匹配任意字符串(除換行符)。

貪心和非貪心匹配

在前面的例子中,(ha){1,}可以匹配1個或其他數量個ha,但為什么會返回最長的。這是因為Python的正則表達式默認是貪心的,這表示在有二義的情況下,他們匹配最長的字符串。要想讓他返回最短的那個,即將他變成非貪心的,則需要在花括號后加一個問號,這里注意區別前述中分組后加問號是表示可有可無的意思。例如:

>>> haRegex = re.compile(r"(ha){1,}?")
>>> mo = haRegex.search("hahahahahahahahahahah. ")
>>> mo.group()
'ha'

search()方法返回第一個匹配到的對象,findall()方法返回所有匹配到的字符串

search()方法返回一個Match對象,這個對象擁有group()方法;findall()方法直接返回一個列表,而列表則沒有group()方法。如下:

>>> import re
>>> phoneNumRegex = re.compile(r"(\d\d\d-)?(\d\d\d\d\d\d\d\d)")
>>> mo = phoneNumRegex.search("Home: 029-88618871, Office: 010-36758954. ")
>>> mo
<_sre.SRE_Match object; span=(6, 18), match='029-88618871'>
>>> mo.group()
'029-88618871'
>>> mo_2 = phoneNumRegex.findall("Home: 029-88618871, Office: 010-36758954. ")
>>> mo_2
[('029-', '88618871'), ('010-', '36758954')]
>>> mo_2.group()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'list' object has no attribute 'group'

總結:

  1. 如果調用在一個沒有分組的正則表達式上,例如\d\d\d-\d\d\d\d\d\d\d\d,方法findall()將返回一個匹配字符串的列表,例如['029-88618871','010-36758954'];
  2. 如果調用在一個有分組的正則表達式上,例如(\d\d\d-)?(\d\d\d\d\d\d\d\d),方法findall()將返回一個字符串的元組的列表,每個分組對應元組中的一個字符串,例如:[('029-', '88618871'), ('010-', '36758954')]。

建立自己的字符分類

如果覺得想要匹配的字符由上面的縮寫(\s\d\w等)來表示太寬泛,可以用方括號定義自己的字符分類。如[abcdeABCDE]或[a-eA-E]將匹配“abcdeABCDE”中的任意字符一次。值得注意的是,方括號中普通的正則表達式符號不會被解釋,這就意味着在匹配"."時不需要輸入"\."等。

通過在左方括號后緊跟一個^字符,就可以得到“非字符類”。如[^a-eA-E]就匹配所有除了“abcdeABCDE”以外的任意字符一次。

插入字符和美元字符

在正則表達式的開始處使用插入符號(^),表明匹配必須發生在被查找文本的開始除。類似的,在正則表達式的末尾加上美元符號($),表示該字符串必須以這個正則表達式的模式結束才可以匹配到。二者可以同時使用。例如:

helloRegex = re.compile(r"^hello")
>>> mo = helloRegex.search("oh, hello!")
>>> mo_2 = helloRegex.search("hello, Kitty.")
>>> mo == None
True
>>> mo_2
<_sre.SRE_Match object; span=(0, 5), match='hello'>

使用sub()方法替換字符串

正則表達式不僅能夠找到字符串,還能夠捎帶着替代找到的字符串,Regex對象的sub()方法需要傳入兩個字符串參數。第一個參數用來取代查找到的字符串,第二個參數為要查找的字符串。例如:

>>> findXiamenRegex = re.compile(r"Xiamen")
>>> findXiamenRegex.sub("Xi'an", "Xiamen is the capital of Shaanxi.")
"Xi'an is the capital of Shaanxi."

通過傳入re.IGNORECASEre.DOTALLre.VERBOSE來忽略大小寫、匹配換行符和多行輸入正則表達式

re.compile()中傳入re.IGNORECASE或re.I參數可以在匹配時忽略大小寫;傳入re.DOTALL參數來使句點能夠匹配換行符;傳入re.VERBOSE來進行復雜正則表達式的多行輸入。

re.compile()不支持同時傳入多個以上的3個參數,我們可以使用管道符號"|"來進行多個參數的傳入。例如re.compile(r"fool", re.I|re.DOTALL|re.VERBOSE)

小項目:提取剪貼板中的手機號碼、座機號碼和郵件地址,並轉換為規范格式並重新寫回剪貼板(做完就啥都懂了)

這個項目可以分為以下幾步:

  1. 從剪貼板取得文本;
  2. 找出文本中所有的手機號碼、座機號碼和郵件地址;
  3. 將它們粘貼回剪貼板。

首先我們通過網絡搜索得知,國內座機號碼區號位3-4位,且均位0開頭;國內手機號碼則都是1開頭的,有的可能含有86或+86前綴。

代碼如下:

#! python3
# phoneAndEmail.py - Finds phone numbers and email addresses on the clipboard.

import pyperclip
import re

# create phone number regex

phone1_Regex = re.compile(r'''(
    (\+)?
    (86)?
    (1\d{2})
    (-|\s)?
    (\d{8})
    (\D)
)''', re.VERBOSE)

phone2_Regex = re.compile(r'''(
    (0\d{2,3})?
    [-]?
    (\d{8})
    (\-\d{1,4})?
)''', re.VERBOSE)

# create email regex
emailRegex = re.compile(r'''(
    [a-zA-Z0-9._%+-]+
    @
    [a-zA-Z0-9.-]+
)''', re.VERBOSE)

# Find matches in clipboard text.
text = str(pyperclip.paste())
matches = []

for groups in phone1_Regex.findall(text):
    phoneNum = groups[3] + groups[5]
    matches.append(phoneNum)

for groups in phone2_Regex.findall(text):
    phoneNum = groups[1] + groups[2]
    if groups[3] is not None:
        phoneNum += groups[3]
    matches.append(phoneNum)

for groups in emailRegex.findall(text):
    matches.append(str(groups))

# Copy results to the clipboard.
if len(matches) > 0:
    pyperclip.copy("\n".join(matches))
    print("Copied to clipboard: ")
    print("\n".join(matches))
else:
    print("No phone numbers or email address found. ")

復制一段含有手機號碼和電子郵件地址的文本,然后運行程序試試吧。

更多Python正則表達式

更多Python正則表達式內容見:官方文檔

[Python學習筆記]系列是我在學習《Python編程快速上手——讓繁瑣工作自動化(Automate The Boring Stuff With Python)》這本書時的學習筆記。通過自己再手敲一遍概念和代碼,方便自己記憶和日后查閱。如果對你有幫助,那就更好了!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM