現在有一道題目,要求利用python中re模塊來匹配ip地址,我們應如何着手?
首先能想到的是ip地址是數字,正則表達式是如何匹配數字的呢?
\d或[0-9]
對於這個問題,不要一下子上來就寫匹配模式,應該一步步分解,把復雜的問題簡單化
比如ip地址,我們可以總結一下規律
1. 它是一個字符串
2. 字符串內部是由4個1-3位的數字和3個.組成
3. 數字的范圍是0-255
接下來,我們先試一下匹配第1個數字
第一步:嘗試匹配192.168.100.123中的192
>>> import re >>> re.search(r"\d\d\d", "192.168.100.123") <_sre.SRE_Match object; span=(0, 3), match='192'>
第二步:嘗試匹配192.168.100.123中的192.
值得注意的是,由於正則表達式中的元字符 . 表示除了\n之外的任意一個字符,我們需要匹配 . 本身,就需要用 \ 進行轉義
>>> re.search(r"\d\d\d\.", "192.168.100.12") <_sre.SRE_Match object; span=(0, 4), match='192.'>
第三步:嘗試匹配192.168.100.123的整體
>>> re.search(r"\d\d\d\.\d\d\d\.\d\d\d\.\d\d\d", "192.168.100.123") <_sre.SRE_Match object; span=(0, 15), match='192.168.100.123'>
這樣寫有什么問題呢?
1. 我們的數字並不都是3位,像192.168.100.1這樣的,我們的匹配模式就失效了
>>> re.search(r"\d\d\d\.\d\d\d\.\d\d\d\.\d\d\d", "192.168.100.1") >>>
2. 不夠美觀
第四步:優化誤區一
很多人一上手,就寫成了[0-255],這不就是數字的范圍0-255嗎?我們說,這樣是不對的
正則表達式中,真正要匹配的永遠是字符串,一個字符串內部是由三位的數字構成的,如果需要匹配三位數字的形式,就需要用到[0-9][0-9][0-9]或\d\d\d,用一個[0-9]表示的只能匹配一位,[0-255]這種錯誤的寫法也只能匹配到一位0-9之間的數字
>>> re.search(r"[0-255]", "255") <_sre.SRE_Match object; span=(0, 1), match='2'>
如下圖,第一個[0-255]匹配到了1, \.匹配到了.
這個ip地址中根本沒有 1. 這種形式的,所以返回了None
>>> re.search(r"[0-255]\.[0-255]\.[0-255]\.[0-255]", "192.168.100.1") >>>
優化誤區二:
既然上面的不對,那能不能把255分解成 2, 5, 5, 我們匹配時能否寫成[0-2][0-5][0-5],看似是沒問題的,我們來試試
>>> re.search(r"[0-2][0-5][0-5]", "192.168.100.1") <_sre.SRE_Match object; span=(8, 11), match='100'> >>>
為什么192沒有匹配到?168也沒有匹配到?因為數字的十位和個位最大只能是5,超過5的肯定沒法匹配
第五步:繼續優化
我們來看下0-255這個范圍,當百位是0或者1時,十位和個位可以是[0-9],也就是\d,當百位是2時,十位是[0-4]時,個位可以是\d,當百位是2時,十位是5時,個位只能是[0-5],那么,我們是不是可以這樣來寫,比如匹配192,匹配模式可以寫成
[01]\d\d|2[0-4]\d|25[0-5]
如果有重復的,我們可以給上面的模式加上 {n} 表示重復匹配前面的字符n次
>>> re.search(r"(([01]\d\d|2[0-4]\d|25[0-5]\d)\.){3}([01]\d\d|2[0-4]\d|25[0-5]\d)", "192.168.100.123") <_sre.SRE_Match object; span=(0, 15), match='192.168.100.123'>
看似正確了,但是還是匹配不到數字 1 ,因為我們的百位是[01],意味這如果是 1 的情況下,我們的結果是001,但ip地址是不能寫成001, 002的
>>> re.search(r"(([01]\d\d|2[0-4]\d|25[0-5]\d)\.){3}([01]\d\d|2[0-4]\d|25[0-5]\d)", "192.168.100.1") >>>
>>> re.search(r"(([01]\d\d|2[0-4]\d|25[0-5]\d)\.){3}([01]\d\d|2[0-4]\d|25[0-5]\d)", "192.168.100.001") <_sre.SRE_Match object; span=(0, 15), match='192.168.100.001'> >>>
可以通過{0,1}來優化,表示前面的字符重復0-1次,也可以用 ?
>>> re.search(r"(([01]{0,1}\d{0,1}\d|2[0-4]\d|25[0-5]\d)\.){3}([01]{0,1}\d{0,1}\d|2[0-4]\d|25[0-5]\d)", "192.168.100.1") <_sre.SRE_Match object; span=(0, 13), match='192.168.100.1'> >>> re.search(r"(([01]?\d?\d|2[0-4]\d|25[0-5]\d)\.){3}([01]?\d?\d|2[0-4]\d|25[0-5]\d)", "192.168.100.1") <_sre.SRE_Match object; span=(0, 13), match='192.168.100.1'>
這樣,當數字只有個位時,百位的0匹配0次,十位的0匹配0次,只留下個位數字[0-9]