網絡上的一些文本,部分會有一些不必要的空格,如果想把空格全部替換掉,使用字符串string類的replace()方法即可,如:
str = str.replace(' ','')
但如果是中英文混排的文本,如果想替換掉漢字中間的空格,而保留英文單詞之間的空格,則問題的解決要複雜一些。需要用到正則表達式。
如有以下文檔:
行(或段)的首尾、一些漢字之間有不必要的空格,需要替換掉。
用以下Python代碼即可:
處理後的文本保存到了new.txt文檔:
當然,一些有規律的亂碼也可以處理。
上面有提到全部是中文的簡單處理方法,也可以用一個簡單的正則表達式判斷文檔或字符串內容是否包含“英文+空格+英文”的形式,然後用一個條件判斷分別處理:
下面需要重點剖析一下上面關於正則表達式的概念及相關的一些內容:
正則表達式是一種用來匹配字符串的強有力的武器。它的設計思想是用一種描述性的語言來給字符串定義一個規則,凡是符合規則的字符串,我們就認為它“匹配”了,否則,該字符串就是不合法的。
1 compile()方法
向re.compile()傳入一個字符串值,表示編譯一個正則表達式,它將返回一個Regex 模式對象(或者就簡稱為Regex 對象)。
我們在Python中使用正則表達式時,re模塊內部會做兩件事情:
I 編譯正則表達式,如果正則表達式的字符串本身不合法,會報錯;
II 用編譯後的正則表達式去匹配字符串;
如果一個正則表達式要重複使用多次或一些較複雜的正則表達式,出於效率的考慮,我們可以預編譯該正則表達式,接下來重複使用時就不需要編譯這個步驟了,直接匹配。編譯後生成Regular Expression對象。
可以向re.compile()傳入re.IGNORECASE 或re.I,作為第二個參數,讓正則表達式不區分大小寫:
>>> robocop = re.compile(r'robocop', re.I)
>>> robocop.search('RoboCop is part man, part machine, all cop.').group()
'RoboCop'
2 r'……'的寫法
r'……'表示忽略……可中可能存在的轉義字符,當做普通字符看待;
3 中括號[]
有時候你想匹配一組字符,但縮寫的字符分類(\d、\w、\s 等)太寬泛。你可以用方括號[]定義自己的字符分類。
\d、\w 和\s 分別匹配數字、字母和空格。
\D、\W 和\S 分別匹配出數字、字母和空格外的所有字符。
例如,字符分類[aeiouAEIOU]將匹配所有元音字符(一個任意的元音字母),不論大小寫。
Regex = re.compile(r'[aeiouAEIOU]')
也可以使用短橫表示字母或數字的範圍。例如,字符分類[a-zA-Z0-9]將匹配所有小寫字母、大寫字母和數字。
請注意,在方括號內,普通的正則表達式符號不會被解釋。這意味著,你不需要前面加上倒斜槓轉義.、*、?或()字符。例如,字符分類將匹配數字0 到5 和一個句點。你不需要將它寫成[0-5\.],寫成[0-5.]即可。
通過在字符分類的左方括號後加上一個插入字符(^),就可以得到“非字符類”。非字符類將匹配不在這個字符類中的所有字符。例如:
Regex = re.compile(r'[^aeiouAEIOU]')
[abc]匹配方括號內的任意字符(諸如a、b 或c)。
[^abc]匹配不在方括號內的任意字符。
插入字符(^)還有另外一種用法,如果用在正則表達式的最前面,表明匹配必須發生在被查找文本開始處。類似地,可以再正則表達式的末尾加上美元符號($),表示該字符串必須以這個正則表達式的模式結束。如正則表達式r'\d$'匹配以數字0 到9 結束的字符串。可以同時使用^和$,表明整個字符串必須匹配該模式。
^spam 意味著字符串必須以spam 開始。
spam$意味著字符串必須以spam 結束。
4 \s 和\S
\s 空格、製表符或換行符(可以認為是匹配“空白”字符);
\S 除空格、製表符和換行符以外的任何字符;
5 問號?、星號*、加號+、半角句號.
? 匹配零次或一次前面的分組。
* 匹配零次或多次前面的分組。
+ 匹配一次或多次前面的分組。
. 匹配所有字符,換行符\n除外。
問號?在正則表達式中可能有兩種含義:聲明非貪心匹配或表示可選的分組。這兩種含義是完全無關的。
在字符串'HaHaHaHaHa'中,因為(Ha){3,5}可以匹配3 個、4 個或5 個實例,你可能會想,為什麼在前面花括號的例子中,Match 對象的group()調用會返回'HaHaHaHaHa',而不是更短的可能結果。畢竟,'HaHaHa'和'HaHaHaHa'也能夠有效地匹配正則表達式(Ha){3,5}。
Python 的正則表達式默認是“貪心”的,這表示在有二義的情況下,它們會盡可能匹配最長的字符串。花括號的“非貪心”版本匹配儘可能最短的字符串,即在結束的花括號後跟著一個問號。
{n,m}?或*?或+?對前面的分組進行非貪心匹配。
大括號{}
表示匹配{}前面分組的次數。
{n}匹配n 次前面的分組。
{n,}匹配n 次或更多前面的分組。
{,m}匹配零次到m 次前面的分組。
{n,m}匹配至少n 次、至多m 次前面的分組。
{n,m}?或*?或+?對前面的分組進行非貪心匹配。
6 \\u4e00-\\u9fa5
表示漢字unicode編碼方式下的編碼範圍,多達20892方塊字(5*16*16*16=20480)。
後面部分:
最後一個方塊字是龥,yù,呼也。UniCode CJK編碼:U+9FA5,五筆:WGKM
7 小括號()
有時候,你可能需要使用匹配的文本本身,作為替換的一部分。替換時使用\1、\2、\3……。表示“在替換中輸入分組1、2、3……的文本”。(可以理解為需要保留部分)
除了簡單地判斷是否匹配之外,正則表達式還有提取子串的強大功能。match方法配合用()表示的就是要提取的分組(Group)。
比如^(\d{3})-(\d{3,8})$分別定義了兩個組,可以直接從匹配的字符串中提取出區號和本地號碼:
>>> m = re.match(r'^(\d{3})-(\d{3,8})$', '010-12345')
>>> m.group(0)
'010-12345'
>>> m.group(1)
'010'
>>> m.group(2)
'12345'
如果正則表達式中定義了組,就可以在Match對象上用group()方法提取出子串來。
注意到group(0)永遠是原始字符串,group(1)、group(2)……表示第1、2、……個子串。
8 Regex對象的sub()方法
正則表達式不僅能找到文本模式,而且能夠用新的文本替換掉這些模式。Regex對象的sub()方法需要傳入兩個參數。第一個參數是一個字符串,用於取代發現的匹配。第二個參數是一個字符串,即正則表達式。
sub()方法返回替換完成後的字符串。如:
resup =re.compile(r'(\[\d*\])') # 上標處理
s = resup.sub(r'\1', s)
9 re的findall()方法
findall()方法將返回一組字符串,包含被查找字符串中的所有匹配。(search()將返回一個Match對象,包含被查找字符串中的“第一次”匹配的文本。)
利用findall()方法,可以找到“所有”匹配的地方。另一方面,findall()不是返回一個Match 對象,而是返回一個字符串列表。以下兩種寫法都可以:
list1 = re.findall(pattern,strs)
list1 = pattern.findall(strs)
10 split()方法
split()方法提供強大了字符串切割功能,可以按正則表達式指定的字符進行切割,返回一個列表。
如用空格切割:
>>> 'a b c'.split(' ')
['a', 'b', '', '', 'c']
用多個空格切割:
>>> re.split(r'\s+', 'a b c')
['a', 'b', 'c']
用多個字符進行切割:
re.split(r'[\s\,\;]+', 'a,b;; c d')
['a', 'b', 'c', 'd']
關於中文字符匹配,為了加深理解,可以再看一下下面的例子:
常用正則表達式,可以總結一下:
?匹配零次或一次前面的分組。
*匹配零次或多次前面的分組。
+匹配一次或多次前面的分組。
{n}匹配n 次前面的分組。
{n,}匹配n 次或更多前面的分組。
{,m}匹配零次到m 次前面的分組。
{n,m}匹配至少n 次、至多m 次前面的分組。
{n,m}?或*?或+?對前面的分組進行非貪心匹配。
^spam 意味著字符串必須以spam 開始。
spam$意味著字符串必須以spam 結束。
.匹配所有字符,換行符除外。
\d、\w 和\s 分別匹配數字、單詞和空格。
\D、\W 和\S 分別匹配出數字、單詞和空格外的所有字符。
[abc]匹配方括號內的任意字符(諸如a、b 或c)。
[^abc]匹配不在方括號內的任意字符。
字符|稱為“管道”。希望匹配許多表達式中的一個時,就可以使用它。例如,正則表達式r'Batman|Tina Fey'將匹配'Batman'或'Tina Fey'。
正則表達式以外的其它補充:
字符串的strip()方法:可以刪除掉字符串首尾兩端的空格,包括\n,還可以刪除掉首尾兩端的指定字符,形式如strip([str])。strip()方面的功能可以分解為lstrip()和rstrip()。
附代碼:
import re
f0 = open('new.txt','w',encoding="UTF-8")
#處理文本中的空格,只要含有“英文+空格+英文”就不處理
pattern =re.compile(u"[a-zA-Z]+\s+[a-zA-Z]+")
with open('text.txt', 'rU') as file:
....strs = file.read()
....entxt = re.findall(pattern,strs)
if (not entxt):
'''
....s = s.replace('.' , '。')
....s = s.replace(',' , ',')
....s = s.replace('!' , '!')
....s = s.replace('?' , '?')
....'''
....s = strs.replace(' ','') # 處理全角空格
....s = s.replace(' ' , '') # 處理半角空格(全中文可以使用)
....f0.write(s)
....f0.close()
else:
....pattern =re.compile(r'([\\u4e00-\\u9fa5,]{1})\s+([\\u4e00-\\u9fa5,]{1})')
....with open('text.txt', 'rU') as f2:
........str = f2.readline()
........while str: # readline()方法讀到最後會返回一個空字符
............s = str.replace(' ','') # 處理全角空格
............s = pattern.sub(r'\1\2', str)
............s = s.strip() + "\n" # strip()方法會把尾端的\n也去掉
............f0.write(s)
............str = f2.readline() # readline()方法每次只讀取一行
....f0.close() # 如果不是使用上面的with方法,需要close()後文檔才會寫入nex.tx
-End-
閱讀更多 小智雅匯 的文章