python3基础知识复习 --正则表达式(RE)

正则表达式 (import re)

https://docs.python.org/zh-cn/3.9/howto/regex.html

正则表达式(Regular expressions 也称为 REs,或 regexes 或 regex patterns)本质上是一个微小的且高度专业化的编程语言。它被嵌入到 Python 中,并通过 re 模块提供给程序猿使用。

  • Re.search() – 用于在字符串中搜索正则表达式第一次出现的位置,匹配成功显示位置和target,否则什么都没有
re.search(r'p', 'apple')
<re.Match object; span=(1, 2), match='p'>
  • 正则表达式默认区分大小写,后面需要该XXXX

  • 元字符并不能匹配自身,需要"\元字符"来匹配自身,元字符列表 . ^ $ + ? { } [ ] \ | ( )

  • Re.findall(r'[a-z]', 'Peanutfish') — 查到所有满足条件的字符并以列表形式返回

字符匹配

“.” 通配符

匹配除了换行符外的任意一个字符,**如果设置了 re.DOTALL 标志,.将匹配包括换行符在内的任何字符。

 re.search(r'p.', 'apple')
<re.Match object; span=(1, 3), match='pp'>
“\”用法
\

1. 将一个普通字符变成特殊字符,例如\d表示匹配所有十进制数字

2. 解除元字符的特殊功能,例如\.表示匹配点号本身

3. 引用序号对应的子组所匹配的字符串

反斜杠真牛逼,反斜杠后边跟元字符去除特殊功能,反斜杠后边跟普通字符实现特殊功能。

如果要搜索".“,需要在前面加”\"

