python re正则查找_python正则表达式 - re

1,匹配符号

基本元字符

. : 任意字符,除了\n,flags设置为DOTALL(S)可以让.匹配\n

|:逻辑或

\:转义

():捕获组

空白字符

[\b] : 回退

\f : 换页

\n : 换行

\r : 回车

\t : 制表tab

\v : 垂直制表

特定字符

\d : 数字,等价[0-9]

\D : 非数字,等价[^0-9]

\w : 字母或数字或_,等价[A-Za-z0-9_]

\W : 非字母非数字非_,等价[^A-Za-z0-9]

\s : 空白字符,等价[\f\n\r\t\v]

\S : 非空白字符,等价[^\f\n\r\t\v]

重复匹配

+ : 一个或多个

* : 零个或多个

? : 零个或一个

{n} : n次

{n,} : 最少n次

{n,m} : 最少n次,最多m次

备注:+和*是贪婪型,有多少匹配多少,加上?可以变懒惰型

位置匹配

\b : 单词边界,即\w和\W之间的位置

\B : 非单词边界

^ : 字符串开头,flags设置为MULTILINE(M)可以匹配\n后的位置

$ : 字符串结尾,flags设置为MULTILINE(M)可以匹配\n前的位置

\A:字符串开头

\Z:字符串结尾

?=:向前查找

?<=:向后查找

2,字符集

字符集[]的规范/元字符不同于正则式主体

字符集中的特有元字符:

-:连字符,表示范围,如果不想表示范围必须放最前面

^:对字符集取反,作用于给定字符集内所有的字符或范围,而不是仅限于^后面的一个字符或区间

正则表达式主体中的元字符,在字符集中无需转义的(转义了也可以,pycharm提示多余的):

(

)

.

*

?

匹配举例:

[0-9] : 数字

[A-Z] : 大写字母

[a-z] : 小写字母

[A-Za-z0-9] : 字母或者数字

[^0-9] : 非数字

[.(] : .或(,[]字符集合内的元字符无需转义

[-.\s]:-或.或空白字符,"-"如果不想表示范围必须放最前面

[\n\d\s]: 匹配换行符或数字或空白

备注:如果想通过字符集[]匹配转义符\,则正则表达式引擎必须接受字符\\:

print('a[\]b') # a[\]b

print('a[\\]b') # a[\]b,和单\结果一样,因为单\会自动转义,详见后面的转移章节说明

print(r'a[\\]b') # a[\\]b,OK!

3,匹配flags

MULTILINE(M):改变“^”和“$”,让“^”能够匹配到换行符后的位置,让“$”能够匹配到换行符前的位置

DOTALL(S):改变“.”,让“.”能够匹配换行符

IGNORECASE(I):忽略大小写

LOCALE(L)

UNICODE(U)

VERBOSE(X)

ASCII(A)

re.M可以使得^和$匹配'\n'后和'\n'前得位置,例如:

用'\n'也可以匹配结果,但是'\n'不是位置匹配,会把'\n'也匹配在内。

>>> s = 'aaa\nbbb\nccc'

>>> r = re.search('^bbb$', s)

>>> r # 匹配不到

>>> r = re.search('^bbb$', s, re.M)

>>> r.group()

'bbb'

>>> r = re.search('\nbbb\n', s, re.M)

>>> r.group()

'\nbbb\n'

总结:经过re.M改造后的^和$,相当于是向前查找和向后查找结合了‘\n’,例如:

s = 'start\nhello guxh2\nend'

print(s)

"""

start

hello guxh2

end

"""

print(repr(s))

'start\nhello guxh2\nend'

>>> re.findall(r'^hello .*?$', s, re.M)

['hello guxh2']

>>> re.findall(r'(?<=\n)hello .*?(?=\n)', s)

['hello guxh2']

>>> re.findall(r'\nhello .*?\n', s) # 不加定界会匹配上\n

re.findall(r'\nhello .*?\n', s)

4,匹配模式

整体匹配(只匹配第一次):re.search / re.match

全局匹配(匹配所有):re.findall

分组捕获:(),整体匹配和全局匹配都可以进行分组捕获,捕获匹配结果中的部分内容,分组捕获可以嵌套,详见匹配举例。分组可以嵌套,匹配结果会含大分组和子分组的结果。

5,匹配写法

方法一,需要重复执行匹配的,先编译再匹配

好处是多个地方用到这个匹配模式regex,想修改时只需要修改一个地方

regex = re.compile(p)

regex.search(s)

re.search(regex, s)

方法二,简单的可以直接匹配

re.search(p, s)

6,python提供的匹配方法

1)re.search(p, s)

从s中提取完全符合p的内容,只提取第一次命中的,返回re.match对象

提取整体匹配结果:searchObj.group(), searchObj.group(0)

提取所有分组捕获结果:searchObj.groups()

提取单个分组捕获结果:searchObj.group(n), 即groups()[i] = group(i+1)

2)re.findall(p, s)

