正则表达式操作--re

本文深入解析Python正则表达式的语法与用法,包括重复限定符、特殊字符及正则表达式对象的详细说明。并通过多个示例,如扑克牌验证、电话簿制作、文本捣蛋等,展示re模块的函数如match、search、split、findall的实际应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

参考1: python的re模块总结
参考2: python 详解re模块
参考3: Python标准库之 re - 正则表达式操作

Python用“\\\\”表示正则表达式中的“\”,因为正则表达式中要匹配“\”,需要用 \ 来进行转义,变成“\\”,而Python语法中又需要对字符串中每一个 \ 进行转义,所以就变成了“\\\\”。
为了使正则具有更好的可读性,Python特地设计了原始字符串。如 r(’\n’) ,表示两个字符:‘\’ 和 ‘n’。

1、正则表达式语法

  • 通过 re 模块中的函数,检查特定字符串是否与给定的正则表达式匹配。
  • 正则表达式可以连接起来形成新的正则表达式;
    如果A 和B都是正则表达式,那么AB也是正则表达式。
    通常,如果p匹配A,q匹配B,则pq将匹配AB。【其中p、q为字符串】

(1)重复限定符(*,+,?,{m,n},等等)【例如,表达式(?:a{6})*匹配六个’a’字符的任意倍数。】

*     对于前一个字符重复0到无穷次
+     对于前一个字符重复1到无穷次
?    对于前一个字符重复0到1次
{m,n} 对于前一个字符重复次数在为m到n次,其中,{0,} = *,   {1,} = +,  {0,1} = ?
{m}   对于前一个字符重复m次

(2)特殊字符:

'.'  在默认模式下,它匹配除换行符之外的任何字符。
'^'  匹配字符串的开头。(还匹配非关系,如:[^5]将匹配除了'5'之外的任何字符)
'$'  匹配字符串的结尾。
[]   用于表示一组字符。在一组:
          如:[amk]将匹配'a', 'm'或'k'。
          如:[0-5][0-9]将所有的后两位数字从匹配00到59
'|'   A|B,其中A和B可以是任意re
(...)  匹配括号内的正则表达式


\number  匹配相同编号的组的内容。组从1开始编号。
\A  仅匹配字符串的开头。
\b  匹配空字符串,但仅匹配单词的开头或结尾
\B  匹配空字符串,但仅当它不在单词的开头或结尾时。
\d  匹配任何十进制数字; 这相当于集合[0-9]。
\D  匹配任何非数字字符; 这相当于集合 [^0-9]。
\s  它与任何空白字符匹配
\S  匹配任何非空白字符
\w  匹配任何字母数字字符和下划线; 这相当于[a-zA-Z0-9_]。
\W  匹配任何非字母数字字符; 这相当于[^a-zA-Z0-9_]。
\Z  仅匹配字符串末尾的匹配项。

2、re 模块内容–(函数,常量和异常)

re.compile(pattern,flags = 0 )
将正则表达式模式编译为正则表达式对象,可以使用它match()和 search()方法进行匹配。

prog = re.compile(pattern)
result = prog.match(string)

相当于:
result = re.match(pattern, string)

注意: re.compile(),当在单个程序中多次使用表达式时,使用和保存生成的正则表达式对象以便重用会更有效。

re.search(pattern,string,flags = 0 )
扫描字符串,查找正则表达式模式生成匹配项的第一个位置 ,并返回相应的MatchObject 实例。如果字符串中没有位置与模式匹配则返回None。
注意: 这与在字符串中的某个点找到零长度匹配不同。

re.match(pattern,string,flags = 0 )
如果字符串开头的零个或多个字符与正则表达式模式匹配,则返回相应的MatchObject实例。如果字符串与模式不匹配则返回None。
注意: 这与零长度匹配不同。

re.split(pattern,string,maxsplit = 0,flags = 0 )
按模式的出现拆分字符串。如果在模式中使用捕获括号,则模式中所有组的文本也将作为结果列表的一部分返回。如果maxsplit非零,则最多 发生maxsplit拆分,并且字符串的其余部分将作为列表的最后一个元素返回。

>>> re.split('\W+', 'Words, words, words.')
['Words', 'words', 'words', '']

