常用縮寫字符及其含義表格查詢
縮寫字符分類 | 含義 |
---|---|
\d | 0-9的任意數字 |
\D | 除0-9的數字以外的任何字符 |
\w | 任何字母、數字或下划線字符(可以認為是匹配“單詞”字符) |
\W | 除字母、數字和下划線意外的任何字符 |
\s | 空格、制表符或換行符(可以認為是匹配“空白”字符) |
\S | 除空格、制表符或換行符的任何字符 |
? | 問號緊跟於分組后表示前面分組的內容是可有可無的 |
* | 星號緊跟於分組后表示前面分組的內容匹配0次或多次 |
+ | 加號緊跟於分組后表示前面分組的內容匹配至少1次 |
| | 管道匹配多個分組 |
{} | 花括號內加數字表示匹配特定字數 |
. | 句點表示匹配任何字符(除了換行) |
在Python中使用正則表達式的步驟
在Python中使用正則表達式主要有下面幾個步驟:
- 用
import re
導入正則表達式模塊; - 用
re.compile()
函數創建一個Regex 對象; - 向Regex 對象的
search()
或findall()
方法傳入目標字符串; - 調用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'
總結:
- 如果調用在一個沒有分組的正則表達式上,例如
\d\d\d-\d\d\d\d\d\d\d\d
,方法findall()
將返回一個匹配字符串的列表,例如['029-88618871','010-36758954']; - 如果調用在一個有分組的正則表達式上,例如
(\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.IGNORECASE
、re.DOTALL
和re.VERBOSE
來忽略大小寫、匹配換行符和多行輸入正則表達式
向re.compile()
中傳入re.IGNORECASE或re.I參數可以在匹配時忽略大小寫;傳入re.DOTALL參數來使句點能夠匹配換行符;傳入re.VERBOSE來進行復雜正則表達式的多行輸入。
re.compile()
不支持同時傳入多個以上的3個參數,我們可以使用管道符號"|"來進行多個參數的傳入。例如re.compile(r"fool", re.I|re.DOTALL|re.VERBOSE)
。
小項目:提取剪貼板中的手機號碼、座機號碼和郵件地址,並轉換為規范格式並重新寫回剪貼板(做完就啥都懂了)
這個項目可以分為以下幾步:
- 從剪貼板取得文本;
- 找出文本中所有的手機號碼、座機號碼和郵件地址;
- 將它們粘貼回剪貼板。
首先我們通過網絡搜索得知,國內座機號碼區號位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)》這本書時的學習筆記。通過自己再手敲一遍概念和代碼,方便自己記憶和日后查閱。如果對你有幫助,那就更好了!