Python3 如何優雅地使用正則表示式(詳解六)
修改字串
我們已經介紹完如何對字元進行搜尋,接下來我們講講正則表示式如何修改字串。
正則表示式使用以下方法修改字串:
方法 | 用途 |
split() | 在正則表示式匹配的地方進行分割,並返回一個列表 |
sub() | 找到所有匹配的子字串,並替換為新的內容 |
subn() | 跟 sub() 幹一樣的勾當,但返回新的字串以及替換的數目 |
分割字串
正則表示式的 split() 方法將字串在匹配的地方進行分割,並將分割後的結果作為列表返回。它的做法其實很像字串的 split() 方法,但這個可以使用更加廣泛的分隔符。你猜的沒錯,它同時提供了一個模組級別的函式:re.split()
.split(string[, maxsplit=0])
通過正則表示式匹配來分割字串。如果在 RE 中,你使用了捕獲組,那麼它們的內容會作為一個列表返回。你可以通過傳入一個 maxsplit 引數來設定分割的數量。如果 maxsplit 的值是非 0,表示至多有 maxsplit 個分割會被處理,剩下的內容作為列表的最後一個元素返回。
下邊例子中,分隔符是任何非字母數字字元:
>>> p = re.compile(r'\W+') >>> p.split('This is a test, short and sweet, of split().') ['This', 'is', 'a', 'test', 'short', 'and', 'sweet', 'of', 'split', ''] >>> p.split('This is a test, short and sweet, of split().', 3) ['This', 'is', 'a', 'test, short and sweet, of split().']
有時候你可能不僅對分隔符之間的內容感興趣,你可能對分隔符本身(就是正則表示式匹配的內容)也同樣感興趣。如果使用了捕獲組,那麼作為分隔符的值也會被返回:
>>> p = re.compile(r'\W+') >>> p2 = re.compile(r'(\W+)') >>> p.split('This... is a test.') ['This', 'is', 'a', 'test', ''] >>> p2.split('This... is a test.') ['This', '... ', 'is', ' ', 'a', ' ', 'test', '.', '']
模組級別的函式 re.split() 除了將 RE 作為第一個引數外,其他引數是一樣的:
>>> re.split('[\W]+', 'Words, words, words.')
['Words', 'words', 'words', '']
>>> re.split('([\W]+)', 'Words, words, words.')
['Words', ', ', 'words', ', ', 'words', '.', '']
>>> re.split('[\W]+', 'Words, words, words.', 1)
['Words', 'words, words.']
搜尋和替換
另一個常見的任務就是找到所有的匹配部分,並替換成不同的字串。sub 方法可以幫你實現這個願望!sub 方法有一個 replacement 引數,它可以是一個待替換的字串,或者一個處理字串的函式。
.sub(replacement, string[, count=0])
返回一個字串,這個字串從最左邊開始,所有 RE 匹配的地方都替換成 replacement。如果沒有找到任何匹配,那麼返回原字串。
可選引數 count 指定最多替換的次數,必須是一個非負值。預設值是 0,意思是替換所有找到的匹配。
下邊是使用 sub() 方法的例子,它會將所有的顏色替換成 color:
>>> p = re.compile( '(blue|white|red)')
>>> p.sub( 'colour', 'blue socks and red shoes')
'colour socks and colour shoes'
>>> p.sub( 'colour', 'blue socks and red shoes', count=1)
'colour socks and red shoes'
subn() 方法跟 sub() 方法幹同樣的勾當,但區別是返回值為一個包含有兩個元素的元組:一個是替換後的字串,一個是替換的數目。
>>> p = re.compile( '(blue|white|red)')
>>> p.subn( 'colour', 'blue socks and red shoes')
('colour socks and colour shoes', 2)
>>> p.subn( 'colour', 'no colours at all')
('no colours at all', 0)
空匹配只有在它們沒有緊挨著前一個匹配時才會被替換掉:
>>> p = re.compile('x*')
>>> p.sub('-', 'abxd')
'-a-b-d-'
如果 replacement 引數是一個字串,那麼裡邊的反斜槓都會被處理。比如 \n 將會被轉換成一個換行符,\r 轉換成回車,等等。未知的轉義如 \j 保持原樣。逆向引用如 \6,則被 RE 中相應的捕獲組匹配的內容所替換。這使你可以在替換後的字串中插入一部分原字串。
下邊例子中,將匹配被 { 和 } 括起來的單詞 section,並將 section 替換成 subsection:
>>> p = re.compile('section{ ( [^}]* ) }', re.VERBOSE)
>>> p.sub(r'subsection{\1}','section{First} section{second}')
'subsection{First} subsection{second}'
解釋:
1. 大家還記得嗎?這裡開啟了 re.VERBOSE,空格將被忽略。因為這裡一堆符號,用空格隔開看著才不會亂糟糟的......
2. 這裡 r'subsection{\1}' 使用 \1 引用匹配模式中的 ([^}]*) 匹配的字串內容。
還可以使用 Python 的擴充套件語法 (?P<name>...) 指定命名組,引用命名組的語法是 \g<name>。\g<name> 會將名字為 name 的組匹配的字串替換進去。另外,\g<數字> 是通過組的序號進行引用。\g<2> 其實就相當於 \2,但我們更提倡使用 \g<2>,因為這樣可以避免歧義。例如,\g<2>0 的含義是引用序號為 2 的組,然後後邊匹配一個字元 '0',而你寫成 \20 就會被認為是引用序號為 20 的組了。
>>> p = re.compile('section{ (?P<name> [^}]* ) }', re.VERBOSE)
>>> p.sub(r'subsection{\1}','section{First}')
'subsection{First}'
>>> p.sub(r'subsection{\g<1>}','section{First}')
'subsection{First}'
>>> p.sub(r'subsection{\g<name>}','section{First}')
'subsection{First}'
有時候你可能不滿足簡單的字串替換,你可能需要在替換的過程中動點“手腳”......沒關係,一樣可以滿足你!replacement 引數還可以是一個函式,該函式將會在正則表示式模式每次不重複匹配的時候被呼叫。在每次呼叫時,函式會收到一個匹配物件的引數,因此你就可以利用這個物件去計算出新的字串並返回它。
下邊的例子中,替換函式將十進位制數替換為十六進位制數:
>>> def hexrepl(match):
... "Return the hex string for a decimal number"
... value = int(match.group())
... return hex(value)
...
>>> p = re.compile(r'\d+')
>>> p.sub(hexrepl, 'Call 65490 for printing, 49152 for user code.')
'Call 0xffd2 for printing, 0xc000 for user code.'
當使用模組級的 re.sub() 函式時,正則表示式模式作為第一個引數。該模式可以是一個字串或一個編譯好的物件。如果你需要指定正則表示式標誌,那麼你必須使用後者;或者使用模式內嵌修正器,例如 sub("(?i)b+", "x", "bbbb BBBB") 返回 'x x'。
(未完待續)