>>> re.split('(\W+)', 'Words, words, words.')
['Words', ', ', 'words', ', ', 'words', '.', '']

>>> re.split('\W+', 'Words, words, words.', 1)
['Words', 'words, words.']

>>> re.split('[a-f]+', '0a3B9', flags=re.IGNORECASE)
['0', '3', '9']

如果分隔符中有捕获组并且它在字符串的开头匹配,则结果将以空字符串开头。对于字符串的结尾也是如此:

>>> re.split('(\W+)', '...words, words...')
['', '...', 'words', ', ', 'words', '...', '']

这样,分隔符组件总是在结果列表中的相同相对索引处找到(例如,如果分隔符中有一个捕获组,则第0个,第2个等等)。

注意: 拆分永远不会在空模式匹配上拆分字符串。

>>> re.split('x*', 'foo')
['foo']

>>> re.split("(?m)^$", "foo\n\nbar\n")
['foo\n\nbar\n']

re.findall(pattern,string,flags = 0 )
返回的所有非重叠的匹配模式的字符串,如字符串列表。该字符串进行扫描左到右,并匹配以发现的顺序返回。如果模式中存在一个或多个组,则返回组列表; 如果模式有多个组,这将是一个元组列表。结果中包含空匹配。

re.finditer(pattern,string,flags = 0 )
返回一个迭代器,在字符串中MatchObject的RE 模式的所有非重叠匹配上产生实例。该字符串进行扫描左到右,并匹配以发现的顺序返回。结果中包含空匹配。

re.sub(pattern,repl,string,count = 0,flags = 0 )
返回通过替换repl替换字符串中最左边的非重叠模式而获得的字符串。如果未找到模式, 则返回字符串不变。 repl可以是字符串或函数; 如果它是一个字符串,则处理其中的任何反斜杠转义。也就是说,转换为单个换行符,转换为回车符,依此类推。

>>> re.sub(r'def\s+([a-zA-Z_][a-zA-Z_0-9]*)\s*\(\s*\):',
...        r'static PyObject*\npy_\1(void)\n{',
...        'def myfunc():')
'static PyObject*\npy_myfunc(void)\n{'

如果repl是一个函数,则会为每个非重叠的模式调用调用它 。该函数接受单个匹配对象参数,并返回替换字符串。

>>> def dashrepl(matchobj):
...     if matchobj.group(0) == '-': return ' '
...     else: return '-'
>>> re.sub('-{1,2}', dashrepl, 'pro----gram-files')
'pro--gram files'
>>> re.sub(r'\sAND\s', ' & ', 'Baked Beans And Spam', flags=re.IGNORECASE)
'Baked Beans & Spam'

re.subn(pattern,repl,string,count = 0,flags = 0 )
执行相同的操作sub(),但返回元组。(new_string, number_of_subs_made)

re.escape(pattern)
除ASCII字母和数字外,转义模式中的所有字符。如果要匹配可能包含正则表达式元字符的任意文字字符串,这将非常有用。

>>> print re.escape('python.exe')
python\.exe

