python字符串与文本处理技巧(2):大小写敏感搜索、最短匹配、多行匹配、Unicode标准化
1.?字符串忽略大小寫的搜索替換
- re.findall(patter, string, flags=re.IGNORECASE)
當我們需要忽略字符串中的字母大小寫進行模式搜索時可以采用如下方案:
import re text = 'UPPER PYTHON, lower python, Mix Python' target1 = re.findall('python', text, flags=re.IGNORECASE) print(target1) # >>> ['PYTHON', 'python', 'Python']target2 = re.sub('python', 'snake', text, flags=re.IGNORECASE) print(target2) # >>> UPPER snake, lower snake, Mix snake2. 最短匹配模式
如果我們正在試著用正則表達式匹配某個文本模式,但是它找到的是模式的最長可能匹配。 而我們想修改它變成查找最短的可能匹配。這個問題一般出現在需要匹配一對分隔符之間的文本的時候(比如引號包含的字符串)。考慮如下的例子:
from re import compilestr_pat = compile(r'"(.*)"') text1 = 'computer says "no."' target1 = str_pat.findall(text1) print(target1) # >>> ['no.']text2 = 'computer says "no.", but phone says "yes."' target2 = str_pat.findall(text2) print(target2) # >>> ['no.", but phone says "yes.']在這個例子中,模式 r'"(.*)"' 的意圖是匹配被雙引號包含的文本。 但是在正則表達式中*操作符是貪婪的,因此匹配操作會查找最長的可能匹配。 于是在第二個例子中搜索 text2 的時候返回結果并不是我們想要的。
為了修正這個問題,可以在模式中的*操作符后面加上?修飾符,就像這樣:
str_pat_new = compile(r'"(.*?)"') target3 = str_pat_new.findall(text2) print(target3) # >>> ['no.', 'yes.']這樣就使得匹配變成非貪婪模式,從而得到最短的匹配,也就是我們想要的結果。
Comment?: ?在一個模式字符串中,點(.)匹配除了換行外的任何字符。 然而,如果你將點(.)號放在開始與結束符(比如引號)之間的時候,那么匹配操作會查找符合模式的最長可能匹配。 這樣通常會導致很多中間的被開始與結束符包含的文本被忽略掉,并最終被包含在匹配結果字符串中返回。 通過在 * 或者 + 這樣的操作符后面添加一個 ? 可以強制匹配算法改成尋找最短的可能匹配。
3. 多行匹配模式
當我們試著使用正則表達式去匹配一大塊的文本,并需要跨越多行去匹配。這個問題很典型的出現在:當我們用點(.)去匹配任意字符的時候,忘記了點(.)不能匹配換行符的事實。如果我們希望(.)可以匹配包括換行符在內的所有字符,應該指明re.DOTALL參數。例如:
import recomment = re.compile(r'/\*(.*?)\*/') text1 = '/*this is a comment */ and /* this is another comment */' text2 = """ /* this is a multiline commnet */ and /* this sentense is added to this comments*/ """ print(comment.findall(text1)) print(comment.findall(text2)) # >>> ['this is a comment ', ' this is another comment '] # >>> []"""修正問題,修改模式字符串,增加對換行的支持""" comment_new = re.compile(r'/\*((?:.|\n)*?)\*/') print(comment_new.findall(text2)) # >>>[' this is\na multiline commnet ', # ' this\nsentense is added to this comments']"""re.DOTALL可以使得正則化表達式中的(.)匹配包括換行符中的任意字符"""" comment_New = re.compile(r'/\*(.*?)\*/', re.DOTALL) print(comment_New.findall(text2)) # >>> [' this is\na multiline commnet ', # ' this\nsentense is added to this comments']在這個模式中, (?:.|\n) 指定了一個非捕獲組 (也就是它定義了一個僅僅用來做匹配,而不能通過單獨捕獲或者編號的組)。com
Comment:對于簡單的情況使用 re.DOTALL 標記參數工作的很好, 但是如果模式非常復雜或者是為了構造字符串令牌而將多個模式合并起來, 這時候使用這個標記參數就可能出現一些問題。
4. Unicode標準化
在處理Unicode字符串時,我們需要確保所有字符串在底層有相同的表示。
在Unicode中,某些字符能夠用多個合法的編碼表示。例如下面的例子:
s1 = 'Spicy Jalape\u00f1o' s2 = 'Spicy Jalapen\u0303o' print(s1, len(s1), s2, len(s2), s1==s2) # >>> Spicy Jalape?o 14 Spicy Jalapen?o 15 False文本”Spicy Jalape?o”使用兩種形式來表示。 前者使用整體字符”?”(U+00F1),后者使用拉丁字母”n”后面跟一個”~”的組合字符(U+0303)。在需要比較字符串的程序中使用字符的多種表示會產生問題。 為了修正這個問題,我們可以使用unicodedata模塊先將文本標準化:
import unicodedatas1 = 'Spicy Jalape\u00f1o' s2 = 'Spicy Jalapen\u0303o' print(s1, len(s1), s2, len(s2), s1==s2) # >>> Spicy Jalape?o 14 Spicy Jalapen?o 15 Falset1 = unicodedata.normalize('NFC', s1) t2 = unicodedata.normalize('NFC', s2) print(t1, len(t1), t2, len(t2), t1==t2) # >>> Spicy Jalape?o 14 Spicy Jalape?o 14 Trueprint(ascii(t1), ascii(t2)) # >>> 'Spicy Jalape\xf1o' 'Spicy Jalape\xf1o'normalize()。第一個參數指定字符串標準化的方式。 NFC表示字符應該是整體組成,當然也可以用NFD,NFD表示字符應該分解為多個組合字符表示。
Comment:標準化對于任何需要以一致的方式處理Unicode文本的程序都是非常重要的。 當處理來自用戶輸入的字符串,而我們很難去控制編碼的時候尤其如此。 在清理和過濾文本的時候字符的標準化也是很重要的。 比如,假設我們想清除掉一些文本上面的變音符的時候(可能是為了搜索和匹配)。
文章參考 python3-codebook
總結
以上是生活随笔為你收集整理的python字符串与文本处理技巧(2):大小写敏感搜索、最短匹配、多行匹配、Unicode标准化的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python字符串与文本处理技巧(1):
- 下一篇: Google存储海量私人信息 隐私问题不