正则表达式、re、常用模块
阅读目录
正则表达式本身也和python没有什么关系,就是匹配字符串内容的一种规则。
官方定义:正则表达式是对字符串操作的一种逻辑公式,就是用事先定义好的一些特定字符、及这些特定字符的组合,组成一个“规则字符串”,这个“规则字符串”用来表达对字符串的一种过滤逻辑。
正则表达式
一说规则我已经知道你很晕了,现在就让我们先来看一些实际的应用。在线测试工具 http://tool.chinaz.com/regex/
首先你要知道的是,谈到正则,就只和字符串相关了。在我给你提供的工具中,你输入的每一个字都是一个字符串。
其次,如果在一个位置的一个值,不会出现什么变化,那么是不需要规则的。
比如你要用"1"去匹配"1",或者用"2"去匹配"2",直接就可以匹配上。这连python的字符串操作都可以轻松做到。
那么在之后我们更多要考虑的是在同一个位置上可以出现的字符的范围。
字符组 : [字符组] 在同一个位置可能出现的各种字符组成了一个字符组,在正则表达式中用[]表示 字符分为很多类,比如数字、字母、标点等等。 假如你现在要求一个位置"只能出现一个数字",那么这个位置上的字符只能是0、1、2...9这10个数之一。
正则 |
待匹配字符 |
匹配 |
说明 |
[0123456789] |
8 |
True |
在一个字符组里枚举合法的所有字符,字符组里的任意一个字符 |
[0123456789] |
a |
False |
由于字符组中没有"a"字符,所以不能匹配 |
[0-9] |
7 |
True |
也可以用-表示范围,[0-9]就和[0123456789]是一个意思 |
[a-z] |
s |
True |
同样的如果要匹配所有的小写字母,直接用[a-z]就可以表示 |
[A-Z] |
B |
True |
[A-Z]就表示所有的大写字母 |
[0-9][a-f][A-F] |
e |
True |
可以匹配数字,大小写形式的a~f,用来验证十六进制字符 |
元字符 |
匹配内容 |
. | 匹配除换行符以外的任意字符 |
\w | 匹配字母或数字或下划线或汉字 |
\s | 匹配任意的空白符 |
\d | 匹配数字 |
\n | 匹配一个换行符 |
\t | 匹配一个制表符 |
\b | 匹配一个单词的结尾 |
^ | 匹配字符串的开始 |
$ | 匹配字符串的结 |
\W |
匹配非字母或数字或下划线或汉字 |
\D |
匹配非空白符 |
\S |
匹配非数字 |
a|b |
匹配字符a或字符b |
() |
匹配括号内的表达式,也表示一个组 |
[...] |
匹配字符组中的字符 |
[^...] |
匹配除了字符组中字符的所有字符 |
量词 |
用法说明 |
* | 重复零次或更多次 |
+ | 重复一次或更多次 |
? | 重复零次或一次 |
{n} | 重复n次 |
{n,} | 重复n次或更多次 |
{n,m | 重复n到m次 |
. ^ $
正则 | 待匹配字符 | 匹配 结果 |
说明 |
海. | 海燕海娇海东 | 海燕海娇海东 | 匹配所有"海."的字符 |
^海. | 海燕海娇海东 | 海燕 | 只从开头匹配"海." |
海.$ | 海燕海娇海东 | 海东 | 只匹配结尾的"海.$" |
* + ? { }
正则 | 待匹配字符 | 匹配 结果 |
说明 |
李.? | 李杰和李莲英和李二棍子 |
李杰 |
?表示重复零次或一次,即只匹配"李"后面一个任意字符 |
李.* | 李杰和李莲英和李二棍子 | 李杰和李莲英和李二棍子 |
*表示重复零次或多次,即匹配"李"后面0或多个任意字符 |
李.+ | 李杰和李莲英和李二棍子 | 李杰和李莲英和李二棍子 |
+表示重复一次或多次,即只匹配"李"后面1个或多个任意字符 |
李.{1,2} | 李杰和李莲英和李二棍子 |
李杰和 |
{1,2}匹配1到2次任意字符 |
注意:前面的*,+,?等都是贪婪匹配,也就是尽可能匹配,后面加?号使其变成惰性匹配
正则 | 待匹配字符 | 匹配 结果 |
说明 |
李.*? | 李杰和李莲英和李二棍子 | 李杰 李莲 李二 |
惰性匹配 |
字符集[][^]
正则 | 待匹配字符 | 匹配 结果 |
说明 |
李[杰莲英二棍子]* | 李杰和李莲英和李二棍子 |
李杰 |
表示匹配"李"字后面[杰莲英二棍子]的字符任意次 |
李[^和]* | 李杰和李莲英和李二棍子 |
李杰 |
表示匹配一个不是"和"的字符任意次 |
[\d] | 456bdha3 |
4 |
表示匹配任意一个数字,匹配到4个结果 |
[\d]+ | 456bdha3 |
456 |
表示匹配任意个数字,匹配到2个结果 |
分组 ()与 或 |[^]
身份证号码是一个长度为15或18个字符的字符串,如果是15位则全部🈶️数字组成,首位不能为0;如果是18位,则前17位全部是数字,末位可能是数字或x,下面我们尝试用正则来表示:
正则 | 待匹配字符 | 匹配 结果 |
说明 |
^[1-9]\d{13,16}[0-9x]$ | 110101198001017032 |
110101198001017032 |
表示可以匹配一个正确的身份证号 |
^[1-9]\d{13,16}[0-9x]$ | 1101011980010170 |
1101011980010170 |
表示也可以匹配这串数字,但这并不是一个正确的身份证号码,它是一个16位的数字 |
^[1-9]\d{14}(\d{2}[0-9x])?$ | 1101011980010170 |
False |
现在不会匹配错误的身份证号了 |
^([1-9]\d{16}[0-9x]|[1-9]\d{14})$ | 110105199812067023 |
110105199812067023 |
表示先匹配[1-9]\d{16}[0-9x]如果没有匹配上就匹配[1-9]\d{14} |
转义符 \
在正则表达式中,有很多有特殊意义的是元字符,比如\d和\s等,如果要在正则中匹配正常的"\d"而不是"数字"就需要对"\"进行转义,变成'\\'。
在python中,无论是正则表达式,还是待匹配的内容,都是以字符串的形式出现的,在字符串中\也有特殊的含义,本身还需要转义。所以如果匹配一次"\d",字符串中要写成'\\d',那么正则里就要写成"\\\\d",这样就太麻烦了。这个时候我们就用到了r'\d'这个概念,此时的正则是r'\\d'就可以了。
正则 | 待匹配字符 | 匹配 结果 |
说明 |
\d | \d | False |
因为在正则表达式中\是有特殊意义的字符,所以要匹配\d本身,用表达式\d无法匹配 |
\\d | \d | True |
转义\之后变成\\,即可匹配 |
"\\\\d" | '\\d' | True |
如果在python中,字符串中的'\'也需要转义,所以每一个字符串'\'又需要转义一次 |
r'\\d' | r'\d' | True |
在字符串之前加r,让整个字符串不转义 |
贪婪匹配
贪婪匹配:在满足匹配时,匹配尽可能长的字符串,默认情况下,采用贪婪匹配
正则 | 待匹配字符 | 匹配 结果 |
说明 |
<.*> |
<script>...<script> |
<script>...<script> |
默认为贪婪匹配模式,会匹配尽量长的字符串 |
<.*?> | r'\d' |
<script> |
加上?为将贪婪匹配模式转为非贪婪匹配模式,会匹配尽量短的字符串 |
几个常用的非贪婪匹配Pattern
*? 重复任意次,但尽可能少重复 +? 重复1次或更多次,但尽可能少重复 ?? 重复0次或1次,但尽可能少重复 {n,m}? 重复n到m次,但尽可能少重复 {n,}? 重复n次以上,但尽可能少重复
.*?的用法
. 是任意字符 * 是取 0 至 无限长度 ? 是非贪婪模式。 何在一起就是 取尽量少的任意字符,一般不会这么单独写,他大多用在: .*?x 就是取前面任意长度的字符,直到一个x出现
re模块下的常用方法
import re ret = re.findall('a', 'eva egon yuan') # 返回所有满足匹配条件的结果,放在列表里 print(ret) #结果 : ['a', 'a'] ret = re.search('a', 'eva egon yuan').group() print(ret) #结果 : 'a' # 函数会在字符串内查找模式匹配,只到找到第一个匹配然后返回一个包含匹配信息的对象,该对象可以 # 通过调用group()方法得到匹配的字符串,如果字符串没有匹配,则返回None。 ret = re.match('a', 'abc').group() # 同search,不过尽在字符串开始处进行匹配 print(ret) #结果 : 'a' ret = re.split('[ab]', 'abcd') # 先按'a'分割得到''和'bcd',在对''和'bcd'分别按'b'分割 print(ret) # ['', '', 'cd'] ret = re.sub('\d', 'H', 'eva3egon4yuan4', 1)#将数字替换成'H',参数1表示只替换1个 print(ret) #evaHegon4yuan4 ret = re.subn('\d', 'H', 'eva3egon4yuan4')#将数字替换成'H',返回元组(替换的结果,替换了多少次) print(ret) obj = re.compile('\d{3}') #将正则表达式编译成为一个 正则表达式对象,规则要匹配的是3个数字 ret = obj.search('abc123eeee') #正则表达式对象调用search,参数为待匹配的字符串 print(ret.group()) #结果 : 123 import re ret = re.finditer('\d', 'ds3sy4784a') #finditer返回一个存放匹配结果的迭代器 print(ret) # <callable_iterator object at 0x10195f940> print(next(ret).group()) #查看第一个结果 print(next(ret).group()) #查看第二个结果 print([i.group() for i in ret]) #查看剩余的左右结果
注意:
1 findall的优先级查询:
import re ret = re.findall('www.(baidu|oldboy).com', 'www.oldboy.com') print(ret) # ['oldboy'] 这是因为findall会优先把匹配结果组里内容返回,如果想要匹配结果,取消权限即可 ret = re.findall('www.(?:baidu|oldboy).com', 'www.oldboy.com') print(ret) # ['www.oldboy.com']
2 split的优先级查询
ret=re.split("\d+","eva3egon4yuan") print(ret) #结果 : ['eva', 'egon', 'yuan'] ret=re.split("(\d+)","eva3egon4yuan") print(ret) #结果 : ['eva', '3', 'egon', '4', 'yuan'] #在匹配部分加上()之后所切出的结果是不同的, #没有()的没有保留所匹配的项,但是有()的却能够保留了匹配的项, #这个在某些需要保留匹配部分的使用过程是非常重要的。
总结:
正则是用一些具有特殊含义的符号组合在一起来描述字符或者字符串的方法,这些特殊符号组合在一起称为正则表达式,正则说白了就是一种规则。
正则本质上是一种小型的编程语言,它内嵌在Python中并通过re模块实现
1.元字符
在正则表达式中一共有11个元字符:
. ^ $ * + ? {} [] () \ |
1) . 代表除换行符之外的任意一个符号,(如果想代表换行符也有办法,就是在方法的参数中,更改模式为re.S)
2)^ 代表以什么开头
3) $ 代表以什么结尾,如果^ . $就表示从头匹配到尾,完全匹配
4) * + ? {}
这几个都表示重复,
*表示前面的字符重复零到无穷次
+表示前面的字符重复一到无穷次
? 表示前面的字符重复零或一次
{} 大括号里可以指定数字,表示重复多少次,或者指定一个范围,表示重复这个范围的次数
ret=re.findall('12{1,3}','12222222') print(ret) #['1222'] 默认贪婪匹配
5) 字符集[] 只匹配字符集中的一个符号,是或者的关系。
需注意的是:元字符在字符集里并没有特殊功能
在字符集中有功能的符号只有三个:- ^ \
- 代表范围,0-9,a-z,A-Z
^ 写在字符集开头代表取反,‘非’的意思,注意非的是字符集中的所有字符
ret=re.findall('[^abc]','abdcdefg') print(ret) #['d', 'd', 'e', 'f', 'g']
\代表转义
6) 管道符 | 也是或者的意思,与字符集的区别在于,管道符不止匹配一个
如果A和B是正则表达式,A|B将匹配任何匹配了A或者B的字符串。| 的优先级非常低,是为了当你有多字符要选择时能适当的运行。举个例子来说明管道符与字符集的区别:
#如果要匹配Python和Jython ret=re.findall('[PJ]ython','Python,Jython') #但是如果要匹配python和perl呢 ret=re.findall('p(?:ython|erl)','python,perl')
7) 转义符 \
1.转义符后边跟元字符去除特殊功能,如\.就表示匹配.
2.抓义符后边跟一些普通字符实现特殊功能,如\d表示任意十进制数
\d 匹配任何十进制数; 它相当于类 [0-9]。
\D 匹配任何非数字字符; 它相当于类 [^0-9]。
\s 匹配任何空白字符; 它相当于类 [ \t\n\r\f\v]。
\S 匹配任何非空白字符; 它相当于类 [^ \t\n\r\f\v]。
\W 匹配任何非字母数字字符; 它相当于类 [^a-zA-Z0-9_]
\b 匹配一个特殊字符边界,比如空格 ,&,#等
\A 匹配字符串开始
\z 匹配字符串结束
\Z 匹配字符串结束,如果存在换行,只匹配到换行前的结束字符
\G 匹配最后匹配完成的位置
\n 匹配一个换行符
\t 匹配一个制表符
注意:当我们需要匹配\ 时需要在规则中写四个\,因为我们写的正则表达式,首先要由Python解释器先翻译,再由re模块解释,如果在规则外面加一个r,表示原生字符串,就跳过了Python解释器翻译的过程
8) 分组()表示把括号里的表达式当成一个整体处理
在分组的时候可以为分组命名,称作有名分组
ret=re.search(r'(?P<first_mul>-?\d+\.?\d*)(?:\*|\/)(?P<second_mul>-?\d+\.?\d*)', s)
在findall方法中,分组具有优先级,如果要取消优先级,就要在规则前加一个?:
ret=re.findall('p(ython|erl)','python,perl') print(ret) #['ython', 'erl'] ret=re.findall('p(?:ython|erl)','python,perl') print(ret) #['python', 'perl']
2.贪婪匹配
贪婪匹配是指在满足匹配条件时,匹配尽可能长的字符串,默认情况下就是贪婪匹配,. * + {} 默认情况下都是贪婪匹配
s='<div>zhang<img></div><a href=''></div>' ret=re.findall('<div>.*</div>',s) print(ret) #['<div>zhang<img></div><a href=></div>']
非贪婪匹配的话要在非贪婪的规则后面加一个问号?
s='<div>zhang<img></div><a href=''></div>' ret=re.findall('<div>.*?</div>',s) print(ret) #['<div>zhang<img></div>']
.*? 经常会在爬虫中应用到,.*?x就是匹配任意长度的字符,直到一个x出现
1) re.fidall() 方法
匹配所有满足条件的结果,并将结果放在列表中,没有匹配到的话返回空列表
ret=re.findall('[PJ]ython','Python,Jython') print(ret) #['Python', 'Jython']
re.search() 方法
只匹配到第一个满足条件的结果,并返回一个包含匹配结果的对象
可以通过.group() 的方法得到匹配到的字符串,没有匹配到的话返回None
ret=re.search('[PJ]ython','Python,Jython') print(ret.group()) #Python ret=re.search('[ab]ython','Python,Jython') print(ret) #None print(ret.group()) #报错AttributeError: 'NoneType' object has no attribute 'group'
前面提过的有名分组,可以通过.group(组名) 的方式,只提取指定组名的信息
s='3*2' ret=re.search(r'(?P<first_mul>-?\d+\.?\d*)(?:\*|\/)(?P<second_mul>-?\d+\.?\d*)', s) print(ret.group('first_mul')) #3 print(ret.group('second_mul')) #2
re.match() 方法
和search方法差不多,不同处是match方法只在字符串开头的地方进行匹配
re.split() 分割,与字符串中的分割方法只能指定一个确定的分割符不同,正则中的分割可以用正则表达式作为分隔符
s='hello124124world3235hi' ret=re.split('\d+',s) print(ret) #['hello', 'world', 'hi']
还可以指定最大分割次数
s='hello124124world3235hi' ret=re.split('\d+',s,1) print(ret) #['hello', 'world3235hi']
还可以显示分割符
s='hello124124world3235hi' ret=re.split('(\d+)',s,1) print(ret) #['hello', '124124', 'world3235hi']
re.sub() 替换
ret=re.sub('w.{2,3}d','everyone','hello wasd') print(ret) #hello everyone
re.subn() 替换并返回替换次数
ret=re.subn('w.{2,3}d','everyone','hello wasd') print(ret) #('hello everyone', 1)
re.compile() 编译,就是把一个规则变成一个对象,如果频繁使用这个规则的话,就不用每次都在re里面写规则了
obj=re.compile('\d+') ret=obj.findall('agasgagr4t24aga43') print(ret) #['4', '24', '43']
re.finditer() 返回的是一个迭代器对象,这个方法是为了处理那种如果返回的内容很多,都放在列表中会很占内存,这时就可以把匹配到的结果放到一个迭代器对象中,什么时候用,什么时候取
ret=re.finditer('\d+','asg67gas6gasd58gasg69asg58asg96g9as6ga78') print(ret) #<callable_iterator object at 0x000001D415E3B240> print(next(ret).group()) #67 print(next(ret).group()) #6 print(next(ret).group()) #58
模块的导入和使用
模块的导入应该在程序开始的地方
更多相关内容 http://www.cnblogs.com/Eva-J/articles/7292109.html
collections模块
在内置数据类型(dict、list、set、tuple)的基础上,collections模块还提供了几个额外的数据类型:Counter、deque、defaultdict、namedtuple和OrderedDict等。
1.namedtuple: 生成可以使用名字来访问元素内容的tuple
2.deque: 双端队列,可以快速的从另外一侧追加和推出对象
3.Counter: 计数器,主要用来计数
4.OrderedDict: 有序字典
5.defaultdict: 带有默认值的字典
namedtuple
我们知道tuple
可以表示不变集合,例如,一个点的二维坐标就可以表示成:
>>> p = (1, 2)
但是,看到(1, 2),很难看出这个tuple是用来表示一个坐标的。
这时,namedtuple
就派上了用场:
>>> from collections import namedtuple >>> Point = namedtuple('Point', ['x', 'y']) >>> p = Point(1, 2) >>> p.x >>> p.y
类似的,如果要用坐标和半径表示一个圆,也可以用namedtuple
定义:
#namedtuple('名称', [属性list]): Circle = namedtuple('Circle', ['x', 'y', 'r'])
deque
使用list存储数据时,按索引访问元素很快,但是插入和删除元素就很慢了,因为list是线性存储,数据量大的时候,插入和删除效率很低。
deque是为了高效实现插入和删除操作的双向列表,适合用于队列和栈:
>>> from collections import deque >>> q = deque(['a', 'b', 'c']) >>> q.append('x') >>> q.appendleft('y') >>> q deque(['y', 'a', 'b', 'c', 'x'])
deque除了实现list的append()
和pop()
外,还支持appendleft()
和popleft()
,这样就可以非常高效地往头部添加或删除元素。
OrderedDict
使用dict时,Key是无序的。在对dict做迭代时,我们无法确定Key的顺序。
如果要保持Key的顺序,可以用OrderedDict
:
>>> from collections import OrderedDict >>> d = dict([('a', 1), ('b', 2), ('c', 3)]) >>> d # dict的Key是无序的 {'a': 1, 'c': 3, 'b': 2} >>> od = OrderedDict([('a', 1), ('b', 2), ('c', 3)]) >>> od # OrderedDict的Key是有序的 OrderedDict([('a', 1), ('b', 2), ('c', 3)])
注意,OrderedDict
的Key会按照插入的顺序排列,不是Key本身排序:
>>> od = OrderedDict() >>> od['z'] = 1 >>> od['y'] = 2 >>> od['x'] = 3 >>> od.keys() # 按照插入的Key的顺序返回 ['z', 'y', 'x']
defaultdict
有如下值集合 [
11
,
22
,
33
,
44
,
55
,
66
,
77
,
88
,
99
,
90.
..],将所有大于
66
的值保存至字典的第一个key中,将小于
66
的值保存至第二个key的值中。
即: {
'k1'
: 大于
66
,
'k2'
: 小于
66
}
>>> od = OrderedDict() >>> od['z'] = 1 >>> od['y'] = 2 >>> od['x'] = 3 >>> od.keys() # 按照插入的Key的顺序返回 ['z', 'y', 'x'] defaultdict 有如下值集合 [11,22,33,44,55,66,77,88,99,90...],将所有大于 66 的值保存至字典的第一个key中,将小于 66 的值保存至第二个key的值中。 即: {'k1': 大于66 , 'k2': 小于66}
from collections import defaultdict values = [11, 22, 33,44,55,66,77,88,99,90] my_dict = defaultdict(list) for value in values: if value>66: my_dict['k1'].append(value) else: my_dict['k2'].append(value) #defaultdict字典解决方法
使用dict
时,如果引用的Key不存在,就会抛出KeyError
。如果希望key不存在时,返回一个默认值,就可以用defaultdict
:
>>> from collections import defaultdict >>> dd = defaultdict(lambda: 'N/A') >>> dd['key1'] = 'abc' >>> dd['key1'] # key1存在 'abc' >>> dd['key2'] # key2不存在,返回默认值 'N/A' #例2
Counter
Counter类的目的是用来跟踪值出现的次数。它是一个无序的容器类型,以字典的键值对形式存储,其中元素作为key,其计数作为value。计数值可以是任意的Interger(包括0和负数)。Counter类和其他语言的bags或multisets很相似。
c = Counter('abcdeabcdabcaba') print c 输出:Counter({'a': 5, 'b': 4, 'c': 3, 'd': 2, 'e': 1})
其他详细内容 http://www.cnblogs.com/Eva-J/articles/7291842.html