本篇文章主要讲解一些基础的python正则表达式的应用,以及re模块中的几个方法,关于正则表达式和个函数的详细解释,参看链接:Python 正则表达式,这个网址讲述的非常清楚,我这篇文章只是为了更清晰的讲述一下它的应用。需要先掌握的几个函数为 re.match()、 re.dearch()、re.compile()、re.findall()、re.finditer()、内置函数replace()、strip(),在上边的链接里都有,我再赘述没有什么意义。
首先我们从一个问题出发,来看一下我们为什么要用正则表达式。今天我要预处理些评论数据,数据的形式见下图,我的需求主要有以下几个:
①去除评论中的换行符、空格、tab空格等格式;
②去除评论中的""最满意"": 、 ""最不满意"":、 ""空间"":等标签(包括冒号);
③去除评论中的一些标点符号如句号、引号、括号、破折号等;
④对句子进行简单的拆分。
可以看出,我希望对于文本的处理是比较细粒度的,已经具体到某些特定的文字表达了,这个时候我们想要使用一种通用、简单、有效的方法进行文本预处理,那最好就用正则表达式,它在更为经典的语言中被设计来就是用于文本匹配与处理的。
正则表达式就是通过匹配的方式来查找目标文本中对应的短语或符号,再针对匹配到的字符进行删除、修改、替换等操作。简言之,就是用正则表达式的语言查找人类语言中对应的句子。举个例子我想要找到上图中""最满意""(包括英文的引号)对应的汉字及标点符号,那么与之匹配的正则表达式可以是 '.*:' 或者 '\"(.*)\".[::]' ,具体使用哪种形式还需要看你想要处理的文本的特征。我来举两个例子:
(1)例一:假如我的句子为:""最满意"" : ""最满意的还是动力,可能因为之前1.4T的凌度排量较,开上2.0T哪动力感觉好辣眼睛。
那么这个时候我们用两种方式进行匹配都可以,如下。可以看到两种方式都能匹配到我们想要的字符串。
(2)例二:假如我的句子为:我的观点如下:""最满意"" : ""最满意的还是动力,可能因为之前1.4T的凌度排量较,开上2.0T哪动力感觉好辣眼睛。
那么这个时候我们就能看出来差别了,很明显第一种方式匹配的更加精准。
我们可以分析一下上边提到的两个正则表达式,就知道是什么原因了。
(3)表达式解析
'.*:' 解析:
- “.”可以匹配除了“\n”之外的任何单个字符(如果想要包括“\n”,需要使用“[.\n]”);
- “*”可以匹配“*”前面0个或多个正则表达式(例如这里的“.*”就可以代表0个或多个“.”,而每一个“.”又可以代表任何除换行以外的字符);
- “:”就代表的中文的冒号(注意正则表达式中的标点一般都区分中文的还是英文的,但是有一些例如减号、等号等不区分)。
'\"(.*)\".[::]' 解析:
- “\"”代表的就是英文的双引号,前面的斜杠是转义字符(这里需要注意英文的单双引号、以及一些整么表达式模式如“^”、“.”如果想要表达的实际上就是这些符号,需要加上转义字符);
- “(.*)”含义见上;
- “[::]”中的方括号表示的是一个或的关系,表示方括号内符号中的任意一个。
另外,有时我们可能要删除句子中的“\”,这个时候我们的正则表达式可以写成r'\',前面的r就是让python忽略正则表达式中的转义字符,这时候我们就能匹配到斜杠了。
最后,针对文首的四个需求,我写了如下代码来处理我的评论,可能结构写的不好,但是可以作为参考。
(1)首先是数据清洗的,这个先后顺序不是随便的,正则表达式以及处理顺序需要根据你的文本的特点自行调整。
def review_pro(self):
# 数据清洗,输入输出均为单行文本
n_review = re.sub('[{}\"\s]+', '', self.review) # 去除大括号、换行、空格等
nn_review = n_review.replace(re.search('....:', n_review).group(), "") # 去除标题“最不满意:”
removed_review = re.sub('(..:)|(...:)', '', nn_review) # 去除其它标题
return removed_review
(2)然后是分句的,我这里分句后还想保留原来的标点符号,所以多了两步处理,利用split()函数后又把标点加了回去。
def split_review(self):
# 分句,输入输出均为text文档
split_text = []
with open(self.input_address, encoding='utf-8') as f:
for line in f:
split_row = re.split(r'([;。!??!])', line.replace("\n", ""))
pun_num = re.findall(r'([;。!??!])', line)
for i in range(len(split_row) - len(pun_num)):
if re.match(r'([;。!??!])', split_row[i]):
split_row[i - 1] += split_row[i]
split_row.remove(split_row[i])
for row in split_row:
split_text.append(row)
f.close()