正则表达式

定义

正则表达式是一种用来匹配字符串的强有力的武器。它的设计思想是用一种描述性的语言来给字符串定义一个规则,凡是符合规则的字符串,我们就认为它“匹配”了,否则,该字符串就是不合法的

因为正则表达式也是用字符串表示的,所以,我们要首先了解如何用字符来描述字符。

要想真正的用好正则表达式,正确的理解元字符是最重要的事情。下面列出了常见的元字符和对它们的一个简短的描述.

  • \d:匹配一个数字字符。等价于[0-9]
  • \D:匹配一个非数字字符。等价于[^0-9]
  • \s:匹配任何不可见字符,包括空格、制表符、换页符等等。等价于[ \f\n\r\t\v]
  • \w:匹配包括下划线的任何单词字符。类似但不等价于“[A-Za-z0-9_]”,这里的"单词"字符使用Unicode字符集
  • \W:匹配任何非单词字符。等价于“[^A-Za-z0-9_]”
  • ^:表示行的开头, ^\d 表示必须以数字开头
  • 我们使用$表示行的结束
  • . :匹配除“\n”(换行)和"\r"(回车)之外的任何单个字符。要匹配包括“\n”和"\r"在内的任何字符,请使用像“[\s\S]”的模式
  • x|y:匹配x或y。例如,“z|food”能匹配“z”或“food”(此处请谨慎)。“[z|f]ood”则匹配“zood”或“food”
  • [xyz]:字符集合。匹配所包含的任意一个字符。例如,“[abc]”可以匹配“plain”中的“a”
  • [^xyz]:负值字符集合。匹配未包含的任意字符。例如,“[^abc]”可以匹配“plain”中的“plin”任一字符
  • [a-z]:注意:只有连字符在字符组内部时,并且出现在两个字符之间时,才能表示字符的范围; 如果出字符组的开头,则只能表示连字符本身
  • [^a-z]:负值字符范围。匹配任何不在指定范围内的任意字符。例如,“[^a-z]”可以匹配任何不在“a”到“z”范围内的任意字符
  • ( ):将( 和 ) 之间的表达式定义为“组”(group),并且将匹配这个表达式的字符保存到一个临时区域(一个正则表达式中最多可以保存9个),它们可以用 \1 到\9 的符号来引用
  • |:将两个匹配条件进行逻辑“或”(or)运算。例如正则表达式(him|her) 匹配"it belongs to him"和"it belongs to her",但是不能匹配"it belongs to them."。注意:这个元字符不是所有的软件都支持的
  • 正则表达式匹配变长的字符:
    • *:匹配前面的子表达式0到多次。* 等价于

    • +:匹配前面的子表达式一次或多次(大于等于1次)。+等价于

    • ?:匹配前面的子表达式零次或一次。等价于

    • {n}:n是一个非负整数。匹配确定的n次

    • {n,}:n是一个非负整数。至少匹配n次

    • {n,m}: m和n均为非负整数,其中n<=m。最少匹配n次且最多匹配m次。请注意在逗号和两个数之间不能有空格

    • ?: 当该字符紧跟在任何一个其他限制符(*,+,?,{n},{n,},{n,m})后面时,匹配模式是非贪婪的。非贪婪模式尽可能少地匹配所搜索的字符串,而默认的贪婪模式则尽可能多地匹配所搜索的字符串。例如,对于字符串“oooo”,“o+”将尽可能多地匹配“o”,得到结果[“oooo”],而“o+?”将尽可能少地匹配“o”,得到结果 ['o', 'o', 'o', 'o']

python中的re模块

有了前面的准备知识,我们就可以在 Python 中使用正则表达式了。 Python 提供re模块,包含所有正则表达式的功能。
由于 Python 的字符串本身也用\转义,所以要特别注意:

s = 'ABC\\-001' # Python 的字符串
# 对应的正则表达式字符串变成:
# 'ABC\-001'

因此我们强烈建议使用 Python 的 r 前缀(这里的r前缀表示对应的python字符串中不转义.),就不用考虑转义的问题了:

s = r'ABC\-001' # Python 的字符串
# 对应的正则表达式字符串不变:
# 'ABC\-001'

小结:python的正则表达式语言也是用字符串来表示的,所以要确保得到的正则表达式字符串正确,得到正则表达式字符串后,按照正则表达式的语法进行匹配解码,在匹配解码过程中正则表达式字符串还有可能进行正则表达式的转义.

使用re.match判断是否匹配

使用re模块中的match函数表示是否匹配

  • re.match函数的第一个参数为正则表达式,
  • 第二个参数为我们要判断的字符串,
  • 如果匹配成功的话,返回一个Match对象,否则返回None
>>> import re
>>> re.match(r'^\d{3}\-\d{3,8}$', '010-12345')
<_sre.SRE_Match object; span=(0, 9), match='010-12345'>

常见的判断方法就是:

test = '用户输入的字符串'
if re.match(r'正则表达式', test):
    print('ok')
else:
    print('failed')

使用re.split切分字符串

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

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

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

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

使用re模块中的re.split函数进行字符串切分

  • 第一个参数为正则表达式,
  • 第二个参数为我们要切分的字符串.

再试试:

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

再再试试:

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

使用re.match分组提取子串

除了简单地判断是否匹配之外,正则表达式还有提取子串的强大功能用()表示的就是要提取的分组( Group)。比如:

>>> m = re.match(r'^(\d{3})-(\d{3,8})$', '010-12345')
>>> m
<_sre.SRE_Match object; span=(0, 9), match='010-12345'>
>>> m.group(0)
'010-12345'
>>> m.group(1)
'010'
>>> m.group(2)
'12345

如果正则表达式中定义了组,就可以在 Match 对象上用 group()方法提取出子串来,

  • 用groups()方法提取出所有的要提取的子串出来。
  • 注意到 group(0)永远是原始字符串,
  • group(1)、 group(2)……表示第 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', '05', '30')

这个正则表达式可以直接识别合法的时间。

使用re.compile编译正则表达式

当我们在 Python 中使用正则表达式时, re 模块内部会干两件事情:

  • 编译正则表达式,如果正则表达式的字符串本身不合法,会报错
  • 用编译后的正则表达式去匹配字符串

如果一个正则表达式要重复使用几千次,出于效率的考虑,我们可以预编译该正则表达式,接下来重复使用时就不需要编译这个步骤了,直接匹配:

>>> import re
# 编译:
>>> re_telephone = re.compile(r'^(\d{3})-(\d{3,8})$')
# 使用:
>>> re_telephone.match('010-12345').groups()
('010', '12345')
>>> re_telephone.match('010-8086').groups()
('010', '8086')

编译后生成 Regular Expression 对象,由于该对象自己包含了正则表达式,所以调用对应的方法时不用给出正则字符串

正则表达式贪婪匹配

正则匹配默认是贪婪匹配,也就是匹配尽可能多的字符。

>>> re.match(r'^(\d+)(0*)$', '102300').groups()
('102300', '')

由于\d+采用贪婪匹配,直接把后面的 0 全部匹配了,结果 0*只能匹配空字符串了。
必须让\d+采用非贪婪匹配(也就是尽可能少匹配,这里的尽可能少匹配指的是在能匹配的前提下尽可能少匹配),才能把后面的 0匹配出来,加个?就可以让\d+采用非贪婪匹配

>>> re.match(r'^(\d+?)(0*)$', '102300').groups()
('1023', '00')

posted on 2021-10-17 17:09  朴素贝叶斯  阅读(85)  评论(0编辑  收藏  举报

导航