Python實用技法第31篇:文本過濾和清理

問題

某些無聊的腳本小子在Web頁面表單中填入了“pýtĥöñ”這樣的文本,我們想以某種方式將其清理掉。

解決方案

文本過濾和清理所涵蓋的範圍非常廣泛,涉及文本解析和數據處理方面的問題。在非常簡單的層次上,我們可能會用基本的字符串函數(例如str.upper()和str.lower())將文本轉換爲標準形式。簡單的替換操作可通過str.replace()或re.sub()來完成,它們把重點放在移除或修改特定的字符序列上。也可以利用unicodedata.normalize()來規範化文本。

然而我們可能想更進一步。比方說也許想清除整個範圍內的字符,或者去掉音符標誌。要完成這些任務,可以使用常被忽視的str.translate()方法。爲了說明其用法,假設有如下這段混亂的字符串:

>>> s = 'pytĥon\fis\tawesome\r\n'
>>> s
'pytĥon\x0cis\tawesome\r\n'
>>>
Python資源分享qun 784758214 ,內有安裝包,PDF,學習視頻,這裏是Python學習者的聚集地,零基礎,進階,都歡迎

第一步是清理空格。要做到這步,先建立一個小型的轉換表,然後使用translate()方法:

>>> remap = {
...      ord('\t') : ' ',
...      ord('\f') : ' ',
...      ord('\r') : None        # Deleted
... }
>>> a = s.translate(remap)
>>> a
'pytĥon is awesome\n'

可以看到,類似t和f這樣的空格符已經被重新映射成一個單獨的空格。回車符r已經完全被刪除掉了。

可以利用這種重新映射的思想進一步構建出更加龐大的轉換表。例如,我們把所有的Unicode組合字符都去掉:

>>> import unicodedata
>>> import sys
>>> cmb_chrs = dict.fromkeys(c for c in range(sys.maxunicode) if unicodedata.combining(chr(c)))
...
>>> b = unicodedata.normalize('NFD', a)
>>> b
'pytĥon is awesome\n'
>>> b.translate(cmb_chrs)
'python is awesome\n'
>>>

在這個例子中,我們使用dict.fromkeys()方法構建了一個將每個Unicode組合字符都映射爲None的字典。

原始輸入會通過unicodedata.normalize()方法轉換爲分離形式,然後再通過translate()方法刪除所有的重音符號。我們也可以利用相似的技術來去掉其他類型的字符(例如控制字符)。

下面來看另一個例子。這裏有一張轉換表將所有的Unicode十進制數字字符映射爲它們對應的ASCII版本:

>>> digitmap = { c: ord('0') + unicodedata.digit(chr(c))
...               for c in range(sys.maxunicode)
...               if unicodedata.category(chr(c)) == 'Nd' }
...
>>> len(digitmap)
460
>>> # Arabic digits
>>> x = '\u0661\u0662\u0663'
>>> x.translate(digitmap)
'123'
>>>

另一種用來清理文本的技術涉及I/O解碼和編碼函數。大致思路是首先對文本做初步的清理,然後通過結合encode()和decode()操作來修改或清理文本。示例如下:

>>> a
'pytĥon is awesome\n'
>>> b = unicodedata.normalize('NFD', a)
>>> b.encode('ascii', 'ignore').decode('ascii')
'python is awesome\n'
>>>

這裏的normalize()方法先對原始文本做分解操作。後續的ASCII編碼/解碼只是簡單地一次性丟棄所有不需要的字符。很顯然,這種方法只有當我們的最終目標就是ASCII形式的文本時纔有用。

討論

文本過濾和清理的一個主要問題就是運行時的性能。一般來說操作越簡單,運行得就越快。對於簡單的替換操作,用str.replace()通常是最快的方式——即使必須多次調用它也是如此。比方說如果要清理掉空格符,可以編寫如下的代碼:

def clean_spaces(s):
    s = s.replace('\r', '')
    s = s.replace('\t', ' ')
    s = s.replace('\f', ' ')
return s

Python資源分享qun 784758214 ,內有安裝包,PDF,學習視頻,這裏是Python學習者的聚集地,零基礎,進階,都歡迎

如果試着調用它,就會發現這比使用translate()或者正則表達式的方法要快得多。

另一方面,如果需要做任何高級的操作,比如字符到字符的重映射或刪除,那麼translate()方法還是非常快的。

從整體來看,我們應該在具體的應用中去進一步揣摩性能方面的問題。不幸的是,想在技術上給出一條“放之四海而皆準”的建議是不可能的,所以應該嘗試多種不同的方法,然後做性能統計分析。

儘管本節的內容主要關注的是文本,但類似的技術也同樣適用於字節對象(byte),這包括簡單的替換、翻譯和正則表達式。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章