re.search(r’\.', ‘apple.com’)

<re.Match object; span=(5, 6), match=‘.’>

特殊字符含义
\d匹配任何十进制数字;相当于类[0-9]
\D与\d相反,匹配任何非十进制数字的字符;相当于类[^0-9]
\s匹配任何空白字符(包含空格、换行符、制表符等);相当于类[ \t\n\r\f\v]
\S与\s相反,匹配任何非空白字符;相当于类[^ \t\n\r\f\v]
\w匹配任何单词字符。如果正则表达式以字节的形式表示,这相当于字符类[a-zA-Z0-9_];如果正则表达式是一个字符串,\w会匹配所有 Unicode 数据库(unicodedata 模块提供)中标记为字母的字符。re.ASCII 标志使得 \w 只能匹配 ASCII 字符,不要忘了,Python3 是 Unicode 的。
\W于\w相反
\b

匹配单词的开始或结束,即一个单词边界,单词被定义为 Unicode 的字母数字或下横线字符

举个栗子:\bFishC\b会匹配字符串 "love FishC"、FishC." 或 "(FishC)"

\B与\b相反

它们可以包含在一个字符类中,并且一样拥有特殊含义。例如[\s,.]是一个字符类,它将匹配任何空白字符(\s的特殊含义),‘,‘或’.’。

“\d” — 匹配任意一个数字,匹配多个使用"\d\d\d…"

re.search(r’\d\d\d’, ‘I have 256 iphones’)

<re.Match object; span=(7, 10), match=‘256’>

*关于反斜杠的用法 *3. 引用序号对应的子组所匹配的字符串

\序号

1. 引用序号对应的子组所匹配的字符串,子组的序号范围从 1 - 99开始计算

2. 如果序号是以 0 开头,或者 3 个数字的长度表示的是八进制数,表示这个八进制数表示的ASCII对应的字符。那么不会被用于引用对应的子组,而是用于匹配八进制数字所表示的 ASCII 码值对应的字符

# 比较好理解的解释方法学会了,粘贴在这里方便以后的朋友:
(原帖地址https://www.cnblogs.com/gddcz/p/9131597.html)
关于正则表达式 \1 \2之类的问题 

我们创建一个正则表达式
var RegExp = /^(123)(456)\2\1$/;
这个正则表达式匹配到的字符串就是
123456456123
创建另外第二正则表达式
var RegExp1 = /^(123)(456)\1$/;
这个正则表达式匹配到的字符串是
123456123
创建另外第三正则表达式
var RegExp1 = /^(123)(456)\2$/;
这个正则表达式匹配到的字符串是
123456456

这个\1 \2...... 都要和正则表达式集合()一起使用
简单的说就是
\1表示重复正则第一个圆括号内匹配到的内容
\2表示重复正则第二个圆括号内匹配到的内容
…
"""

# 三位数的时候表示八进制代表的ascii,101(o)-->65(d)-->A
>>> re.search(r'(123)\101', '123A')
<re.Match object; span=(0, 4), match='123A'>

"[]"字符类

“[]” — 两个元字符创建一个字符类,用来表示范围,匹配该类中任意一个数字就是匹配

注1:连字符 - 如果出现在字符串中间表示字符范围描述;如果如果出现在首位则仅作为普通字符

注2:特殊字符仅有反斜线 \ 保持特殊含义,用于转义字符。其它特殊字符如 *、+、? 等均作为普通字符匹配,r’[\n]’ — 表示匹配转义字符回车

注3:脱字符 ^ 如果出现在首位则表示匹配不包含其中的任意字符,例如 [^5] 会匹配除了 '5' 之外的任何字符;如果 ^ 出现在字符串中间就仅作为普通字符匹配

>>> re.search(r'^Pea', 'Peanut.com')
<re.Match object; span=(0, 3), match='Pea'>
 
>>> re.search(r'[^5]', '552345')
<re.Match object; span=(2, 3), match='2'>
"$" --- 匹配输入字符的结束位置
>>> re.search(r'pea$', 'This is pea')
<re.Match object; span=(8, 11), match='pea'>
 
 
Re.search(r'[0-9]', '1234') --- 注意只能匹配一个数字0-9[0-255]并不能匹配0-255,而是匹配0-2,55的一个数字
>>> re.search(r'[0-255]', '188')
<re.Match object; span=(0, 1), match='1'>
 
>>> re.search(r'[0-255]', '358')
<re.Match object; span=(1, 2), match='5'>
 
re.search(r'[a-zA-Z]', 'The pig')
<re.Match object; span=(0, 1), match='T'>

重复的事情

区别:

* 匹配的是零次或者多次,所以被重复的内容可能压根儿不会出现;例如 ca*t 将匹配 ct(0 个字符 a),cat(1 个字符 a),caaat(3 个字符 a)…

+ 匹配至少需要出现一次。例如 ca+t 会匹配 cat 和 caaat,但不会匹配 ct。

? 匹配前一个字符匹配零次或者一次。你可以这么想,它的作用就是把某种东西标志位可选的。例如 小?甲鱼 可以匹配 小甲鱼,也可以匹配 甲鱼

C 语言的 int 类型大小的内部限制,正则表达式引擎会限制字符 ‘a’ 的重复个数不超过 20 亿个.

正则表达式默认的重复规则是贪婪的,当你重复匹配一个 RE 时,匹配引擎会尝试尽可能多的去匹配。直到 RE 不匹配或者到了结尾,匹配引擎就会回退一个字符,然后再继续尝试匹配。

EG:

先考虑一下表达式 a[bcd]*b,首先需要匹配字符 ‘a’,然后是零个到多个 [bcd],最后以 ‘b’ 结尾。那现在想象一下,这个 RE 匹配字符串 abcbd 会怎样?

步骤匹配说明
1a匹配 RE 的第一个字符 ‘a’
2abcbd引擎在符合规则的情况下尽可能地匹配 [bcd]*,直到该字符串的结尾
3失败引擎尝试匹配 RE 最后一个字符 ‘b’,但当前位置已经是字符串的结尾‘’,所以失败告终
4abcb回退,所以 [bcd]* 匹配少一个字符
5失败再一次尝试匹配 RE 最后一个字符 ‘b’,但字符串最后一个字符是 ‘d’,所以失败告终
6abc再次回退,所以 [bcd]* 这次只匹配 ‘bc’
7abcb再一次尝试匹配字符 ‘b’,这一次字符串当前位置指向的字符正好是 ‘b’,匹配成功

最终,RE 匹配的结果是 abcb。

启用非贪婪模式:在表示重复次数的元字符后面加一个"?"表示非贪婪模式,

非贪婪的限定符 *?、+?、?? 或 {m,n}?,尽可能地匹配小的文本。

>>> re.search(r'a[bcd]*?b', 'abcbd')
<re.Match object; span=(0, 2), match='ab'>

最灵活的应该是元字符 {m,n}(m 和 n 都是十进制整数)

其实 *、+ 和 ? 都可以使用 {m,n} 来代替。{0,} 跟 * 是一样的;{1,} 跟 + 是一样的;{0,1} 跟 ? 是一样的。不过还是鼓励大家记住并使用 、+ 和 ?,因为这些字符更短并且更容易阅读。还有一个原因是匹配引擎对 ** + ? 做了优化,效率要更高些。

“{}” — 重复匹配的次数

>>> re.search(r'b{3}', 'abbbbbbbbbbbbbbbbc') --- 只要满足b有3次就能匹配
<re.Match object; span=(1, 4), match='bbb'>
 
Re.search(r'ab{3,5}c', 'xxxxxx') -- 匹配的b出现310次都可以,显示最大匹配字符串
>>> re.search(r'b{3,5}', 'abbbbbbbbbbbbbbbbc')
<re.Match object; span=(1, 6), match='bbbbb'>

编译正则表达式

正则表达式被编译为模式对象,该对象拥有各种方法供你操作字符串,如查找模式匹配或者执行字符串替换。

re.compile() 也可以接受 flags 参数,用于开启各种特殊功能和语法变化

>>> p = re.compile(‘ab*’, re.IGNORECASE)

>>> p

<_sre.SRE_Pattern object at 0x…>

*模式对象*,下面是其常用方法

方法功能
match()判断一个正则表达式是否从开始处匹配一个字符串
search()遍历字符串,找到正则表达式匹配的第一个位置
findall()遍历字符串,找到正则表达式匹配的所有位置,并以列表的形式返回
finditer()遍历字符串,找到正则表达式匹配的所有位置,并以迭代器的形式返回

如果没有找到任何匹配的话,match() 和 search() 会返回 None;如果匹配成功,则会返回一个匹配对象(match object),包含所有匹配的信息:例如从哪儿开始,到哪儿结束,匹配的子字符串等等。

\>>> a = re.compile('[a-z]+')
\>>> a.match("")
\>>> print(a.match(""))
None
\>>> print(a.match("abc"))
<re.Match object; span=(0, 3), match='abc'>

Findall 用法

If there are no groups, return a list of strings matching the whole pattern. If there is exactly one group, return a list of strings matching that group. If multiple groups are present, return a list of tuples of strings matching the groups.

Eg1: download pic from tieba

import urllib.request
import re
import os
 
def get_html(url):
  request = urllib.request.Request(url)
  request.add_header('User-Agent', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.163 Safari/537.36')
  html = urllib.request.urlopen(request).read().decode('utf-8')
  return html
     
def download_pic(url):
  html = get_html(url)
  pic_addr = re.findall(r'bpic="([^"]+\.jpg)"', html)  # () 自组的内容在使用findall时直接返回列表,多个自组就以元祖形式返回
 
  for each in pic_addr:
    name = each.split('/')[-1]
    urllib.request.urlretrieve(each, os.getcwd()+os.sep+"tieba"+os.sep+name, None)  #urlretrieve可直接下载
    
  print('All pictures are saved!')  
 
if __name__ == "__main__":
  print('starting!')
  url = "https://tieba.baidu.com/f?ie=utf-8&kw=%E6%A8%B1%E6%9C%A8%E8%8A%B1%E9%81%93&fr=search"
  download_pic(url)
Eg2:''' list IP list from a website'''
import urllib.request
import re
 
url = "https://www.89ip.cn/"
request = urllib.request.Request(url)
request.add_header('User-Agent', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.163 Safari/537.36')
html = urllib.request.urlopen(request).read().decode('utf-8')
 
ip_re = re.compile(r'(?:(?:[01]?\d?\d|2[0-4]\d|25[0-5])\.){3}(?:[01]?\d?\d|2[0-4]\d|25[0-5])') # (?:…)表示非捕获组,避免了findall的元祖返回
# ip_re = re.compile(r'\d+\.\d+\.\d+\.\d+')
 
ip_list = ip_re.findall(html)
 
for each in ip_list:
  print(each)

匹配对象方法:

方法功能
group()返回匹配的字符串
start()返回匹配的开始位置
end()返回匹配的结束位置
span()返回一个元组表示匹配位置(开始,结束)
>>> a = re.compile(r'[0-9]')
>>> b = a.match('abcd1234')
>>> print(b)
None
>>> c= a.search('abcd1234')
>>> print(c)
<re.Match object; span=(4, 5), match='1'>
>>> c.group()
'1'
>>> c.start()
4
>>> c.end()
5
>>> c.span()
(4, 5)

在实际应用中,最常用的方式是将匹配对象存放在一个局部变量中,并检查其返回值是否为 None。

p = re.compile( ... )
m = p.match( 'string goes here' )
if m:
  print('Match found: ', m.group())
else:
  print('No match')

编译标志

编译标志让你可以修改正则表达式的工作方式。

完整名和简写, 多个标志还可以同时使用(通过“|”),如:re.I | re.M 就是同时设置 I 和 M 标志。

下边列举一些支持的编译标志:

标志含义
ASCII, A使得转义符号如 \w,\b,\s 和 \d 只能匹配 ASCII 字符, 而不匹配完整的 Unicode 字符
DOTALL, S使得 . 匹配任何符号,包括换行符
IGNORECASE, I匹配的时候不区分大小写, 如果你不设置 LOCALE,则不会考虑语言(区域)设置这方面的大小写问题。
LOCALE, L支持当前的语言(区域)设置, 使得 \w,\W,\b 和 \B 依赖当前的语言(区域)环境,而不是 Unicode 数据库。区域设置是 C 语言的一个功能,主要作用是消除不同语言之间的差异。例如你正在处理的是法文文本,你想使用 \w+ 来匹配单词,但是 \w 只是匹配 [A-Za-z] 中的单词,并不会匹配 ‘é’ 或 ‘ç’。如果你的系统正确的设置了法语区域环境,那么 C 语言的函数就会告诉程序 ‘é’ 或 ‘ç’ 也应该被认为是一个字符。当编译正则表达式的时候设置了 LOCALE 的标志,\w+ 就可以识别法文了,但速度多少会受到影响。
MULTILINE, M多行匹配,影响 ^ 和 $. 通常 ^ 只匹配字符串的开头,而 $ 则匹配字符串的结尾。当这个标志被设置的时候,^ 不仅匹配字符串的开头,还匹配每一行的行首;& 不仅匹配字符串的结尾,还匹配每一行的行尾。
VERBOSE, X (for ‘extended’)启用详细的正则表达式, 使用了这个标志,空格会被忽略(除了出现在字符类中和使用反斜杠转义的空格);这个标志同时允许你在正则表达式字符串中使用注释,# 符号后边的内容是注释,不会递交给匹配引擎(除了出现在字符类中和使用反斜杠转义的 #)。

下边是使用 re.VERBOSE 的例子,大家看下正则表达式的可读性是不是提高了不少:

charref = re.compile(r"""
&[#]        # 开始数字引用
(
  0[0-7]+     # 八进制格式
 | [0-9]+     # 十进制格式
 | x[0-9a-fA-F]+  # 十六进制格式
)
;          # 结尾分号
""", re.VERBOSE)

如果没有设置 VERBOSE 标志,那么同样的正则表达式会写成:

charref = re.compile("&#(0[0-7]+|[0-9]+|x[0-9a-fA-F]+);")

元字符 - 零宽断言

不匹配任何字符,只是简单地表示成功或失败。

\b 表示当前位置位于一个单词的边界,但 \b 并不能改变位置。因此零宽断言不应该被重复使用,因为 \b 并不会修改当前位置,所以 \b\b 跟 \b 是没什么两样的,

理解“改变位置”和“零宽断言”的意思?比如 abc 匹配完 a 之后,咱的当前位置就会移动,才能继续匹配 b*,依次类推**…但是 \babc *的话,\b* 表示当前位置在单词的边界(单词的第一个字母或者最后一个字母),这时候当前位置不会发生改变,接着将 a 与当前位置的字符进行匹配…*

|

或操作符,对两个正则表达式进行或操作。如果 A 和 B 是正则表达式,A | B 会匹配 A 或 B 中出现的任何字符。

使用 \| 或 [|] 来匹配 '|' 字符本身。

^

匹配字符串的起始位置。如果设置了 MULTILINE 标志,就会变成匹配每一行的起始位置。在 MULTILINE 中,每当遇到换行符就会立刻进行匹配。

>>> a = ‘’’ this is a test

from where you typed

to you come from’‘’

>>> r = re.compile(r’^from’, re.MULTILINE)

>>> r.search(a)

<re.Match object; span=(16, 20), match=‘from’>

$

匹配字符串的结束位置,每当遇到换行符也会离开进行匹配。

>>> print(re.search(‘}$’, ‘{block}\n’))

<_sre.SRE_Match object; span=(6, 7), match=‘}’>

同样,我们使用 \$ or [$] 来匹配 '$' 字符本身.

\A

只匹配字符串的起始位置。如果没有设置 MULTILINE 标志的时候,\A 和 ^ 的功能是一样的;但如果设置了 MULTILINE 标志:\A 还是匹配字符串的起始位置,但 ^ 会对字符串中的每一行都进行匹配。

\Z

只匹配字符串的结束位置。同$一致

\b --> \B 相反,表示非单词边界

单词边界,这是一个只匹配单词的开始和结尾的零宽断言。“单词”定义为一个字母数字的序列,所以单词的结束指的是空格或者非字母数字的字符。

在使用这些特殊的序列的时候,有两点是需要注意的:第一点需要注意的是,Python 的字符串跟正则表达式在有些字符上是有冲突的(回忆之前反斜杠的例子)。比如说在 Python 中,\b 表示的是退格符(ASCII 码值是 8)。所以,你如果不使用原始字符串,Python 会将 \b 转换成退格符处理,这样就肯定跟你的预期不一样了。

下边的例子中,我们故意不写表示原始字符串的 'r',结果确实大相庭径:
>>> p =     re.compile('\bclass\b')
>>> print(p.search('\b' + 'class' + '\b')) 
<_sre.SRE_Match object;     span=(0, 7), match='\x08class\x08'>

“()” — 表示分组

Eg:查找IP, "|"表示或,[01]{0,1}表示数字0或1出现0次或一次,由于ip可以为个位数,所有前两位是重复{0,1}, ([01]{0,1}\d{0,1}\d|2[0-4]\d|25[0-5])是一个整体来匹配,这个整体加上"."重复3次{3}

>>> re.search(r'(([01]{0,1}\d{0,1}\d|2[0-4]\d|25[0-5])\.){3}([01]{0,1}\d{0,1}\d|2[0-4]\d|25[0-5])', '192.168.11.1')
<re.Match object; span=(0, 12), match='192.168.11.1'>
a = re.compile('(([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5]).){3}([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])')
a = re.compile('(([01]?\d?\d|2[0-4]\d|25[0-5]).){3}([01]?\d?\d|2[0-4]\d|25[0-5])')
>> 网页快速查找IP(但不保证满足IP要求)ip_re = re.compile(r'\d+\.\d+\.\d+\.\d+')

使用 ( ) 表示的子组我们还可以对它进行按层次索引,可以将索引值作为参数传递给这些方法:group(),start(),end() 和 span()。序号 0 表示第一个分组(这个是默认分组,一直存在的,所以不传入参数相当于默认值 0), 序号1,2…表示子组:

提取子串非常有用。来看一个凶残的例子:

# 这个正则表达式可以直接识别合法的时间。
>>> t = '19:05:30'
>>> m = re.match(r'^(0[0-9]|1[0-9]|2[0-3]|[0-9])\:(0[0-9]|1[0-9]|2[0-9]|3[0-9]|4[0-9]|5[0-9]|[0-9])\:(0[0-9]|1[0-9]|2[0-9]|3[0-9]|4[0-9]|5[0-9]|[0-9])$', t)
>>> m.groups() # 返回所有子组
('19', '03', '02')
>>> m.group(0) # 返回完整结果
'19:03:02'
>>> m.group(1) # 返回第一个子组
'19'
>>> m.group(2) # 返回第二个子组
'03'
>>> m.group(3) 
'02'
>>> m.group(4) # 没有第4个子组,报IndexError
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
IndexError: no such group

正则表达式的扩展语法

非捕获组 (?:…)

有时候你只需要用一个组来表示部分正则表达式,你并不需要这个组去匹配任何东西,这时你可以通过非捕获组来明确表示你的意图。

>>> m = re.match("([abc])+", "abc")
>>> m.groups()
('c',)
>>> m = re.match("(?:[abc])+", "abc")
>>> m.groups()
()

除了你不能从非捕获组获得匹配的内容之外,其他的非捕获组跟普通子组没有什么区别了。你可以在里边放任何东西,使用重复功能的元字符,或者跟其他子组进行嵌套(捕获的或者非捕获的子组都可以)。

作用:

当你需要修改一个现有的模式的时候,(?:…) 是非常有用的。原始是添加一个非捕获组并不会影响到其他(捕获)组的序号。值得一提的是,在搜索的速度上,捕获组和非捕获组的速度是没有任何区别的。

命名组 (?P)

命名组除了有一个名字标识之外,跟其他捕获组是一样的。

匹配对象的所有方法不仅可以处理那些由数字引用的捕获组,还可以处理通过字符串引用的命名组。除了使用名字访问,命名组仍然可以使用数字序号进行访问:

>>> p = re.compile(r’(?P<word>\b\w+\b)')

>>> m = p.search( ‘(((( Lots of punctuation )))’ )

>>> m.group(‘word’)

‘Lots’

>>> m.group(1)

‘Lots’

命名组非常好用,因为它让你可以使用一个好记的名字代替一些毫无意义的数字。下边是来自 imaplib 模块的例子:

InternalDate = re.compile(r'INTERNALDATE "'

​    r'(?P<day>[ 123][0-9])-(?P<mon>[A-Z][a-z][a-z])-'

​    r'(?P<year>[0-9][0-9][0-9][0-9])'

​    r' (?P<hour>[0-9][0-9]):(?P<min>[0-9][0-9]):(?P<sec>[0-9][0-9])'

​    r' (?P<zonen>[-+])(?P<zoneh>[0-9][0-9])(?P<zonem>[0-9][0-9])'

​    r'"')

很明显,使用 m.group(‘zonem’) 访问匹配内容要比使用数字 9 更简单明了。

正则表达式中,反向引用的语法像 (…)\1 是使用序号的方式来访问子组;在命名组里,显然也是有对应的变体:使用名字来代替序号。其扩展语法是 (?P=name),含义是该 name 指向的组需要在当前位置再次引用。那么搜索两个单词的正则表达式可以写成 (\b\w+)\s+\1,也可以写成 (?P\b\w+)\s+(?P=word):

>>> p = re.compile(r’(?P\b\w+)\s+(?P=word)')

>>> p.search(‘Paris in the the spring’).group()

‘the the’

前向断言

属于零宽断言,分为前向肯定断言和前向否定断言两种形式。

(?=…)

前向肯定断言。如果当前包含的正则表达式(这里以 … 表示)在当前位置成功匹配,则代表成功,否则失败。一旦该部分正则表达式被匹配引擎尝试过,就不会继续进行匹配了;剩下的模式在此断言开始的地方继续尝试。

(?!..)

前向否定断言。这跟前向肯定断言相反(不匹配则表示成功,匹配表示失败)。

eg:

匹配扩展名不是 bat 的文件

.*[.](?!bat$).*$

同时排除 bat 和 exe 扩展名

.*[.](?!bat$|exe$).*$

修改字符串

方法用途
split()在正则表达式匹配的地方进行分割,并返回一个列表
sub()找到所有匹配的子字符串,并替换为新的内容
subn()跟 sub() 干一样的勾当,但返回新的字符串以及替换的数目

分割字符串

re.split(string[, maxsplit=0])

正则表达式的 split() 方法将字符串在匹配的地方进行分割,并将分割后的结果作为列表返回。它的做法其实很像字符串的 split() 方法,但这个可以使用更加广泛的分隔符。你猜的没错,它同时提供了一个模块级别的函数:re.split()

如果在 RE 中,你使用了捕获组,那么它们的内容会作为一个列表返回。你可以通过传入一个 maxsplit 参数来设置分割的数量。如果 maxsplit 的值是非 0,表示至多有 maxsplit 个分割会被处理,剩下的内容作为列表的最后一个元素返回。

\>>> p = re.compile(r'\W+')
\>>> p.split('this is a test, short and sweet, of split().')
['this', 'is', 'a', 'test', 'short', 'and', 'sweet', 'of', 'split', '']
\>>> p.split('this is a test, short and sweet, of split().', 3)
['this', 'is', 'a', 'test, short and sweet, of split().']

用正则表达式切分字符串比用固定的字符更灵活,请看正常的切分代码:

>>> 'a b   c'.split(' ')
['a', 'b', '', '', 'c']

嗯,无法识别连续的空格,用正则表达式试试:

>>> re.split(r'\s+', 'a b   c')
['a', 'b', 'c']

>>> re.split(r'[\s\,]+', 'a,b, c  d') # 加入","可以正常分割
['a', 'b', 'c', 'd']

>>> re.split(r'[\s\,\;]+', 'a,b;; c  d') # 加入";"可以正常分割
['a', 'b', 'c', 'd']

如果用户输入了一组标签,下次记得用正则表达式来把不规范的输入转化成正确的数组。

搜索和替换

sub 方法有一个 replacement 参数,它可以是一个待替换的字符串,或者一个处理字符串的函数。

re.sub(replacement, string[, count=0])

subn() 方法跟 sub() 方法干同样的勾当,但区别是返回值为一个包含有两个元素的元组:一个是替换后的字符串,一个是替换的数目。

返回一个字符串,这个字符串从最左边开始,所有 RE 匹配的地方都替换成 replacement。如果没有找到任何匹配,那么返回原字符串。

可选参数 count 指定最多替换的次数,必须是一个非负值。默认值是 0,意思是替换所有找到的匹配。

下边例子中,将匹配被 { 和 } 括起来的单词 section,并将 section 替换成 subsection:

  1. >>> p = re.compile(‘section{ ( [^}]* ) }’, re.VERBOSE)
  2. >>> p.sub(r’subsection{\1}',‘section{First} section{second}’)
  3. ‘subsection{First} subsection{second}’

1. 大家还记得吗?这里开启了 re.VERBOSE,空格将被忽略。因为这里一堆符号,用空格隔开看着才不会乱糟糟的**…
2.*这里 r’subsection{\1}’ 使用 \1 引用匹配模式中的 ([^}]*) 匹配的字符串内容。

还可以使用 Python 的扩展语法 (?P…) 指定命名组,引用命名组的语法是 \g。\g 会将名字为 name 的组匹配的字符串替换进去。另外,\g<数字> 是通过组的序号进行引用。\g<2> 其实就相当于 \2,但我们更提倡使用 \g<2>,因为这样可以避免歧义。

>>> p = re.compile(r'section{ ( [^}]+ ) }', re.VERBOSE)
>>> p.sub(r'subsection{\1}', 'section{First}, section{second}')
'subsection{First}, subsection{second}'
>>> p = re.compile(r'section{(?P<name>[^}]+)}')
>>> p.sub(r'subsection{\1}', 'section{First}, section{Second}')
'subsection{First}, subsection{Second}'
>>> p.sub(r'subsection{\g<name>}', 'section{First}, section{Second}')
'subsection{First}, subsection{Second}'
>>> p.sub(r'subsection{\g<1>}', 'section{First}, section{Second}')
'subsection{First}, subsection{Second}'

另外replacement 还可以是一个函数,匹配得到的对象作为参数传给这个函数进行处理然后返回新的东西

>>> def hexrep(match):
value = int(match.group())
return hex(value)
 
>>> p = re.compile(r'\d+')
>>> p.sub(hexrep, '65536 is from 23456 girls and boys!')
'0x10000 is from 0x5ba0 girls and boys!'
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值