提取所有符合p的内容,返回字符串组成的列表

使用分组捕获时,返回分组捕获结果(元组)组成的列表

re.search使用分组捕获时既可以捕获整体匹配,也可以捕获分组内容

re.findall使用分组捕获时只能捕获分组内容

3)re.match(p, s)

可以用re.search(^)替代

MULTILINE模式下,match也只匹配s,但re.search可以匹配换行后的开始

4)re.finditer(p, s)

re.findall的惰性版,返回iterator

5)re.split(p, s)

根据p分割字符串s

6)re.sub(p, repl, s, count=n)

p是匹配到的值,repl是替换后值,s是m目标字符串,count是替换多少次

re.subn(p, repl)可以返回替换次数

7,懒惰匹配和贪婪匹配

*和+都是贪婪比配,尽可能多

可以结合?变成懒惰匹配

>>>re.search('a.*?b', 'abab').group()

ab

>>>re.search('a.*?b', 'abab').group()

abab

>>>re.search('192\.168\.1\..*$', '192.168.1.12').group()

192.168.1.12

>>>re.search('192\.168\.1\..*?', '192.168.1.12').group() # 没有结束符,懒惰不匹配

192.168.1.

>>>re.search('192\.168\.1\..*', '192.168.1.12').group() # 没有结束符,贪婪匹配至最后

192.168.1.12

8,转义

1)python语言的转义

'\n'在python中代表换行,print输出时会做特殊处理:

s = 'x\nz'

print(s) # x(换行)z

print(repr(s)) # 'x\nz'

要想print输出'x\nz',需要对'\'进行转义:

s = 'x\\nz' # 或s = r'x\nz'

print(s) # x\nz

print(repr(s)) # 'x\\nz'

如果'\' + '字符'没有特殊含义,python语言会自动对\进行转义:

s = 'x\yz'

print(s) # x\yz

print(repr(s)) # 'x\\yz',发现多了个'\'

自动转义时,PEP8会提示invalid escape sequence '\y',看来PEP8要求'\'+'字符'没有特殊含义时,统一做好转义!

2)正则表达式的转义 - 非元字符

传递给正则引擎的字符串regex的内容,是print(regex)的结果,而不是print(repr(s))的结果

假设s = 'x\yz',print(s)的结果是'x\yz',print(repr(s))的结果是'x\\yz',传递给正则表达式的是'x\yz'

匹配时,也是匹配对象字符串s的print(s)的结果

和编程语言的转义一样,正则引擎也有自己的转义规则,当'\' + '字符'属于元字符时,可以进行特殊处理,例如\w匹配字符,数字,以及'_'

但是如果'\' + '字符'不是元字符,即没有特殊含义时,正则引擎不会像编程语言那样自动进行转义,而是要求必须接收转义后的'\',例如:

s = r'x\yz'

regex = 'x\\yz' # 或者regex = 'x\yz',print(regex)都是'x\yz',传递给正则引擎的就是'x\yz'

re.search(regex, s) # re.error: bad escape \y at position 1

这样的字符串传递给正则引擎才能得到正确处理:

regex = r'x\\yz' # print(regex)是'x\\yz',print(repr(regex))是'x\\\\yz'

regex = 'x\\\yz' # 同上

regex = 'x\\\\yz' # 同上

为什么'x\\\yz'和'x\\\\yz'结果一样呢?因为对于'\\\y',前2个'\\'转义为'\',后面的'\y',python语言自动转义成了'\y'(同样也会收到PEP8提示)

3)正则表达式的转义 - 元字符

假设要匹配的字符串为'a(换行)b',即:

s = 'a\nb'

