共计 2075 个字符,预计需要花费 6 分钟才能阅读完成。
上周,我的测试同事告诉我,你的用户名怎么还允许中文啊?当时我心里就想,你们测试肯定又搞错接口了,我用的是正则 w 过滤了参数,怎么可能出错,除非 Python 正则系统出错了,那是不可能的。本着严谨的作风,我自己先测试一下,没问题看我怎么怼回去。可是当我测试,我就懵逼了,中文真 TM 都验证通过,不对啊,我以前也是这么过滤参数的,测试没问题啊?唯一的区别是现在用的是 Python3。
上网搜了一圈,发现没有一篇文章讲述 Python2 和 Python3 的正则在处理字符串是的区别,都是一视同仁,知道我去翻了一遍官方文档,才明白怎么回事。
问题复现
我们都知道,Python 有个正则规则 \w
,几乎所有的网上博客文章都告诉你,这个规则匹配字母数字及下划线,但实际并不是这样:
有 Python2 代码如下:
~|⇒ pythonPython 2.7.10 (default, Aug 17 2018, 19:45:58)
[GCC 4.2.1 Compatible Apple LLVM 10.0.0 (clang-1000.0.42)] on darwinType "help", "copyright", "credits" or "license" for more information.>>> import re>>> aa = '捕蛇者说'>>> re.match('\w{1,20}', aa)>>> bb = 'abc123ADB'>>> re.match('\w{1,20}', bb)
<_sre.SRE_Match object at 0x1031b0b28>
我们可以看到,在 python2 中,\w
是无法匹配中文的。那么,同样的代码在 Python3 中运行结果是什么样子的了?
~|⇒ python3Python 3.7.1 (default, Nov 28 2018, 11:55:14)
[Clang 9.0.0 (clang-900.0.39.2)] on darwinType "help", "copyright", "credits" or "license" for more information.>>> import re>>> aa = '捕蛇者说'>>> re.match('\w{1,20}', aa)
<re.Match object; span=(0, 4), match='捕蛇者说'>>>> bb = 'abc123ADB'>>> re.match('\w{1,20}', bb)
<re.Match object; span=(0, 9), match='abc123ADB'>
但在 Python3 中 \w
是可以匹配中文的,这是怎么回事了?要回答这个问题,我们要回到 Python 官方文档中来寻找答案。
解决问题
当我们仔细阅读 Python 的官方文档时,就会发现,对于同样的正则规则\w
,Python2 和 Python3 区别好大,我们先来看看 Python2:
When the LOCALE and UNICODE flags are not specified, matches any alphanumeric character and the underscore; this is equivalent to the set [a-zA-Z0-9_]. With LOCALE, it will match the set [0-9_] plus whatever characters are defined as alphanumeric for the current locale. If UNICODE is set, this will match the characters [0-9_] plus whatever is classified as alphanumeric in the Unicode character properties database.
翻译一下:当没有设置 LOCALE(re.L)和 UNICODE(re.U)标志,匹配数字字母和下划线,如果设置了 LOCALE(re.L)则匹配数字下划线和 LOCALE 文字。如果设置了 UNICODE(re.U)标志,匹配数字下划线和 Unicode 字符集里的字符。
那么 Python3 了:
对于 Unicode (str) 样式:匹配 Unicode 词语的字符,包含了可以构成词语的绝大部分字符,也包括数字和下划线。如果设置了 ASCII 标志,就只匹配 [a-zA-Z0-9_]。对于 8 位 (bytes) 样式:匹配 ASCII 字符中的数字和字母和下划线,就是 [a-zA-Z0-9_]。如果设置了 LOCALE 标记,就匹配当前语言区域的数字和字母和下划线。
到此,我明白了,默认情况下,不设置任何标志,Python2 w 匹配 ASCII 字符集里的字符,包括数字字符和下划线,Python3 w 匹配数字下划线和 Unicode 字符集。所以,为了迁移方便,如果你想匹配 ASCII 字符集里的字符,指定标志为 re.A,如果你想匹配 Unicode 字符集里的字符,指定标志为 re.U。
总结
到此,我的问题是彻底解决了,但也有两个教训:
- 看网上的教程要多注意,特别是教程里的环境和自己环境的区别
- 多看官方文档
关于 Python2 和 Python3,还有很多区别,这里就不一一列举了,欢迎大家留言讨论。