python-字符串全解(六):正则表达式-预定义字符类 上一章我们讲了字符类这一章我们讲预定义字符类预定义字符类就是正则表达式提前规定了特殊组合的字符它代表的不是一个字符而且一类字符比如正数字母等等。3预定义字符预定义字符的形式有点像python的转义字符包含了数字(\d)非数字(\D)单词字符(\w)非单词字符(\W)空白(\s)非空白(\S)。从这些形式来看小写和大写表示了相反的字符集合。这些集合其实都可以用上一节提到的字符类替代但是这个更简洁你必须记住这些都代表哪一个字符组合。3.1数字(\d)正则表达式用\d来来代表数字用字符类表示就是[0-9]可以匹配从0到9的任意一个字符。我们简单的来看下面的例子。import re a a1ca2cabc a1 a\\dca2cabc a2 ra\dca2cabc # a3 a\dca2cabc SyntaxWarning: invalid escape sequence \d b re.findall(ra\dc, a) c re.findall(rb[0-2], a) d re.findall(ra\\dc, a1) e re.findall(a\\\\dc, a1) f re.findall(ra\\dc, a2) g re.findall(a\\\\dc, a2) print(a {}, a1 {}, a2 {}, b {}, c {}, d {}, e {}, f {}, g {}. format(a, a1, a2, b, c, d, e, f, g)) # 输出结果a a1ca2cabc, a1 a\dca2cabc, a2 a\dca2cabc, b [a1c, a2c], # c [], d [a\\dc], e [a\\dc], f [a\\dc], g [a\\dc]b是通过\d代表数字所以字符串中的a1c和a2c是符合正则的子串。c的效果是一样的只是c用的是字符类因为我们知道最大的数字是2所以我们用了[0-2]而不是0-9当然更为通用的写法是0-9匹配所有数字。a1和a2的字符串是一样的只是a1用\\转义代表\所以a1和a2中的字符是\和d两个字符。这里必须在强调一下python字符串中没有\d的转义如果你前面不加r直接写\d会报不存在的转义字符的警告所以我们需要写\\。a2是所有字符不转义所以直接一个\就可以了。d和e是正则表达式中如何匹配\d这两个字符因为\d代表数字所以需要将\写成\\代表\只表示反斜杠本身而不是预定义字符的开始。对于e我们用了四个\\\\这看起来有点晕。因为没有加r所以\\\\表示两个\\本身那么在正则表达式中两个反斜杠又表示\本身而不是预定义字符。这就和d的效果是一样的。这和我们在转义和非转义章节提到的一样首先正则表达式是一个字符串需要遵循python字符串的语法该转义的得先转义转义之后的字符串然后再应用正则表达式的语法明白了这一点你就没那么困惑了。f和g和de的正则表达式一样的只是字符串用的是a2a2只是不转义所以和a1的串表示是一样的。3.2非数字(\D)只需要将d变成大写D就表示了非数字。其对应的字符类是[^0-9]。我们简单看一个例子。import re a a1ca2cabc b re.findall(ra\Dc, a) c re.findall(ra[^0-9]c, a) print(a {}, b {}, c {}. format(a, b, c)) # 输出结果a a1ca2cabc, b [abc], c [abc]b只是将上一节中的\d换成了\D结果变成了abca和c中的是数字的子串没有得到匹配。c是同样的效果只是用字符类的表示。3.3单词字符(\w)前面的章节我们学过\b表示单词边界这个单词字符恰恰是相反的表示字母数字和下划线对应的等价的字符类表示为[a-zA-Z0-9_]表示从小写字母a到z大写字母A-Z数字0到9以及下划线_。我看一下下面的代码import re a a1ca_cabca¥c b re.findall(ra\wc, a) c re.findall(ra[a-zA-A0-9_]c, a) print(a {}, b {}, c {}. format(a, b, c)) # 输出结果a a1ca_cabca¥c, b [a1c, a_c, abc], # c [a1c, a_c, abc]a中的子串有个a¥c因为¥不属于\w所以结果并没有a¥c。c是与b等价的字符类形式。同样如果a字符串中如果有\w这两个字符正则表达式匹配需要用r\\w或者\\\\w原因在3.1的小节中已经讨论了这里不再赘述。3.4非单词字符(\W)和数字类型相似只需要将w改成大写的W就代表非单词字符这恰恰和\b表示的单词边界是一致的唯一的不同是\b并不占位。对应的字符类的写法是[^a-zA-Z0-9_]我们来看下下面的代码import re a a1ca_cabca¥c b re.findall(ra\Wc, a) c re.findall(ra[^a-zA-A0-9_]c, a) d re.findall(ra\b.c, a) print(a {}, b {}, c {}, d {}. format(a, b, c, d)) # 输出结果a a1ca_cabca¥c, b [a¥c], c [a¥c], # d [a¥c]b中我们将\w换成了\W最后的结果变成了a¥c这符合我们的预期。c是字符类的等价写法。d我们运用了\b加通配符.实现了同样的效果。这个例子印证了\b不是用来占位的他只是告诉字符a之后的字符是单词的边界那么根据单词边界的定义后面字符不不能是字母数字或下划线。我们用点号来匹配这个¥后面跟c达到了同样的效果。通过这个例子我相信你对\b的用法会有更近一步的了解。3.5空白(\s)所谓空白不单单的指空格还包括了水平制表符(\t)垂直制表符(\v)换页(\f)换行(\n)回车(\r)其对应的字符类为[ \t\v\f\n\r]s在这里是space的意思不能认为是字符串格式化表达式中的\s注意这个集合里面有六个字符第一个是空格。我们来看一些例子import re a |a\nca\tcabca¥ca c| b re.findall(ra\sc, a) c re.findall(ra[ \t\v\f\r\n]c, a) print(a {}, b {}, c {}. format(a, b, c)) # 输出结果a |a # ca cabca¥ca c|, b [a\nc, a\tc, a c], # c [a\nc, a\tc, a c]a字符串中包含了a\nca\tc以及a c(这个中间是空格)这三个都属于空白字符所以被匹配如b所示。c是等价的字符类表示方式。3.6非空白(\S)同样的只需要将小写的s改成大写的S就就可以表示相反的意思。对应字符类的表示为[^ \t\v\f\r\n]我们看下面的代码import re a |a\nca\tcabca¥ca c| b re.findall(ra\Sc, a) c re.findall(ra[^ \t\v\f\r\n]c, a) print(a {}, b {}, c {}. format(a, b, c)) # 输出结果a |a # ca cabca¥ca c|, b [abc, a¥c], # c [abc, a¥c]从结果来看abc和a¥c中间的字符都不是空白所以这两个子串被匹配。3.6字符类中嵌套预定义字符从上面可以看出用预定义字符可以比用字符类更简洁。假如我们既想匹配数字又想匹配空白字符呢上一节我们讲了字符类在字符类中也可以嵌套预定义字符我们把\d和\s一起放入中括号中就像使用普通字符那样就可以达到同时匹配数字和空白字符的效果。如下代码所示import re a |a\nca\tca1ca¥ca c| b re.findall(ra[\s\d]c, a) c re.findall(ra[0-9 \t\v\f\r\n]c, a) print(a {}, b {}, c {}. format(a, b, c)) # 输出结果a |a # ca ca1ca¥ca c|, b [a\nc, a\tc, a1c, a c], # c [a\nc, a\tc, a1c, a c]b匹配了a和c之间是数字或者空白的子串c用非预定义字符表示。同样如果需要表示非数字和空白只需要加上^就可以。如下所示import re a |a\nca\tca1ca¥ca c| b re.findall(ra[^\s\d]c, a) c re.findall(ra[^0-9 \t\v\f\r\n]c, a) print(a {}, b {}, c {}. format(a, b, c)) # 输出结果a |a # ca ca1ca¥ca c|, b [a¥c], c [a¥c]字符串a中字符a和c之间不是数字的子串只有a¥c符合。讲到这里预定义字符我们就讲完了。上面代码我们都没涉及到字符出现两次或者多次的时候假如有一个字符串“a112345678ca123c我们想匹配中间是多个不确定长度的数字我们现在所学的就无能为力了。这就引出下一章我们要讲的内容量词通过两次我们可以表示各种重复的场景从而解决重复字符的问题。