Python2和Python3正则匹配中文时的编码问题
我们都会遇到这样的人,他们说话时是中文英文穿插使用的。也就是一句话中有中文也有英文,很多时候没有办法避免,尤其是说一些专业术语时,当然也有纯个人说话习惯和故意的。
我想表达的是,在程序中也难免会遇到这种情况,同一条数据中即有中文也有英文,还可能有数字或其他的字符。如果我们只想要提取出其中的中文内容,把其他的“杂质”过滤掉,我们可以使用正则来实现这个功能。
但是,在使用中,也会有一些小问题,比如对于Python2和Python3来说,就会有一些区别需要注意。我们现在就来看怎么进行处理。
一、Python3中正则匹配中文
Python中的正则匹配是通过一套字符规则来进行匹配的,通过re模块来实现。
参考: https://blog.csdn.net/weixin_43790276/article/details/90690569
Windows系统中,re中的\w可以匹配大小写英文字母、数字和中文。Linux系统中,\w不能匹配中文。
# coding=utf-8
import re
test_str = 'When面对困难111,we正面面对,yes,加油!666'
comp = re.match(r'\w+', test_str)
print(comp.group())
运行结果:
# Windows运行结果
When面对困难111
# Linux运行结果
When
但是,这样的结果并不是我们需要的,我们只要中文,因此不能使用\w来匹配。
我们可以通过在中括号[]内指定字符范围来匹配中文,中文的字符范围是 \u4E00-\u9FA5 。
# coding=utf-8
import re
test_str = 'When面对困难111, we 正面面对, yes, 加油!666'
comp = re.compile(r'[\u4E00-\u9FA5]+')
re_result = comp.findall(test_str)
print(re_result)
运行结果:
['面对困难', '正面面对', '加油']
可以看到,我们成功匹配到了test_str中的所有中文。
二、Python2中匹配中文的问题
在实际工作中,还有非常多的生产环境在使用Python2,如果公司既有Python2也有Python3的环境,那么,我们的代码部署之后就有可能在Python2和Python3两种解释器上运行。
我们看一下上面的代码在Python2中的运行结果。
['When', '111', 'e', 'es', '666']
上面在Python3中匹配中文的代码没有做任何改动,在Python2中运行时,匹配结果跟我们的需求完全背道而驰。
那是什么原因造成的呢?其实是Python2解释器和Python3解释器编码不同造成的。Python2和Python3最大的区别,或者说最让程序员头疼的问题基本都是编码问题,不过在这里不做过多讨论。
如何可以证明是编码问题呢?我们可以在test_str前和 '[\u4E00-\u9FA5]+' 前加一个 u ,看看结果会怎样。
# coding=utf-8
import re
test_str = u'When面对困难111, we 正面面对, yes, 加油!666'
comp = re.compile(u'[\u4E00-\u9FA5]+')
re_result = comp.findall(test_str)
print(re_result)
for s in re_result:
s = s.encode('gbk')
print(s)
运行结果:
[u'\u9762\u5bf9\u56f0\u96be', u'\u6b63\u9762\u9762\u5bf9', u'\u52a0\u6cb9']
面对困难
正面面对
加油
可以看到,现在我们匹配到的结果,已经不是上面与需求背道而驰的结果了。虽然是我们看不懂的Unicode编码字符,但是,最起码匹配到的内容是正确的,我们做一下解码就可以了。
注意:python中的print关键字是做“字符串格式化输出",它本身就会自动对我们得到的结果做处理,比如上面的结果我们不自己解码,print也会帮我们解码。
三、Python2中正确匹配中文
在上面的代码中,我们在字符串的前面加了一个 u ,表示字符串是Unicode编码的字符串,这样就完成了匹配中文的功能。
但是,在实际中,这样去拼接并不是一个优雅的方法(即使通过代码拼接)。
我们应该将字符串进行编码,编码之后的字符串就是Unicode字符串了(使用decode()或使用unicode())。
# coding=utf-8
import re
test_str = 'When面对困难111, we 正面面对, yes, 加油!666'
comp = re.compile(u'[\u4E00-\u9FA5]+')
# test_str = unicode(test_str, encoding='utf-8')
# re_result = comp.findall(test_str)
re_result = comp.findall(test_str.decode('utf-8'))
print(re_result)
for s in re_result:
s = s.encode('gbk')
print(s)
运行结果:
[u'\u9762\u5bf9\u56f0\u96be', u'\u6b63\u9762\u9762\u5bf9', u'\u52a0\u6cb9']
面对困难
正面面对
加油
注意:在编码的时候,应该使用utf-8,在解码的时候应该使用gbk,否则会乱码。
四、Python3和Python2兼容
上面我们分别完成了在Python3和Python2中匹配中文,这两种方式在Windows和Linux上的运行结果是一样的,所以说我们不用担心跨平台的问题,不管服务器是什么操作系统都可以兼容。
但是这两种方式都不能同时兼容Python3和Python2,如果要让我们的代码能够同时在两个版本的解释器中运行,只能通过分支判断的方式来实现了。
Python中可以使用sys.version来获取当前解释器的版本,我们这里可以通过版本来判断。下面是完整代码。
# coding=utf-8
import re
import sys
test_str = 'When面对困难111, we 正面面对, yes, 加油!666'
if sys.version[0] == '3':
comp = re.compile(r'[\u4E00-\u9FA5]+')
re_result = comp.findall(test_str)
else:
comp = re.compile(u"[\u4E00-\u9FA5]+")
re_result = comp.findall(test_str.decode('utf-8'))
re_result = [s.encode('gbk') for s in re_result]
print(re_result)
for a in re_result:
print(a)
运行结果:
# Python3
['面对困难', '正面面对', '加油']
面对困难
正面面对
加油
# Python2
['\xc3\xe6\xb6\xd4\xc0\xa7\xc4\xd1', '\xd5\xfd\xc3\xe6\xc3\xe6\xb6\xd4', '\xbc\xd3\xd3\xcd']
面对困难
正面面对
加油
现在我们可以同时在Python3和Python2中匹配中文了。
但是,在Python2中,当我们直接打印匹配结果的列表时,显示的并不是中文,遍历出来打印才显示中文。
这个问题在Python2中是避免不了的,如果我们要将结果打印成一个列表且显示中文的效果,可以用以下两种方式来实现。但是打印的数据类型已经转换成字符串了,不再是列表,只是数据的样子是列表的样子而已。
# coding=utf-8
import re
import sys
import json
test_str = 'When面对困难111, we 正面面对, yes, 加油!666'
if sys.version[0] == '3':
comp = re.compile(r'[\u4E00-\u9FA5]+')
re_result = comp.findall(test_str)
else:
comp = re.compile(u"[\u4E00-\u9FA5]+")
re_result = comp.findall(test_str.decode('utf-8'))
re_result = [s.encode('gbk') for s in re_result]
# re_result = repr(re_result).decode('string-escape') # 方法一
re_result = json.dumps(re_result, ensure_ascii=False) # 方法二
print(re_result)
print(type(re_result))
运行结果:
["面对困难", "正面面对", "加油"]
<type 'str'>