>>> import re
>>> m = re.search('(?<=abc)def', 'abcdef')
>>> m.group(0)
'def'
这个例子搜索一个跟随在连字符后的单词:
>>> m = re.search(r'(?<=-)\w+', 'spam-egg')
>>> m.group(0)
'egg'
3.5 版更變: 添加定长组合引用的支持。
(?<!…)
匹配当前位置之前不是 …
的样式。这个叫:dfn:negative lookbehind assertion (后视断定取非)。类似正向后视断定,包含的样式匹配必须是定长的。由 negative lookbehind assertion 开始的样式可以从字符串搜索开始的位置进行匹配。
(?(id/name)yes-pattern|no-pattern)
如果给定的 id 或 name 存在,将会尝试匹配 yes-pattern
,否则就尝试匹配 no-pattern
,no-pattern
可选,也可以被忽略。比如, (<)?(\w+@\w+(?:\.\w+)+)(?(1)>|$)
是一个email样式匹配,将匹配 '<[email protected]>'
或 '[email protected]'
,但不会匹配 '<[email protected]'
,也不会匹配 '[email protected]>'
。
由 '\'
和一个字符组成的特殊序列在以下列出。 如果普通字符不是ASCII数位或者ASCII字母,那么正则样式将匹配第二个字符。比如,\$
匹配字符 '$'
.
\number
匹配数字代表的组合。每个括号是一个组合,组合从1开始编号。比如 (.+) \1
匹配 'the the'
或者 '55 55'
, 但不会匹配 'thethe'
(注意组合后面的空格)。这个特殊序列只能用于匹配前面99个组合。如果 number 的第一个数位是0, 或者 number 是三个八进制数,它将不会被看作是一个组合,而是八进制的数字值。在 '['
和 ']'
字符集合内,任何数字转义都被看作是字符。
\A
只匹配字符串开始。
\b
匹配空字符串,但只在单词开始或结尾的位置。一个单词被定义为一个单词字符的序列。注意,通常 \b
定义为 \w
和 \W
字符之间,或者 \w
和字符串开始/结尾的边界, 意思就是 r'\bfoo\b'
匹配 'foo'
, 'foo.'
, '(foo)'
, 'bar foo baz'
但不匹配 'foobar'
或者 'foo3'
。
默认情况下,Unicode字母和数字是在Unicode样式中使用的,但是可以用 ASCII
标记来更改。如果 LOCALE
标记被设置的话,词的边界是由当前语言区域设置决定的,\b
表示退格字符,以便与Python字符串文本兼容。
\B
匹配空字符串,但 不 能在词的开头或者结尾。意思就是 r'py\B'
匹配 'python'
, 'py3'
, 'py2'
, 但不匹配 'py'
, 'py.'
, 或者 'py!'
. \B
是 \b
的取非,所以Unicode样式的词语是由Unicode字母,数字或下划线构成的,虽然可以用 ASCII
标志来改变。如果使用了 LOCALE
标志,则词的边界由当前语言区域设置。
对于 Unicode (str) 样式:Matches any Unicode decimal digit (that is, any character in
Unicode character category [Nd]). This includes [0-9]
, and
also many other digit characters. If the ASCII
flag is
used only [0-9]
is matched (but the flag affects the entire
regular expression, so in such cases using an explicit [0-9]
may be a better choice).
对于8位(bytes)样式:匹配任何十进制数,就是 [0-9]
。
\D
Matches any character which is not a decimal digit. This is
the opposite of \d
. If the ASCII
flag is used this
becomes the equivalent of [^0-9]
(but the flag affects the entire
regular expression, so in such cases using an explicit [^0-9]
may
be a better choice).
对于 Unicode (str) 样式:Matches Unicode whitespace characters (which includes
[ \t\n\r\f\v]
, and also many other characters, for example the
non-breaking spaces mandated by typography rules in many
languages). If the ASCII
flag is used, only
[ \t\n\r\f\v]
is matched (but the flag affects the entire
regular expression, so in such cases using an explicit
[ \t\n\r\f\v]
may be a better choice).
对于8位(bytes)样式:匹配ASCII中的空白字符,就是 [ \t\n\r\f\v]
。
\S
Matches any character which is not a whitespace character. This is
the opposite of \s
. If the ASCII
flag is used this
becomes the equivalent of [^ \t\n\r\f\v]
(but the flag affects the entire
regular expression, so in such cases using an explicit [^ \t\n\r\f\v]
may
be a better choice).
对于 Unicode (str) 样式:Matches Unicode word characters; this includes most characters
that can be part of a word in any language, as well as numbers and
the underscore. If the ASCII
flag is used, only
[a-zA-Z0-9_]
is matched (but the flag affects the entire
regular expression, so in such cases using an explicit
[a-zA-Z0-9_]
may be a better choice).
对于8位(bytes)样式:匹配ASCII字符中的数字和字母和下划线,就是 [a-zA-Z0-9_]
。如果设置了 LOCALE
标记,就匹配当前语言区域的数字和字母和下划线。
\W
Matches any character which is not a word character. This is
the opposite of \w
. If the ASCII
flag is used this
becomes the equivalent of [^a-zA-Z0-9_]
(but the flag affects the
entire regular expression, so in such cases using an explicit
[^a-zA-Z0-9_]
may be a better choice). If the LOCALE
flag is
used, matches characters considered alphanumeric in the current locale
and the underscore.
\Z
只匹配字符串尾。
绝大部分Python的标准转义字符也被正则表达式分析器支持。:
\a \b \f \n
\r \t \u \U
\v \x \\
(注意 \b
被用于表示词语的边界,它只在字符集合内表示退格,比如 [\b]
。)
'\u'
and '\U'
escape sequences are only recognized in Unicode
patterns. In bytes patterns they are errors.
八进制转义包含为一个有限形式。如果首位数字是 0, 或者有三个八进制数位,那么就认为它是八进制转义。其他的情况,就看作是组引用。对于字符串文本,八进制转义最多有三个数位长。
3.3 版更變: 增加了 '\u'
和 '\U'
转义序列。
3.6 版更變: 由 '\'
和一个ASCII字符组成的未知转义会被看成错误。
6.2.2. 模块内容
模块定义了几个函数,常量,和一个例外。有些函数是编译后的正则表达式方法的简化版本(少了一些特性)。绝大部分重要的应用,总是会先将正则表达式编译,之后在进行操作。
3.6 版更變: 标志常量现在是 RegexFlag
类的实例,这个类是 enum.IntFlag
的子类。
re.
compile
(pattern, flags=0)
Compile a regular expression pattern into a regular expression object, which can be used for matching using its
match()
, search()
and other methods, described
below.
这个表达式的行为可以通过指定 标记 的值来改变。值可以是以下任意变量,可以通过位的OR操作来结合( |
操作符)。
prog = re.compile(pattern)
result = prog.match(string)
result = re.match(pattern, string)
如果需要多次使用这个正则表达式的话,使用 re.compile()
和保存这个正则对象以便复用,可以让程序更加高效。
通过 re.compile()
编译后的样式,和模块级的函数会被缓存, 所以少数的正则表达式使用无需考虑编译的问题。
re.
ASCII
让 \w
, \W
, \b
, \B
, \d
, \D
, \s
和 \S
只匹配ASCII,而不是Unicode。这只对Unicode样式有效,会被byte样式忽略。相当于前面语法中的内联标志 (?a)
。
注意,为了保持向后兼容, re.U
标记依然存在(还有他的同义 re.UNICODE
和嵌入形式 (?u)
) , 但是这些在 Python 3 是冗余的,因为默认字符串已经是Unicode了(并且Unicode匹配不允许byte出现)。
re.
IGNORECASE
进行忽略大小写匹配;表达式如 [A-Z]
也会匹配小写字符。Unicode匹配(比如 Ü
匹配 ü
)同样有用,除非设置了 re.ASCII
标记来禁用非ASCII匹配。当前语言区域不会改变这个标记,除非设置了 re.LOCALE
标记。这个相当于内联标记 (?i)
。
Note that when the Unicode patterns [a-z]
or [A-Z]
are used in
combination with the IGNORECASE
flag, they will match the 52 ASCII
letters and 4 additional non-ASCII letters: 『İ』 (U+0130, Latin capital
letter I with dot above), 『ı』 (U+0131, Latin small letter dotless i),
『ſ』 (U+017F, Latin small letter long s) and 『K』 (U+212A, Kelvin sign).
If the ASCII
flag is used, only letters 『a』 to 『z』
and 『A』 to 『Z』 are matched (but the flag affects the entire regular
expression, so in such cases using an explicit (?-i:[a-zA-Z])
may be
a better choice).
re.
LOCALE
由当前语言区域决定 \w
, \W
, \b
, \B
和大小写敏感匹配。这个标记只能对byte样式有效。这个标记不推荐使用,因为语言区域机制很不可靠,它一次只能处理一个 「习惯”,而且只对8位字节有效。Unicode匹配在Python 3 里默认启用,并可以处理不同语言。 这个对应内联标记 (?L)
。
3.6 版更變: re.LOCALE
只能用于byte样式,而且不能和 re.ASCII
一起用。
re.
VERBOSE
这个标记允许你编写更具可读性更友好的正则表达式。通过分段和添加注释。空白符号会被忽略,除非在一个字符集合当中或者由反斜杠转义,或者在 *?
, (?:
or (?P<…>
分组之内。当一个行内有 #
不在字符集和转义序列,那么它之后的所有字符都是注释。
意思就是下面两个正则表达式等价地匹配一个十进制数字:
a = re.compile(r"""\d + # the integral part
\. # the decimal point
\d * # some fractional digits""", re.X)
b = re.compile(r"\d+\.\d*")
对应内联标记 (?x)
。
re.
match
(pattern, string, flags=0)
如果 string 开始的0或者多个字符匹配到了正则表达式样式,就返回一个相应的 匹配对象 。 如果没有匹配,就返回 None
;注意它跟零长度匹配是不同的。
注意即便是 MULTILINE
多行模式, re.match()
也只匹配字符串的开始位置,而不匹配每行开始。
如果你想定位 string 的任何位置,使用 search()
来替代(也可参考 search() vs. match() )
re.
fullmatch
(pattern, string, flags=0)
如果整个 string 匹配到正则表达式样式,就返回一个相应的 匹配对象 。 否则就返回一个 None
;注意这跟零长度匹配是不同的。
3.4 版新加入.
re.
split
(pattern, string, maxsplit=0, flags=0)
用 pattern 分开 string 。 如果在 pattern 中捕获到括号,那么所有的组里的文字也会包含在列表里。如果 maxsplit 非零, 最多进行 maxsplit 次分隔, 剩下的字符全部返回到列表的最后一个元素。
>>> re.split(r'\W+', 'Words, words, words.')
['Words', 'words', 'words', '']
>>> re.split(r'(\W+)', 'Words, words, words.')
['Words', ', ', 'words', ', ', 'words', '.', '']
>>> re.split(r'\W+', 'Words, words, words.', 1)
['Words', 'words, words.']
>>> re.split('[a-f]+', '0a3B9', flags=re.IGNORECASE)
['0', '3', '9']
如果分隔符里有捕获组合,并且匹配到字符串的开始,那么结果将会以一个空字符串开始。对于结尾也是一样
>>> re.split(r'(\W+)', '...words, words...')
['', '...', 'words', ', ', 'words', '...', '']
这样的话,分隔组将会出现在结果列表中同样的位置。
split()
doesn’t currently split a string on an empty pattern match.
For example:
>>> re.split('x*', 'axbc')
['a', 'bc']
Even though 'x*'
also matches 0 『x』 before 『a』, between 『b』 and 『c』,
and after 『c』, currently these matches are ignored. The correct behavior
(i.e. splitting on empty matches too and returning ['', 'a', 'b', 'c',
'']
) will be implemented in future versions of Python, but since this
is a backward incompatible change, a FutureWarning
will be raised
in the meanwhile.
Patterns that can only match empty strings currently never split the
string. Since this doesn’t match the expected behavior, a
ValueError
will be raised starting from Python 3.5:
>>> re.split("^$", "foo\n\nbar\n", flags=re.M)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: split() requires a non-empty pattern match.
re.
findall
(pattern, string, flags=0)
对 string 返回一个不重复的 pattern 的匹配列表, string 从左到右进行扫描,匹配按找到的顺序返回。如果样式里存在一到多个组,就返回一个组合列表;就是一个元组的列表(如果样式里有超过一个组合的话)。空匹配也会包含在结果里。
Due to the limitation of the current implementation the character
following an empty match is not included in a next match, so
findall(r'^|\w+', 'two words')
returns ['', 'wo', 'words']
(note missed 「t」). This is changed in Python 3.7.
re.
finditer
(pattern, string, flags=0)
Return an iterator yielding match objects over
all non-overlapping matches for the RE pattern in string. The string
is scanned left-to-right, and matches are returned in the order found. Empty
matches are included in the result. See also the note about findall()
.
re.
sub
(pattern, repl, string, count=0, flags=0)
Return the string obtained by replacing the leftmost non-overlapping occurrences
of pattern in string by the replacement repl. If the pattern isn’t found,
string is returned unchanged. repl can be a string or a function; if it is
a string, any backslash escapes in it are processed. That is, \n
is
converted to a single newline character, \r
is converted to a carriage return, and
so forth. Unknown escapes such as \&
are left alone. Backreferences, such
as \6
, are replaced with the substring matched by group 6 in the pattern.
For example:
>>> 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 是一个函数,那它会对每个非重复的 pattern 的情况调用。这个函数只能有一个 匹配对象 参数,并返回一个替换后的字符串。比如
>>> 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'
样式可以是一个字符串或者一个 样式对象 。
The optional argument count is the maximum number of pattern occurrences to be
replaced; count must be a non-negative integer. If omitted or zero, all
occurrences will be replaced. Empty matches for the pattern are replaced only
when not adjacent to a previous match, so sub('x*', '-', 'abc')
returns
'-a-b-c-'
.
在字符串类型的 repl 参数里,如上所述的转义和向后引用中,\g<name>
会使用命名组合 name
,(在 (?P<name>…)
语法中定义) \g<number>
会使用数字组;\g<2>
就是 \2
,但它避免了二义性,如 \g<2>0
。 \20
就会被解释为组20,而不是组2后面跟随一个字符 '0'
。向后引用 \g<0>
把 pattern 作为一整个组进行引用。
3.1 版更變: 增加了可选标记参数。
3.5 版更變: 不匹配的组合替换为空字符串。
3.6 版更變: pattern 中的未知转义(由 '\'
和一个 ASCII 字符组成)被视为错误。
Deprecated since version 3.5, will be removed in version 3.7: Unknown escapes in repl consisting of '\'
and an ASCII letter now raise
a deprecation warning and will be forbidden in Python 3.7.
re.
subn
(pattern, repl, string, count=0, flags=0)
行为与 sub()
相同,但是返回一个元组 (字符串, 替换次数)
.
3.1 版更變: 增加了可选标记参数。
3.5 版更變: 不匹配的组合替换为空字符串。
re.
escape
(pattern)
Escape all the characters in pattern except ASCII letters, numbers and '_'
.
This is useful if you want to match an arbitrary literal string that may
have regular expression metacharacters in it. For example:
>>> 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))))
\/|\-|\+|\*\*|\*
This functions must not be used for the replacement string in sub()
and subn()
, only backslashes should be escaped. For example:
>>> digits_re = r'\d+'
>>> sample = '/usr/sbin/sendmail - 0 errors, 12 warnings'
>>> print(re.sub(digits_re, digits_re.replace('\\', r'\\'), sample))
/usr/sbin/sendmail - \d+ errors, \d+ warnings
3.3 版更變: '_'
不再被转义。
exception re.
error
(msg, pattern=None, pos=None)
raise
一个例外。当传递到函数的字符串不是一个有效正则表达式的时候(比如,包含一个不匹配的括号)或者其他错误在编译时或匹配时产生。如果字符串不包含样式匹配,是不会被视为错误的。错误实例有以下附加属性:
msg
未格式化的错误消息。
regex.
search
(string[, pos[, endpos]])
扫描整个 string 寻找第一个匹配的位置, 并返回一个相应的 匹配对象。如果没有匹配,就返回 None
;注意它和零长度匹配是不同的。
可选的第二个参数 pos 给出了字符串中开始搜索的位置索引;默认为 0
,它不完全等价于字符串切片; '^'
样式字符匹配字符串真正的开头,和换行符后面的第一个字符,但不会匹配索引规定开始的位置。
可选参数 endpos 限定了字符串搜索的结束;它假定字符串长度到 endpos , 所以只有从 pos
到 endpos - 1
的字符会被匹配。如果 endpos 小于 pos,就不会有匹配产生;另外,如果 rx 是一个编译后的正则对象, rx.search(string, 0, 50)
等价于 rx.search(string[:50], 0)
。
>>> pattern = re.compile("d")
>>> pattern.search("dog") # Match at index 0
<_sre.SRE_Match object; span=(0, 1), match='d'>
>>> pattern.search("dog", 1) # No match; search doesn't include the "d"
regex.
match
(string[, pos[, endpos]])
如果 string 的 开始位置 能够找到这个正则样式的任意个匹配,就返回一个相应的 匹配对象。如果不匹配,就返回 None
;注意它与零长度匹配是不同的。
The optional pos and endpos parameters have the same meaning as for the
search()
method.
>>> 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; span=(1, 2), match='o'>
If you want to locate a match anywhere in string, use
search()
instead (see also search() vs. match()).
regex.
fullmatch
(string[, pos[, endpos]])
如果整个 string 匹配这个正则表达式,就返回一个相应的 匹配对象 。 否则就返回 None
; 注意跟零长度匹配是不同的。
The optional pos and endpos parameters have the same meaning as for the
search()
method.
>>> pattern = re.compile("o[gh]")
>>> pattern.fullmatch("dog") # No match as "o" is not at the start of "dog".
>>> pattern.fullmatch("ogre") # No match as not the full string matches.
>>> pattern.fullmatch("doggie", 1, 3) # Matches within given limits.
<_sre.SRE_Match object; span=(1, 3), match='og'>
3.4 版新加入.
6.2.4. 匹配对象
Match objects always have a boolean value of True
.
Since match()
and search()
return None
when there is no match, you can test whether there was a match with a simple
if
statement:
match = re.search(pattern, string)
if match:
process(match)
匹配对象支持以下方法和属性:
match.
expand
(template)
Return the string obtained by doing backslash substitution on the template
string template, as done by the sub()
method.
Escapes such as \n
are converted to the appropriate characters,
and numeric backreferences (\1
, \2
) and named backreferences
(\g<1>
, \g<name>
) are replaced by the contents of the
corresponding group.
3.5 版更變: 不匹配的组合替换为空字符串。
match.
group
([group1, ...])
返回一个或者多个匹配的子组。如果只有一个参数,结果就是一个字符串,如果有多个参数,结果就是一个元组(每个参数对应一个项),如果没有参数,组1默认到0(整个匹配都被返回)。 如果一个组N 参数值为 0,相应的返回值就是整个匹配字符串;如果它是一个范围 [1..99],结果就是相应的括号组字符串。如果一个组号是负数,或者大于样式中定义的组数,一个 IndexError
索引错误就 raise
。如果一个组包含在样式的一部分,并被匹配多次,就返回最后一个匹配。:
>>> 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')
如果正则表达式使用了 (?P<name>…)
语法, groupN 参数就也可能是命名组合的名字。如果一个字符串参数在样式中未定义为组合名,一个 IndexError
就 raise
。
一个相对复杂的例子
>>> 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.
match.
__getitem__
(g)
这个等价于 m.group(g)
。这允许更方便的引用一个匹配
>>> m = re.match(r"(\w+) (\w+)", "Isaac Newton, physicist")
>>> m[0] # The entire match
'Isaac Newton'
>>> m[1] # The first parenthesized subgroup.
'Isaac'
>>> m[2] # The second parenthesized subgroup.
'Newton'
3.6 版新加入.
match.
groups
(default=None)
返回一个元组,包含所有匹配的子组,在样式中出现的从1到任意多的组合。 default 参数用于不参与匹配的情况,默认为 None
。
>>> m = re.match(r"(\d+)\.(\d+)", "24.1632")
>>> m.groups()
('24', '1632')
如果我们使小数点可选,那么不是所有的组都会参与到匹配当中。这些组合默认会返回一个 None
,除非指定了 default 参数。
>>> 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')
match.
groupdict
(default=None)
返回一个字典,包含了所有的 命名 子组。key就是组名。 default 参数用于不参与匹配的组合;默认为 None
。 例如
>>> m = re.match(r"(?P<first_name>\w+) (?P<last_name>\w+)", "Malcolm Reynolds")
>>> m.groupdict()
{'first_name': 'Malcolm', 'last_name': 'Reynolds'}
match.
end
([group])
返回 group 匹配到的字串的开始和结束标号。group 默认为0(意思是整个匹配的子串)。如果 group 存在,但未产生匹配,就返回 -1
。对于一个匹配对象 m, 和一个未参与匹配的组 g ,组 g (等价于 m.group(g)
)产生的匹配是
m.string[m.start(g):m.end(g)]
注意 m.start(group)
将会等于 m.end(group)
,如果 group 匹配一个空字符串的话。比如,在 m = re.search('b(c?)', 'cba')
之后,m.start(0)
为 1, m.end(0)
为 2, m.start(1)
和 m.end(1)
都是 2, m.start(2)
raise 一个 IndexError
例外。
这个例子会从email地址中移除掉 remove_this
>>> email = "tony@tiremove_thisger.net"
>>> m = re.search("remove_this", email)
>>> email[:m.start()] + email[m.end():]
'[email protected]'
match.
pos
The value of pos which was passed to the search()
or
match()
method of a regex object. This is
the index into the string at which the RE engine started looking for a match.
match.
endpos
The value of endpos which was passed to the search()
or
match()
method of a regex object. This is
the index into the string beyond which the RE engine will not go.
if match is None:
return None
return '<Match: %r, groups=%r>' % (match.group(), match.groups())
假设你在写一个扑克程序,一个玩家的一手牌为五个字符的串,每个字符表示一张牌,」a」 就是 A, 「k」 K, 「q」 Q, 「j」 J, 「t」 为 10, 「2」 到 「9」 表示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',)>"
To find out what card the pair consists of, one could use the
group()
method of the match object in the following manner:
>>> pair.match("717ak").group(1)
# 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)
6.2.5.3. search() vs. match()
Python 提供了两种不同的操作:基于 re.match()
检查字符串开头,或者 re.search()
检查字符串的任意位置(默认Perl中的行为)。
>>> re.match("c", "abcdef") # No match
>>> re.search("c", "abcdef") # Match
<_sre.SRE_Match object; span=(2, 3), match='c'>
在 search()
中,可以用 '^'
作为开始来限制匹配到字符串的首位
>>> re.match("c", "abcdef") # No match
>>> re.search("^c", "abcdef") # No match
>>> re.search("^a", "abcdef") # Match
<_sre.SRE_Match object; span=(0, 1), match='a'>
注意 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; span=(4, 5), match='X'>
6.2.5.4. 建立一个电话本
split()
将字符串用参数传递的样式分隔开。这个方法对于转换文本数据到易读而且容易修改的数据结构,是很有用的,如下面的例子证明。
首先,这里是输入。通常是一个文件,这里我们用三引号字符串语法
>>> 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']
最终,将每个条目分割为一个由名字、姓氏、电话号码和地址组成的列表。我们为 split()
使用了 maxsplit
形参,因为地址中包含有被我们作为分割模式的空格符:
>>> [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']]
6.2.5.5. 文字整理
sub()
替换字符串中出现的样式的每一个实例。这个例子证明了使用 sub()
来整理文字,或者随机化每个字符的位置,除了首位和末尾字符
>>> 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.2.5.6. 找到所有副词
findall()
匹配样式 所有 的出现,不仅是像 search()
中的第一个匹配。比如,如果一个作者希望找到文字中的所有副词,他可能会按照以下方法用 findall()
>>> text = "He was carefully disguised but captured quickly by police."
>>> re.findall(r"\w+ly", text)
['carefully', 'quickly']
6.2.5.7. 找到所有副词和位置
如果需要匹配样式的更多信息, finditer()
可以起到作用,它提供了 匹配对象 作为返回值,而不是字符串。继续上面的例子,如果一个作者希望找到所有副词和它的位置,可以按照下面方法使用 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
6.2.5.8. 原始字符记法
原始字符串记法 (r"text"
) 保持正则表达式正常。否则,每个正则式里的反斜杠('\'
) 都必须前缀一个反斜杠来转义。比如,下面两行代码功能就是完全一致的
>>> re.match(r"\W(.)\1\W", " ff ")
<_sre.SRE_Match object; span=(0, 4), match=' ff '>
>>> re.match("\\W(.)\\1\\W", " ff ")
<_sre.SRE_Match object; span=(0, 4), match=' ff '>
当需要匹配一个字符反斜杠,它必须在正则表达式中转义。在原始字符串记法,就是 r"\\"
。否则就必须用 "\\\\"
,来表示同样的意思
>>> re.match(r"\\", r"\\")
<_sre.SRE_Match object; span=(0, 1), match='\\'>
>>> re.match("\\\\", r"\\")
<_sre.SRE_Match object; span=(0, 1), match='\\'>
6.2.5.9. 写一个词法分析器
一个 词法器或词法分析器 分析字符串,并分类成目录组。 这是写一个编译器或解释器的第一步。
文字目录是由正则表达式指定的。这个技术是通过将这些样式合并为一个主正则式,并且循环匹配来实现的
import collections
import re
Token = collections.namedtuple('Token', ['type', 'value', 'line', 'column'])
def tokenize(code):
keywords = {'IF', 'THEN', 'ENDIF', 'FOR', 'NEXT', 'GOSUB', 'RETURN'}
token_specification = [
('NUMBER', r'\d+(\.\d*)?'), # Integer or decimal number
('ASSIGN', r':='), # Assignment operator
('END', r';'), # Statement terminator
('ID', r'[A-Za-z]+'), # Identifiers
('OP', r'[+\-*/]'), # Arithmetic operators
('NEWLINE', r'\n'), # Line endings
('SKIP', r'[ \t]+'), # Skip over spaces and tabs
('MISMATCH', r'.'), # Any other character
tok_regex = '|'.join('(?P<%s>%s)' % pair for pair in token_specification)
line_num = 1
line_start = 0
for mo in re.finditer(tok_regex, code):
kind = mo.lastgroup
value = mo.group()
column = mo.start() - line_start
if kind == 'NUMBER':
value = float(value) if '.' in value else int(value)
elif kind == 'ID' and value in keywords:
kind = value
elif kind == 'NEWLINE':
line_start = mo.end()
line_num += 1
continue
elif kind == 'SKIP':
continue
elif kind == 'MISMATCH':
raise RuntimeError(f'{value!r} unexpected on line {line_num}')
yield Token(kind, value, line_num, column)
statements = '''
IF quantity THEN
total := total + price * quantity;
tax := price * 0.05;
ENDIF;
for token in tokenize(statements):
print(token)
这个词法器产生以下输出
Token(type='IF', value='IF', line=2, column=4)
Token(type='ID', value='quantity', line=2, column=7)
Token(type='THEN', value='THEN', line=2, column=16)
Token(type='ID', value='total', line=3, column=8)
Token(type='ASSIGN', value=':=', line=3, column=14)
Token(type='ID', value='total', line=3, column=17)
Token(type='OP', value='+', line=3, column=23)
Token(type='ID', value='price', line=3, column=25)
Token(type='OP', value='*', line=3, column=31)
Token(type='ID', value='quantity', line=3, column=33)
Token(type='END', value=';', line=3, column=41)
Token(type='ID', value='tax', line=4, column=8)
Token(type='ASSIGN', value=':=', line=4, column=12)
Token(type='ID', value='price', line=4, column=15)
Token(type='OP', value='*', line=4, column=21)
Token(type='NUMBER', value=0.05, line=4, column=23)
Token(type='END', value=';', line=4, column=27)
Token(type='ENDIF', value='ENDIF', line=5, column=4)
Token(type='END', value=';', line=5, column=9)
Frie09
Friedl, Jeffrey. Mastering Regular Expressions. 第三版, O’Reilly Media, 2009. 第三版不再使用Python, 但第一版提供了编写正则表达式的良好细节。