>>> legal_chars = string.ascii_lowercase + string.digits + "!#$%&'*+-.^_`|~:"
>>> print '[%s]+' % re.escape(legal_chars)
[abcdefghijklmnopqrstuvwxyz0123456789\!\#\$\%\&\'\*\+\-\.\^\_\`\|\~\:]+

>>> operators = ['+', '-', '*', '/', '**']
>>> print '|'.join(map(re.escape, sorted(operators, reverse=True)))
\/|\-|\+|\*\*|\*

re.purge()
清除正则表达式缓存。

3、正则表达式对象–(RegexObject)

class re.RegexObject
本RegexObject类支持下列方法和属性:

search(string [,pos [,endpos ] ] )
扫描字符串,查找此正则表达式生成匹配项的位置,并返回相应的MatchObject实例。如果字符串中没有位置与模式匹配则返回None;

>>> pattern = re.compile("d")
>>> pattern.search("dog")     # Match at index 0
<_sre.SRE_Match object at ...>
>>> pattern.search("dog", 1)  # No match; search doesn't include the "d"

match(string [,pos [,endpos ] ] )
如果字符串开头的零个或多个字符与此正则表达式匹配,则返回相应的实例。如果字符串与模式不匹配则返回 None;

>>> pattern = re.compile("o")
>>> pattern.match("dog")      # No match as "o" is not at the start of "dog".
>>> pattern.match("dog", 1)   # Match as "o" is the 2nd character of "dog".
<_sre.SRE_Match object at ...>

split(string,maxsplit = 0 )

findall(string [,pos [,endpos ] ] )

finditer(string [,pos [,endpos ] ] )

sub(repl,string,count = 0 )

subn(repl,string,count = 0 )

flags 正则表达式匹配标志。这是给定的标志compile()和(?..)模式中的任何内联标志的组合 。

groups 模式中捕获组的数量。

groupindex 字典映射由(?P)组号定义的任何符号组名称。如果模式中没有使用符号组,则字典为空。

pattern 从中编译RE对象的模式字符串。

4、Match 对象

class re.MatchObject
Match 对象的布尔值始终为True

match = re.search(pattern, string)
if match:
    process(match)

Match 对象支持以下方法和属性:
expand(pattern)
返回通过在模板字符串模板上执行反斜杠替换获得的字符串,如sub()方法所做。

group([ group1,… ] )
返回匹配的一个或多个子组。如果只有一个参数,则结果为单个字符串; 如果有多个参数,则结果是一个元组。

>>> m = re.match(r"(\w+) (\w+)", "Isaac Newton, physicist")
>>> m.group(0)       # The entire match
'Isaac Newton'
>>> m.group(1)       # The first parenthesized subgroup.
'Isaac'
>>> m.group(2)       # The second parenthesized subgroup.
'Newton'
>>> m.group(1, 2)    # Multiple arguments give us a tuple.
('Isaac', 'Newton')

一个中等复杂的例子:

>>> m = re.match(r"(?P<first_name>\w+) (?P<last_name>\w+)", "Malcolm Reynolds")
>>> m.group('first_name')
'Malcolm'
>>> m.group('last_name')
'Reynolds'

命名组也可以通过其索引引用:

>>> m.group(1)
'Malcolm'
>>> m.group(2)
'Reynolds'

如果一个组匹配多次,则只能访问最后一个匹配:

>>> m = re.match(r"(..)+", "a1b2c3")  # Matches 3 times.
>>> m.group(1)                        # Returns only the last match.
'c3'

groups([ 默认] )
返回包含匹配的所有子组的元组,从1到多个组都在模式中。该默认参数用于那些没有参加比赛组; 它默认为None。

>>> m = re.match(r"(\d+)\.(\d+)", "24.1632")
>>> m.groups()
('24', '1632')

如果我们创建小数位并且后面的所有内容都是可选的,那么并非所有组都可以参与匹配。除非给出默认参数,否则这些组将默认为None:

>>> m = re.match(r"(\d+)\.?(\d+)?", "24")
>>> m.groups()      # Second group defaults to None.
('24', None)

>>> m.groups('0')   # Now, the second group defaults to '0'.
('24', '0')

groupdict([ 默认] )
返回包含匹配的所有已命名子组的字典,由子组名称键入。该默认参数用于那些没有参加比赛组; 它默认为None。

>>> m = re.match(r"(?P<first_name>\w+) (?P<last_name>\w+)", "Malcolm Reynolds")
>>> m.groupdict()
{'first_name': 'Malcolm', 'last_name': 'Reynolds'}

start([ 组] )
end([ 组] )
返回由group匹配的子字符串的开始和结束的索引; group默认为零

m.string[m.start(g):m.end(g)]

注意: 如果组匹配空字符串,m.start(group)则相等。
一个将从电子邮件地址中删除remove_this的示例:

>>> email = "tony@tiremove_thisger.net"
>>> m = re.search("remove_this", email)
>>> email[:m.start()] + email[m.end():]
'tony@tiger.net'

span([ 组] )
对于m,返回2元组。请注意,如果组没有为匹配做出贡献,那么就是这样 。 组默认为零,整个匹配。MatchObject (m.start(group), m.end(group))(-1, -1)

pos
endpos
lastindex
lastgroup

re
正则表达式对象,其方法match()或 search()方法生成此MatchObject 实例。

5、示例

(1)检查对

在这个例子中,我们将使用以下辅助函数来更优雅地显示匹配对象:

def displaymatch(match):
    if match is None:
        return None
    return '<Match: %r, groups=%r>' % (match.group(), match.groups())

假设您正在编写扑克程序,其中玩家的手被表示为5个字符的字符串,每个字符代表一张牌,“a”代表王牌,“k”代表国王,“q”代表女王,“j”代表杰克, “t”代表10,“2”-“9”代表具有该值的卡片。

要查看给定字符串是否是有效的手,可以执行以下操作:

>>> valid = re.compile(r"^[a2-9tjqk]{5}$")
>>> displaymatch(valid.match("akt5q"))  # Valid.
"<Match: 'akt5q', groups=()>"
>>> displaymatch(valid.match("akt5e"))  # Invalid.
>>> displaymatch(valid.match("akt"))    # Invalid.
>>> displaymatch(valid.match("727ak"))  # Valid.
"<Match: '727ak', groups=()>"

最后一手牌,"727ak"包含一对或两张相同价值的牌。要将其与正则表达式匹配,可以使用反向引用:

>>> pair = re.compile(r".*(.).*\1")
>>> displaymatch(pair.match("717ak"))     # Pair of 7s.
"<Match: '717', groups=('7',)>"
>>> displaymatch(pair.match("718ak"))     # No pairs.
>>> displaymatch(pair.match("354aa"))     # Pair of aces.
"<Match: '354aa', groups=('a',)>"

要找出对所包含的内容卡,一个可以使用 group()的方法MatchObject以下列方式:

>>> pair.match("717ak").group(1)
'7'

 Error because re.match() returns None, which doesn't have a group() method:
>>> pair.match("718ak").group(1)
Traceback (most recent call last):
  File "<pyshell#23>", line 1, in <module>
    re.match(r".*(.).*\1", "718ak").group(1)
AttributeError: 'NoneType' object has no attribute 'group'

>>> pair.match("354aa").group(1)
'a'
(2)模拟scanf()

Python目前没有等效的scanf()。正则表达式通常比scanf()格式字符串更强大,但也更冗长 。下表提供了scanf()格式标记和正则表达式之间的一些或多或少的等效映射。

%c     .
%5c 	.{5}
%d  	[-+]?\d+
%e,%E,%f,%g      	[-+]?(\d+(\.\d*)?|\.\d+)([eE][-+]?\d+)?
%i	    [-+]?(0[xX][\dA-Fa-f]+|0[0-7]*|\d+)
%o	    [-+]?[0-7]+
%s  	\S+
%u	   \d+
%x, %X	[-+]?(0[xX])?[\dA-Fa-f]+

从字符串中提取文件名和数字

/usr/sbin/sendmail - 0 errors, 4 warnings

你会使用scanf()像这样的格式

%s - %d errors, %d warnings

等价的正则表达式将是

(\S+) - (\d+) errors, (\d+) warnings
(3)search()与match()

Python提供了两种基于正则表达式的基本操作: re.match()仅在字符串的开头re.search()检查匹配,同时检查字符串 中任何位置的匹配(这是Perl默认执行的操作)。

例如:

>>> re.match("c", "abcdef")    # No match
>>> re.search("c", "abcdef")   # Match
<_sre.SRE_Match object at ...>

以逗号开头的正则表达式’^'可用于search()限制字符串开头的匹配:

>>> re.match("c", "abcdef")    # No match
>>> re.search("^c", "abcdef")  # No match
>>> re.search("^a", "abcdef")  # Match
<_sre.SRE_Match object at ...>

但请注意,在MULTILINE模式中match()仅匹配字符串的开头,而使用search()带有开头的正则表达式’^'将匹配每行的开头。

>>> re.match('X', 'A\nB\nX', re.MULTILINE)  # No match
>>> re.search('^X', 'A\nB\nX', re.MULTILINE)  # Match
<_sre.SRE_Match object at ...>
(4)制作电话簿

split()将字符串拆分为由传递的模式分隔的列表。该方法对于将文本数据转换为可由Python轻松读取和修改的数据结构非常有用,如以下创建电话簿的示例所示。

首先,这是输入。通常它可能来自一个文件,这里我们使用三引号字符串语法:

>>> text = """Ross McFluff: 834.345.1254 155 Elm Street
...
... Ronald Heathmore: 892.345.3428 436 Finley Avenue
... Frank Burger: 925.541.7625 662 South Dogwood Way
...
...
... Heather Albrecht: 548.326.4584 919 Park Place"""

条目由一个或多个换行符分隔。现在我们将字符串转换为列表,每个非空行都有自己的条目:

>>> entries = re.split("\n+", text)
>>> entries
['Ross McFluff: 834.345.1254 155 Elm Street',
'Ronald Heathmore: 892.345.3428 436 Finley Avenue',
'Frank Burger: 925.541.7625 662 South Dogwood Way',
'Heather Albrecht: 548.326.4584 919 Park Place']

最后,将每个条目拆分为包含名字,姓氏,电话号码和地址的列表。我们使用maxsplit参数,split() 因为地址有空格,我们的分裂模式,在其中:

>>> [re.split(":? ", entry, 3) for entry in entries]
[['Ross', 'McFluff', '834.345.1254', '155 Elm Street'],
['Ronald', 'Heathmore', '892.345.3428', '436 Finley Avenue'],
['Frank', 'Burger', '925.541.7625', '662 South Dogwood Way'],
['Heather', 'Albrecht', '548.326.4584', '919 Park Place']]

该:?模式在姓氏后结肠匹配,这样才不会在结果列表中出现。随着maxsplit中4,我们可以分开的,街道名称门牌号码:

>>> [re.split(":? ", entry, 4) for entry in entries]
[['Ross', 'McFluff', '834.345.1254', '155', 'Elm Street'],
['Ronald', 'Heathmore', '892.345.3428', '436', 'Finley Avenue'],
['Frank', 'Burger', '925.541.7625', '662', 'South Dogwood Way'],
['Heather', 'Albrecht', '548.326.4584', '919', 'Park Place']]
(5)文字捣蛋

sub()用字符串或函数的结果替换模式的每次出现。此示例演示如何使用sub()函数“munge”文本,或者随机化句子中每个单词中除第一个和最后一个字符之外的所有字符的顺序:

>>> def repl(m):
...     inner_word = list(m.group(2))
...     random.shuffle(inner_word)
...     return m.group(1) + "".join(inner_word) + m.group(3)
>>> text = "Professor Abdolmalek, please report your absences promptly."
>>> re.sub(r"(\w)(\w+)(\w)", repl, text)
'Poefsrosr Aealmlobdk, pslaee reorpt your abnseces plmrptoy.'
>>> re.sub(r"(\w)(\w+)(\w)", repl, text)
'Pofsroser Aodlambelk, plasee reoprt yuor asnebces potlmrpy.'
(6)找到所有副词

findall()匹配所有出现的模式,而不仅仅是第一个模式search()。例如,如果作家想要在某些文本中找到所有副词,他们可能会findall()以下列方式使用:

>>> text = "He was carefully disguised but captured quickly by police."
>>> re.findall(r"\w+ly", text)
['carefully', 'quickly']
(7)查找所有副词及其位置

如果想要获得关于模式的所有匹配的更多信息而不是匹配的文本,finditer()那么它是有用的,因为它提供了MatchObject代替字符串的实例 。继续前面的例子,如果作家想要 在某些文本中找到所有副词及其位置,他们将按finditer()以下方式使用:

>>> text = "He was carefully disguised but captured quickly by police."
>>> for m in re.finditer(r"\w+ly", text):
...     print '%02d-%02d: %s' % (m.start(), m.end(), m.group(0))
07-16: carefully
40-47: quickly
(8)原始字符串表示法

原始字符串表示法(r"text")保持正则表达式理智。没有它,’'正则表达式中的每个反斜杠()都必须以另一个为前缀来转义它。例如,以下两行代码在功能上是相同的:

>>> re.match(r"\W(.)\1\W", " ff ")
<_sre.SRE_Match object at ...>
>>> re.match("\\W(.)\\1\\W", " ff ")
<_sre.SRE_Match object at ...>

当想要匹配文字反斜杠时,必须在正则表达式中对其进行转义。使用原始字符串表示法,这意味着r"\"。如果没有原始字符串表示法,必须使用"\\",使以下代码行功能相同:

>>> re.match(r"\\", r"\\")
<_sre.SRE_Match object at ...>
>>> re.match("\\\\", r"\\")
<_sre.SRE_Match object at ...>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值