该部分为中谷教育Python视频教程的学习笔记(内容较多)
使用正则表达式
re模块提供了一个正则表达式引擎的接口,可以让你将REstring编译成对象并用它们来进行匹配
编译正则表达式:
>>> import re >>> p = re.compile(\'ab*\') >>> print(p) <_sre.SRE_Pattern object at 0x02425D30> >>>
如果说有一段正则表达式要经常用来匹配的话,那么建议把正则表达式编译出来。编译方式就是re的方法,如re.compile,re.match等
>>> r1 = r\'\d{3,4}-?\d{8}\' #匹配一个电话号码。 #\d的意思是匹配任何十进制数字也就是0-9,{3,4}是重复3次或者4次, #如区号,有的是3个比如010,020,有的是4个比如0753,0760,-?表示这个-号可有可无 #后面的\d{8}是指匹配的十进制数字重复8次 >>> re.findall(r1,\'010-12345678\') [\'010-12345678\'] >>> re.findall(r1,\'0760-12888678\') [\'0760-12888678\'] >>>
编译方式
re.compile可以把正则表达式编译成一个正则表达式对象。
可以把那些经常使用的正则表达式编译成正则表达式对象,这样可以提高一定的效率。
>>> p_tel = re.compile(r1) #取一个比较容易识别的名字,如tel就知道是在匹配电话号码 >>> p_tel <_sre.SRE_Pattern object at 0x0250E3C0> #返回一个正则表达式的对象 >>> p_tel.findall(\'010-12345678\') #编译之后的正则比未编译的正则运算速度要快很多。 [\'010-12345678\'] >>>
re.compile()也接受可选的标志参数,常用来实现不同的特殊功能和语法变更
如现在写一个正则,在匹配当中不区分大小写。
>>> fishdm_re = re.compile(r\'fishdm\', re.I) #后面加一个属性,re.I >>> fishdm_re.findall(\'fishdm\') [\'fishdm\'] >>> fishdm_re.findall(\'FISHDM\') [\'FISHDM\'] >>> fishdm_re.findall(\'FiShDm\') [\'FiShDm\'] >>>
执行匹配
RegexObjet实例有一些方法和属性,完整的列表可以查看 Python参考库
也可参考网文:http://www.cnblogs.com/sevenyuan/archive/2010/12/06/1898075.html
match() 决定re是否在字符串刚开始的位置匹配
search() 扫描字符串,找到这个re匹配的位置
如果没有匹配到的话,match()和search()将返回None,如果成功的话,就会返回一个“MatchObject”实例。
>>> fishdm_re = re.compile(r\'fishdm\', re.I) >>> fishdm_re.match(\'fishdm hello!\') <_sre.SRE_Match object at 0x02277608> #返回一个对象,包含这个fishdm字符串的 >>> fishdm_re.match(\'hello\') >>> #返回一个None >>> fishdm_re.match(\'hello fishdm\') >>> #如果把fishdm放在后面,返回也是空
# 用match去匹配字符串的话,只有被匹配元素或者数据在字符串开始的位置才会返回一个对象 #通常我们利用match的特性来判断匹配数据是否成功 >>> x = fishdm_re.match(\'fishdm hello\') >>> if x: print(\'匹配成功\')
search() 与 match()的区别 >>> fishdm_re = re.compile(r\'fishdm\', re.I) >>> x = fishdm_re.search(\'hello fishdm hao\') >>> if x: print(\'匹配成功\') 匹配成功 >>>
findall()找到re匹配的所有子串,并把它们作为一个列表返回
>>> txt = \'fishdm study hard, he is sure to succeed\' >>> fishdm_re = re.compile(r\'fishdm\', re.I) >>> fishdm_re.findall(txt) [\'fishdm\'] >>> fishdm_re = re.compile(r\'su\', re.I) >>> fishdm_re.findall(txt) [\'su\', \'su\'] >>>
finditer()找到re匹配的所有子串,并把它们作为一个迭代器返回
MatchObject实例方法:
m.group()返回被re匹配的字符串
m.start()返回匹配开始的位置
m.end()返回匹配结束的位置
m.span()返回一个元组包含匹配(开始,结束)的位置
在实际程序中,最常见的做法是将“MatchObject”保存在一个变量里,然后检查它是否为None
>>> fishdm_re = re.compile(r\'fishdm\', re.I) >>> x = fishdm_re.search(\'fishdm hao\') >>> if x: print(\'匹配成功\')
模块级函数:
re模块也提供了顶级函数调用
如: match(),search(),sub(),subn(),spilt(),findall()等
sub(),subn()
>>> s = \'hello fishdm\' #如果要把fishdm替换成python,那么可以用以前学到的字符串方法replace >>> s.replace(\'fishdm\',\'python\') \'hello python\' #但这种方法并不是正则表达式,只是根据指定的字符串进行替换 #这样写,想让它成为一个正则表达式把fishdm改成r\'f..m\' >>> s.replace(r\'f..m\',\'python\') \'hello fishdm\' #明显不起作用,replace()方法并不支持正则表达式 #所以我们有必要在正则表达式当中准备能够实现这样操作的函数,sub >>> s = \'hello fishdm, fishkm,fisham,foskm,feuio,foimn\' >>> rs = r\'f....m\' #注意,这里的.是字符串中fishdm的f和m之间的字母数 >>> re.sub(rs,\'python\',s) \'hello python, python,python,foskm,feuio,foimn\' >>> re.subn(rs,\'python\',s) #subn()是返回替换次数,一共替换了多少次 (\'hello python, python,python,foskm,feuio,foimn\', 3) >>>
spilt() 分割
>>> ip = \'10.0.0.252\' >>> ip.split(\'.\') #之前学过的字符串分割方法 [\'10\', \'0\', \'0\', \'252\'] #同样不支持正则表达式 #比如这样一个字符串 >>> s = \'123 + 456 -789 * 025\' #我想用+-*分割字符串,用split()方法是不行的,该方法只支持一个字符串分割 >>> re.split(r\'+-*\',s) #注意,这样也是不行的,因为在元字符部分学习过,+-*这三个符号是元字符 #需要添加转义符\,不然会报错 如 >>> re.split(r\'[\+\-\*]\',s) [\'123 \', \' 456 \', \'789 \', \' 025\'] >>>
正则表达式还有很多内置的函数,可以通过idle查看
>>> dir(re)
[\'A\', \'ASCII\', \'DEBUG\', \'DOTALL\', \'I\', \'IGNORECASE\', \'L\', \'LOCALE\', \'M\', \'MULTILINE\', \'S\', \'Scanner\', \'T\', \'TEMPLATE\', \'U\', \'UNICODE\', \'VERBOSE\', \'X\', \'_MAXCACHE\', \'__all__\', \'__builtins__\', \'__cached__\', \'__doc__\', \'__file__\', \'__initializing__\', \'__loader__\', \'__name__\', \'__package__\', \'__version__\', \'_alphanum_bytes\', \'_alphanum_str\', \'_cache\', \'_cache_repl\', \'_compile\', \'_compile_repl\', \'_expand\', \'_pattern_type\', \'_pickle\', \'_subx\', \'compile\', \'copyreg\', \'error\', \'escape\', \'findall\', \'finditer\', \'functools\', \'match\', \'purge\', \'search\', \'split\', \'sre_compile\', \'sre_parse\', \'sub\', \'subn\', \'sys\', \'template\']
>>>
>>> help(re)
可以查看RE手册(考验英文水平的时候到了)
还有其他属性请参考:http://m.blog.csdn.net/blog/daillo/7030879 关于松散正表达式的描述
例如:
# 必须引入 re 标准库
import re
# 字符串替换: sub() 与 subn()
s = \'100 NORTH MAIN ROAD\'
# 将字符串结尾的单词“ROAD”替换成“RD.”;该 re.sub() 函数执行基于正则表达式的字符串替换。
print(re.sub(r\'\bROAD$\', \'RD.\', s)) # 打印: 100 NORTH MAIN RD.
## subn() 与 sub() 作用一样,但返回的是包含新字符串和替换执行次数的两元组。
print(re.subn(r\'\bROAD$\', \'RD.\', s)) # 打印: (\'100 NORTH MAIN RD.\', 1)
# 字符串分割, split()
# 在正则表达式匹配的地方将字符串分片,将返回列表。只支持空白符和固定字符串。可指定最大分割次数,不指定将全部分割。
print(re.split(r\'\s+\', \'this is a test\')) # 打印: [\'this\', \'is\', \'a\', \'test\']
print(re.split(r\'\W+\', \'This is a test.\', 2)) # 指定分割次数,打印:[\'this\', \'is\', \'a test\']
# 如果你不仅对定界符之间的文本感兴趣,也需要知道定界符是什么。在 RE 中使用捕获括号,就会同时传回他们的值。
print(re.split(r\'(\W+)\', \'This is a test.\', 2)) # 捕获定界符,打印:[\'this\', \' \', \'is\', \' \', \'a test\']
## `MatchObject` 实例的几个方法
r = re.search(r\'\bR(OA)(D)\b\', s)
print(r.groups()) # 返回一个包含字符串的元组,可用下标取元组的内容,打印: (\'OA\', \'D\')
print(r.group()) # 返回正则表达式匹配的字符串,打印: ROAD
print(r.group(2)) # 返回捕获组对应的内容(用数字指明第几个捕获组),打印: D
print(r.start()) # 返回匹配字符串开始的索引, 打印: 15
print(r.end()) # 返回匹配字符串结束的索引,打印: 19
print(r.span()) # 返回一个元组包含匹配字符串 (开始,结束) 的索引,打印: (15, 19)
# 匹配多个内容, findall() 返回一个匹配字符串行表
p = re.compile(\'\d+\')
s0 = \'12 drummers drumming, 11 pipers piping, 10 lords a-leaping\'
print(p.findall(s0)) # 打印: [12, 11, 10]
print(re.findall(r\'\d+\', s0)) # 也可这样写,打印: [12, 11, 10]
# 匹配多个内容, finditer() 以迭代器返回
iterator = p.finditer(s0)
# iterator = re.finditer(r\'\d+\', s0) # 上句也可以这样写
for match in iterator:
print(match.group()) # 三次分别打印:12、 11、 10
# 记忆组
print(re.sub(\'([^aeiou])y$\', \'ies\', \'vacancy\')) # 将匹配的最后两个字母替换掉,打印: vacanies
print(re.sub(\'([^aeiou])y$\', r\'\1ies\', \'vacancy\')) # 将匹配的最后一个字母替换掉,记忆住前一个(小括号那部分),打印: vacancies
print(re.search(\'([^aeiou])y$\', \'vacancy\').group(1)) # 使用 group() 函数获取对应的记忆组内容,打印: c
# 记忆组(匹配重复字符串)
p = re.compile(r\'(?P<word>\b\w+)\s+\1\') # 注意, re.match() 函数不能这样用,会返回 None
p = p.search(\'Paris in the the spring\')
# p = re.search(r\'(?P<word>\b\w+)\s+\1\', \'Paris in the the spring\') # 这一句可以替换上面两句
print(p.group()) # 返回正则表达式匹配的所有内容,打印: the the
print(p.groups()) # 返回一个包含字符串的元组,打印: (\'the\',)
# 捕获组
r = re.search(r\'\bR(OA)(D)\b\', s) # 如过能匹配到,返回一个 SRE_Match 类(正则表达式匹配对象);匹配不到则返回“None”
# `MatchObject` 实例的几个方法
if r: # 如果匹配不到,则 r 为 None,直接执行下面语句则会报错;这里先判断一下,避免这错误
print(r.groups()) # 返回一个包含字符串的元组,可用下标取元组的内容,打印: (\'OA\', \'D\')
print(r.group()) # 返回正则表达式匹配的字符串,打印: ROAD
print(r.group(2)) # 返回捕获组对应的内容(用数字指明第几个捕获组),打印: D
# 无捕获组
print(re.match("([abc])+", "abcdefab").groups()) # 正常捕获的结果: (\'c\',)
print(re.match("(?:[abc])+", "abcdefab").groups()) # 无捕获组的结果: ()
# 命名组
m = re.match(r\'(?P<word>\b\w+\b) *(?P<word2>\b\w+\b)\', \'Lots of punctuation\')
print(m.groups()) # 返回正则表达式匹配的所有内容,打印:(\'Lots\', \'of\')
print(m.group(1)) # 通过数字得到对应组的信息,打印: Lots
print(m.group(\'word2\')) # 通过名称得到对应组的信息,打印: of
# 命名组 逆向引用
p = re.compile(r\'(?P<word>\b\w+)\s+(?P=word)\') # 与记忆组一样用法, re.match() 函数同样不能这样用,会返回 None
p = p.search(\'Paris in the the spring\') # r\'(?P<word>\b\w+)\s+(?P=word)\' 与 r\'(?P<word>\b\w+)\s+\1\' 效果一样
print(p.group()) # 返回正则表达式匹配的所有内容,打印: the the
print(p.groups()) # 返回一个包含字符串的元组,打印: (\'the\',)
# 使用松散正则表达式,以判断罗马数字为例
pattern = \'\'\'
^ # beginning of string
(M{0,3}) # thousands - 0 to 3 Ms
(CM|CD|D?C{0,3}) # hundreds - 900 (CM), 400 (CD), 0-300 (0 to 3 Cs),
# or 500-800 (D, followed by 0 to 3 Cs)
(XC|XL|L?X{0,3}) # tens - 90 (XC), 40 (XL), 0-30 (0 to 3 Xs),
# or 50-80 (L, followed by 0 to 3 Xs)
(IX|IV|V?I{0,3}) # ones - 9 (IX), 4 (IV), 0-3 (0 to 3 Is),
# or 5-8 (V, followed by 0 to 3 Is)
$ # end of string
\'\'\'
print(re.search(pattern, \'M\')) # 这个没有申明为松散正则表达式,按普通的来处理了,打印: None
print(re.search(pattern, \'M\', re.VERBOSE).groups()) # 打印: (\'M\', \'\', \'\', \'\')
# (?iLmsux) 用法
# 以下这三句的写法都是一样的效果,表示忽略大小写,打印: [\'aa\', \'AA\']
print(re.findall(r\'(?i)(aa)\', \'aa kkAAK s\'))
print(re.findall(r\'(aa)\', \'aa kkAAK s\', re.I))
print(re.findall(r\'(aa)\', \'aa kkAAK s\', re.IGNORECASE))
# 可以多种模式同时生效
print(re.findall(r\'(?im)(aa)\', \'aa kkAAK s\')) # 直接在正则表达式里面写
print(re.findall(r\'(aa)\', \'aa kkAAK s\', re.I | re.M)) # 在参数里面写
print(re.findall(r\'(aa)\', \'aa kkAAK s\', re.I or re.M))
# 预编译正则表达式解析的写法
# romPattern = re.compile(pattern) # 如果不是松散正则表达式,则这样写,即少写 re.VERBOSE 参数
romPattern = re.compile(pattern, re.VERBOSE)
print(romPattern.search(\'MCMLXXXIX\').groups()) # 打印: (\'M\', \'CM\', \'LXXX\', \'IX\')
print(romPattern.search(\'MMMDCCCLXXXVIII\').groups()) # 打印: (\'MMM\', \'DCCC\', \'LXXX\', \'VIII\')
# match()、search()、sub()、findall() 等等都可以这样用
match() vs search()
match() 函数只检查 RE 是否在字符串开始处匹配,而 search() 则是扫描整个字符串。记住这一区别是重要的。
match() 只报告一次成功的匹配,它将从 0 处开始;如果匹配不是从 0 开始的, match() 将不会报告它。
search() 将扫描整个字符串,并报告它找到的第一个匹配。
例:
print(re.match(\'super\', \'superstition\').span()) # 打印: (0, 5)
print(re.match(\'super\', \'insuperable\')) # 打印: None
print(re.search(\'super\', \'superstition\').span()) # 打印: (0, 5)
print(re.search(\'super\', \'insuperable\').span()) # 打印: (2, 7)