Python3 正则表达式
match 与 search 的区别、检索替换、Pattern 对象、修饰符与常用模式。
re.match 函数
re.match(pattern, string) tries to match at the start of the string, returning a Match object on success and None on failure. It does not need to reach the end — a match at position 0 is enough.
import re
print(re.match('www', 'www.runoob.com').span())
print(re.match('com', 'www.runoob.com'))
print(re.match(r'(\d{4})-(\d{2})', '2024-10-31').groups())
print(re.match(r'(\d{4})-(\d{2})', '2024-10-31').group(1))(0, 3)
None
('2024', '10')
2024re.search 方法
re.search(pattern, string) scans the whole string and returns the first match wherever it occurs. Position is irrelevant — that is the essential difference from match.
import re
print(re.search('www', 'www.runoob.com').span())
print(re.search('com', 'www.runoob.com').span())
print(re.search(r'\d+', 'abc123def456').group())
print(re.search(r'(?P<k>\w+)=(?P<v>\d+)', 'a=1 b=22').groupdict())
print(re.findall(r'(?P<k>\w+)=(?P<v>\d+)', 'a=1 b=22'))(0, 3)
(11, 14)
123
{'k': 'a', 'v': '1'}
[('a', '1'), ('b', '22')]re.match 与 re.search 的区别
In one line: match only looks at the start, search scans anywhere. Use re.fullmatch() when the whole string must match; search is the right tool for "does this text contain X".
import re
line = "Cats are smarter than dogs"
m = re.match(r'(.*) are (.*?) .*', line, re.M | re.I)
if m:
print("group():", m.group())
print("group(1):", m.group(1))
print("group(2):", m.group(2))
print(re.match('dogs', line))
print(re.search('dogs', line).span())
print(re.fullmatch(r'\d+', '123'), re.fullmatch(r'\d+', '123a'))group(): Cats are smarter than dogs
group(1): Cats
group(2): smarter
None
(22, 26)
<re.Match object; span=(0, 3), match='123'> None检索和替换
re.sub(pattern, repl, string, count=0) replaces matches; repl may be a string, a function, or a group reference like \1. re.subn() also returns the count, and re.split() cuts by pattern.
import re
print(re.sub(r'\d+', '*', 'abc123def456'))
print(re.sub(r'\d+', '*', 'abc123def456', count=1))
def double(m):
return str(int(m.group()) * 2)
print(re.sub(r'\d+', double, 'abc123def456'))
print(re.subn(r'\d+', '#', 'a1b22c333'))
print(re.sub(r'(\w+) (\w+)', r'\2 \1', 'hello world'))
print(re.split(r'\s+', 'a b\tc'))abc*def*
abc*def456
abc246def912
('a#b#c#', 3)
world hello
['a', 'b', 'c']正则表达式对象
re.compile(pattern, flags) pre-compiles a pattern into a reusable Pattern object. When the same pattern is used repeatedly, compiling once is faster and clearer; the object exposes match / search / findall directly.
import re
p = re.compile(r'(\w+)@(\w+)\.com')
m = p.match('tom@example.com')
print(m.group(0), m.group(1), m.group(2))
print(m.groups())
print(m.start(), m.end(), m.span())
print(p.findall('a@x.com b@y.com'))
print(p.pattern)
print(p.flags)tom@example.com tom example
('tom', 'example')
0 15 (0, 15)
[('a', 'x'), ('b', 'y')]
(\w+)@(\w+)\.com
32正则表达式修饰符 - 可选标志
Flags alter matching: re.I ignores case, re.M makes ^/$ per-line, re.S lets . match newlines, re.X allows comments and whitespace in the pattern. Combine with |.
| Flag | Meaning |
|---|---|
| re.I | Ignore case |
| re.M | ^ and $ match at line boundaries |
| re.S | . matches newline too |
| re.X | Verbose mode: whitespace ignored, # comments |
| re.A | ASCII-only matching for \w, \b |
import re
print(bool(re.match('python', 'Python')))
print(bool(re.match('python', 'Python', re.I)))
text = "line1\nline2"
print(bool(re.search(r'^line2', text)))
print(bool(re.search(r'^line2', text, re.M)))
print(bool(re.match(r'a.b', 'a\nb')))
print(bool(re.match(r'a.b', 'a\nb', re.S)))
pattern = '''
\d+ # integer part
\. # decimal point
\d+ # fraction part
'''
print(bool(re.match(pattern, '3.14', re.X)))False
True
False
True
False
True
True正则表达式模式
The pattern grammar is the common base of every regex dialect: . any char, * + ? quantifiers, {m,n} counts, [] sets, () groups, | alternation, ^ $ anchors, \d \w \s classes, \b word boundary.
| Pattern | Matches |
|---|---|
| ^ | Start of string (or line with re.M) |
| $ | End of string (or line with re.M) |
| . | Any character except newline |
| [abc] | One of a, b, c |
| [^abc] | Any character but a, b, c |
| re* | Zero or more repetitions |
| re+ | One or more repetitions |
| re? | Zero or one repetition |
| re{2,4} | Two to four repetitions |
| \d / \D | Digit / non-digit |
| \w / \W | Word char / non-word char |
| \b | Word boundary |
import re
print(re.findall(r'^a', 'abc\nabc', re.M))
print(re.findall(r'a.c', 'abc adc a-c'))
print(re.findall(r'a[bc]c', 'abc acc axc'))
print(re.findall(r'ab*', 'a ab abb abbb'))
print(re.findall(r'\bcat\b', 'cat cats concat'))
print(re.findall(r'\d{2,3}', '1 22 333 4444'))
print(re.findall(r'(ab)+', 'ababab ab'))['a', 'a']
['abc', 'adc', 'a-c']
['abc', 'acc']
['a', 'ab', 'abb', 'abbb']
['cat']
['22', '333', '444']
['ab', 'ab']正则表达式实例
These are the patterns you meet most in practice: phone validation, stripping HTML tags, splitting on whitespace, and named-group extraction. Rule of thumb: regex is for extraction and validation, not for parsing nested structures.
import re
text = "联系 13800138000 或 13912345678"
print(re.findall(r'1[3-9]\d{9}', text))
html = '<b>bold</b>'
print(re.sub(r'<[^>]+>', '', html))
m = re.match(r'(?P<year>\d{4})-(?P<month>\d{2})', '2024-10')
print(m.group('year'), m.group('month'), m.groupdict())
log = '2024-10-31 12:00:00 ERROR disk full'
mm = re.match(r'(\S+) (\S+) (\w+) (.+)', log)
print(mm.group(3), '|', mm.group(4))['13800138000', '13912345678']
bold
2024 10 {'year': '2024', 'month': '10'}
ERROR | disk fullhtml.parser, xml.etree or json instead.re.match 函数
re.match(pattern, string) 从字符串开头尝试匹配,成功返回 Match 对象,失败返回 None。它不要求匹配到结尾 —— 只要开头对上就算成功。
import re
print(re.match('www', 'www.runoob.com').span())
print(re.match('com', 'www.runoob.com'))
print(re.match(r'(\d{4})-(\d{2})', '2024-10-31').groups())
print(re.match(r'(\d{4})-(\d{2})', '2024-10-31').group(1))(0, 3)
None
('2024', '10')
2024re.search 方法
re.search(pattern, string) 扫描整个字符串,返回第一个匹配。只要出现即可,不限定位置 —— 这是它与 match 最本质的差别。
import re
print(re.search('www', 'www.runoob.com').span())
print(re.search('com', 'www.runoob.com').span())
print(re.search(r'\d+', 'abc123def456').group())
print(re.search(r'(?P<k>\w+)=(?P<v>\d+)', 'a=1 b=22').groupdict())
print(re.findall(r'(?P<k>\w+)=(?P<v>\d+)', 'a=1 b=22'))(0, 3)
(11, 14)
123
{'k': 'a', 'v': '1'}
[('a', '1'), ('b', '22')]re.match 与 re.search 的区别
一句话:match 只认开头,search 全篇查找。要「从头到尾完全匹配」用 re.fullmatch();只是想确认某段文本存在,用 search 更合适。
import re
line = "Cats are smarter than dogs"
m = re.match(r'(.*) are (.*?) .*', line, re.M | re.I)
if m:
print("group():", m.group())
print("group(1):", m.group(1))
print("group(2):", m.group(2))
print(re.match('dogs', line))
print(re.search('dogs', line).span())
print(re.fullmatch(r'\d+', '123'), re.fullmatch(r'\d+', '123a'))group(): Cats are smarter than dogs
group(1): Cats
group(2): smarter
None
(22, 26)
<re.Match object; span=(0, 3), match='123'> None检索和替换
re.sub(pattern, repl, string, count=0) 替换匹配内容,repl 可以是字符串、函数或分组引用 \1;re.subn() 额外返回替换次数。re.split() 按模式切分。
import re
print(re.sub(r'\d+', '*', 'abc123def456'))
print(re.sub(r'\d+', '*', 'abc123def456', count=1))
def double(m):
return str(int(m.group()) * 2)
print(re.sub(r'\d+', double, 'abc123def456'))
print(re.subn(r'\d+', '#', 'a1b22c333'))
print(re.sub(r'(\w+) (\w+)', r'\2 \1', 'hello world'))
print(re.split(r'\s+', 'a b\tc'))abc*def*
abc*def456
abc246def912
('a#b#c#', 3)
world hello
['a', 'b', 'c']正则表达式对象
re.compile(pattern, flags) 预编译模式,返回可复用的 Pattern 对象。同一模式反复使用时编译一次更省,也让代码更清晰;Pattern 上可直接调 match / search / findall。
import re
p = re.compile(r'(\w+)@(\w+)\.com')
m = p.match('tom@example.com')
print(m.group(0), m.group(1), m.group(2))
print(m.groups())
print(m.start(), m.end(), m.span())
print(p.findall('a@x.com b@y.com'))
print(p.pattern)
print(p.flags)tom@example.com tom example
('tom', 'example')
0 15 (0, 15)
[('a', 'x'), ('b', 'y')]
(\w+)@(\w+)\.com
32正则表达式修饰符 - 可选标志
修饰符改变匹配行为:re.I 忽略大小写、re.M 让 ^/$ 匹配每行、re.S 让 . 匹配换行、re.X 允许模式里写注释和空白。多个标志用 | 组合。
| 修饰符 | 含义 |
|---|---|
| re.I | 忽略大小写 |
| re.M | ^ 和 $ 匹配每行首尾 |
| re.S | . 也匹配换行符 |
| re.X | 详细模式:忽略空白,支持 # 注释 |
| re.A | \w、\b 只按 ASCII 匹配 |
import re
print(bool(re.match('python', 'Python')))
print(bool(re.match('python', 'Python', re.I)))
text = "line1\nline2"
print(bool(re.search(r'^line2', text)))
print(bool(re.search(r'^line2', text, re.M)))
print(bool(re.match(r'a.b', 'a\nb')))
print(bool(re.match(r'a.b', 'a\nb', re.S)))
pattern = '''
\d+ # integer part
\. # decimal point
\d+ # fraction part
'''
print(bool(re.match(pattern, '3.14', re.X)))False
True
False
True
False
True
True正则表达式模式
模式语法是所有正则方言的公共基础:. 任意字符、* + ? 量词、{m,n} 次数、[] 字符集、() 分组、| 或、^ $ 锚点、\d \w \s 预定义类、\b 词边界。
| 模式 | 匹配 |
|---|---|
| ^ | 字符串开头(配合 re.M 为每行开头) |
| $ | 字符串结尾(配合 re.M 为每行结尾) |
| . | 除换行外任意字符 |
| [abc] | a、b、c 之一 |
| [^abc] | 除 a、b、c 外任意字符 |
| re* | 零次或多次 |
| re+ | 一次或多次 |
| re? | 零次或一次 |
| re{2,4} | 两到四次 |
| \d / \D | 数字 / 非数字 |
| \w / \W | 单词字符 / 非单词字符 |
| \b | 单词边界 |
import re
print(re.findall(r'^a', 'abc\nabc', re.M))
print(re.findall(r'a.c', 'abc adc a-c'))
print(re.findall(r'a[bc]c', 'abc acc axc'))
print(re.findall(r'ab*', 'a ab abb abbb'))
print(re.findall(r'\bcat\b', 'cat cats concat'))
print(re.findall(r'\d{2,3}', '1 22 333 4444'))
print(re.findall(r'(ab)+', 'ababab ab'))['a', 'a']
['abc', 'adc', 'a-c']
['abc', 'acc']
['a', 'ab', 'abb', 'abbb']
['cat']
['22', '333', '444']
['ab', 'ab']正则表达式实例
下面这些是工程里出现频率最高的几个场景:手机号校验、去 HTML 标签、按空白切分、命名分组提取字段。记住一句:正则擅长「提取和校验」,不适合解析嵌套结构。
import re
text = "联系 13800138000 或 13912345678"
print(re.findall(r'1[3-9]\d{9}', text))
html = '<b>bold</b>'
print(re.sub(r'<[^>]+>', '', html))
m = re.match(r'(?P<year>\d{4})-(?P<month>\d{2})', '2024-10')
print(m.group('year'), m.group('month'), m.groupdict())
log = '2024-10-31 12:00:00 ERROR disk full'
mm = re.match(r'(\S+) (\S+) (\w+) (.+)', log)
print(mm.group(3), '|', mm.group(4))['13800138000', '13912345678']
bold
2024 10 {'year': '2024', 'month': '10'}
ERROR | disk fullhtml.parser、xml.etree 或 json 模块。