传递给正则表达式的字符串regex,经过转义其print(regex)结果应当为'a\nb',于是匹配写法为:

regex = r'a\nb' # 或者regex = 'a\\nb'

re.search(regex, s) # 可以匹配中s

但是发现未经转义的'a\nb'也能匹配中(换成制表符'\t'也能匹配):

regex = 'a\nb'

re.search(regex, s) # 也能匹配中

这是因为给字符串传递'a(换行)b'时,字符串恰好会将其识别为'\n',交给正则引擎时就能够正确去匹配'a(换行)b'

但是如果python语言字符串和正则引擎对字符的解释不一样时,转义或者不转义效果就会不同,例如:

s = 'a*'

regex1 = 'a\x2A'

regex2 = 'a\\x2A'

re.search(regex1, s) # 匹配结果为a

re.search(regex2, s) # 匹配结果为a*

对于'\x2A'(即*),正则表达式解释为匹配前一个字符0次,1次或多次。

如果和\n一样,则不管是'a\x2A'还是'a\\x2A'都能匹配中'a*'

但实际上,不转义'\'就无法匹配'a*',那是因为不转义的'*'在正则表达式中是元字符,被赋予了其他含义。而'*'在python语言字符串中却没有特殊含义。

9,捕获组

1)捕获组对现有匹配方法对影响

捕获组对re.findall的影响:

s = 'abcabcabc'

re.findall('abc', s) # ['abc', 'abc', 'abc']

re.findall('a(b)c', s) # ['b', 'b', 'c']

re.findall('(a(b)c)') # [('abc', 'b'), ('abc', 'b'), ('abc', 'b')]

捕获组对re.sub的影响:

s = 'abcabcabc'

re.sub('abc', 'x', s) # xxx

re.sub('a(b)c', 'x', s) # xxx

re.sub('(a(b)c)', 'x', s) # xxx

备注:看上去对sub不起作用,但捕获组可以用来回溯引用

2)捕获组的回溯引用

s = '

hello world

'

re.search(r'.*?', s) # 可以匹配s

re.findall(r'.*?', s) # ['h1']

re.sub(re.sub(r'(.*?)', r'\1,\2', s)) # h1, hello world,re.sub会对匹配中的整个字符串进行替换

3)捕获组的命名

>>>ID = "310115199012128765"

>>>p = "(?P[0-9]{6})(?P[0-9]{8})"

>>>re.search(p, ID).groupdict()

{'dictrict': '310115', 'birthday': '19901212'}

10,前后查找

前后查找属于位置匹配

(?<=s):向后查找,s后面的位置

(?=s):向前查找,s前面的位置

>>>s = 'ab1c ab2c\nab3c'

>>>re.findall('(a.*?)(?:\s|$)', s) # $不能放字符集[]里面

['ab1c', 'ab2c', 'ab3c']

>>>re.findall('((?<=b).*?)(?:\s|$)', s) # b后面的位置开始

['1c', '2c', '3c']

>>>re.findall('((?=b).*?)(?:\s|$)', s) # b前面的位置开始

['b1c', 'b2c', 'b3c']

前后查找和findall:

即使正则表达式捕获的是位置,但是有了捕获组以后,findall会返回捕获组的内容,参考之前的re.findall('a(b)c', s)

s = '1234567890'

re.findall(r'(?<=\d)(?=(\d\d\d)+$)', s) # ['890', '890', '890'],实际上正则表达式匹配的是三个位置,但有了捕获组以后findall的返回结果是捕获组内容

re.findall(r'(?<=\d)(?=(?:\d\d\d)+$)', s) # ['', '', ''],改为非捕获型后,findall返回的是三个位置:1|234|567|890

可以在该位置插入字符:

r = re.sub(r'(?<=\d)(?=(?:\d\d\d)+$)', ':', s) # 1:234:567:890

前后查找和sub:

sub是对匹配到的内容进行替换,捕获组不会对替换结果产生影响,捕获组在sub中只是用户回溯引用,参考9捕获组中的回溯引用

re.sub(r'(?<=\d)(?=(\d\d\d)+$)', '|', s) # 1|234|567|890,对匹配到的内容(位置)进行替换

re.sub(r'(?<=\d)(?=(?:\d\d\d)+$)', '|', s) # 1|234|567|890,对匹配到的内容

11,匹配举例

'ab1c ab2c\nab3c'匹配例子:

>>>s = 'ab1c ab2c\nab3c'

>>>re.search('a.*?c a.*?c', s).group() # 整体匹配

ab1c ab2c

>>>re.search('a(.*?)c a(.*?)c', s).groups() # 整体匹配 + 捕获分组

'b1 b2'

>>>re.search('a(\w(\w))c a(\w(\w))c', s).groups() # 整体匹配 + 捕获分组

('b1', '1', 'b2', '2')

>>>re.findall('a.*?c', s) # 全局匹配

['ab1c', 'ab2c', 'ab3c']

>>>re.findall('a(.*?)c', s) # 全局匹配 + 捕获分组:只能捕获分组内容

['b1', 'b2', 'b3']

>>>re.findall('a(\w(\w))c', s) # 全局匹配 + 捕获分组嵌套

[('b1', '1'), ('b2', '2'), ('b3', '3')]

>>>re.findall('a.*?c$', s)

['ab3c']

>>>re.findall('a.*?c$', s, flags=re.M) # re.M影响$

['ab1c ab2c', 'ab3c']

>>>re.findall('^a.*?c', s)

['ab1c']

>>>re.findall('^a.*?c', s, flags=re.M) # re.M影响^

['ab1c', 'ab3c']

>>>re.search('a.*c', s).group()

'ab1c ab2c'

>>>re.search('a.*c', s, flags=re.DOTALL).group() # re.DOTALL影响.

'ab1c ab2c\nab3c'

>>>re.split('\s', s)

['ab1c', 'ab2c', 'ab3c']

>>>re.split('(\s)', s) # re.split + 分组可以看到分隔符

['ab1c', ' ', 'ab2c', '\n', 'ab3c']

'a b, c ; d\ e'匹配例子:

>>>s = 'a b, c ; d\ e'

>>>s

'a b, c ; d\\ e' # \被自动转成\\

>>>re.split(r'\s*[\s,;\\]\s*', s)

['a', 'b', 'c', 'd', 'e']

>>>re.split(r'\s*(?:\s|,|;|\\)\s*', s) # []的等效写法

['a', 'b', 'c', 'd', 'e']

>>>re.split(r'\s*(\s|,|;|\\)\s*', s) # 不用?:只抓到了单个分隔符,用它去对s分割会包含单个分隔符

['a', ' ', 'b', ',', 'c', ';', 'd', '\\', 'e']

>>> re.split(r'(\s*(?:\s|,|;|\\)\s*)', s) # 包含完整的分隔符

['a', ' ', 'b', ', ', 'c', ' ; ', 'd', '\\ ', 'e']

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Python 爬虫中,正则表达式是非常常用的技术,可以帮助我们从网页中提取出需要的信息。下面是一些常用的正则表达式符号和方法: 1. ".":匹配任意单个字符,除了换行符。 2. "^":匹配字符串的开始位置。 3. "$":匹配字符串的结束位置。 4. "*":匹配前一个字符出现 0 次或多次。 5. "+":匹配前一个字符出现 1 次或多次。 6. "?":匹配前一个字符出现 0 次或 1 次。 7. "{m}":匹配前一个字符出现 m 次。 8. "{m,n}":匹配前一个字符出现 m 到 n 次。 9. "(...)":分组匹配,匹配括号内的表达式。 10. "[...]":匹配括号内的任意一个字符。 11. "[^...]":匹配不在括号内的任意一个字符。 12. "\d":匹配数字,等同于 [0-9]。 13. "\D":匹配非数字,等同于 [^0-9]。 14. "\s":匹配空白字符,包括空格、制表符、换行符等。 15. "\S":匹配非空白字符。 16. "\w":匹配单词字符,包括字母、数字、下划线。 17. "\W":匹配非单词字符。 在 Python 中,使用 re 模块进行正则表达式的匹配。常用的方法包括: 1. re.compile(pattern):将正则表达式编译成一个对象,提高匹配效率。 2. re.search(pattern, string):在字符串中搜索匹配正则表达式的第一个位置。 3. re.findall(pattern, string):在字符串中搜索匹配正则表达式的所有位置,并返回一个列表。 4. re.sub(pattern, repl, string):将字符串中所有匹配正则表达式的字符替换为指定的字符串。 以上是一些常用的正则表达式符号和方法,希望能对你有所帮助。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值