正则表达式,这个术语不太容易望文生义(没有去考证是如何被翻译为正则表达式的),其实其英文为Regular Expression,直接翻译就是:有规律的表达式。这个表达式其实就是一个字符序列,反映某种字符规律,用(字符串模式匹配)来处理字符串。很多高级语言均支持利用正则表达式对字符串进行处理的操作。
python提供的正则表达式文档可参见:https://docs.python.org/3/library/re.html
In [1]:
import re
1. 初识
In [4]:
s = 'Blow low, follow in of which low. lower, lmoww oow aow bow cow 23742937 dow kdiieur998.'
p = 'low'
low
,由于该单词的规律就是low
,因此可将low
作为一个正则表达式,命名为p
。
In [3]:
m = re.findall(p, s)
m
Out[3]:
findall(pattern, string)
是re模块中的函数,会在字符串string
中将所有匹配正则表达式pattern
模式的字符串提取出来,并以一个list
的形式返回。该方法是从左到右进行扫描,所返回的list
中的每个匹配按照从左到右匹配的顺序进行存放。low
能够将所有单词low
匹配出来,但是也会将lower
,Blow
等含有low
字符串中的low
也匹配出来。
In [ ]:
p = input('请输入字符模式,回车结束!\n')
m = re.findall(p,s)
if not m:
print('没有找到匹配字符!')
else:
print('成功匹配!')
如果不存在可以匹配的字符模式,则返回空列表。可以利用列表是否为空作为分支条件。
In [4]:
p = r'\blow\b'
m = re.findall(p, s)
m
Out[4]:
\b
,即boundary
,是正则表达式中的一种特殊字符,表示单词的边界。正则表达式r'\blow\b'
就是要单独匹配low
,该字符串两侧为单词的边界(边界为空格等,但是并不对边界进行匹配)
In [5]:
p = r'[lmo]ow'
m = re.findall(p, s)
m
Out[5]:
[lmo]
,匹配lmo
字母中的任何一个
In [6]:
p = r'[a-d]ow'
m = re.findall(p, s)
m
Out[6]:
[a-d]
,匹配abcd
字母中的任何一个
In [7]:
p = r'\d'
m = re.findall(p, s)
m
Out[7]:
\d
,即digit,表示数字,\d表示数字(一个数字字符)
In [8]:
p = r'\d+'
m = re.findall(p, s)
m
Out[8]:
+
,表示一个或者重复多个对象,对象为+
前面指定的模式\d+
可以匹配长度至少为1的任意正整数字符。2. 基本匹配与实例
字符模式 | 匹配模式内容 | 等价于 |
---|---|---|
[a-d] | One character of: a, b, c, d | [abcd] |
[^a-d] | One character except: a, b, c, d | [^abcd] |
abc丨def | abc or def | |
\d | One digit | [0-9] |
\D | One non-digit | [^0-9] |
\s | One whitespace | [ \t\n\r\f\v] |
\S | One non-whitespace | [^ \t\n\r\f\v] |
\w | One word character | [a-zA-Z0-9_] |
\W | One non-word character | [^a-zA-Z0-9_] |
. | Any character (except newline) | [^\n] |
固定点标记 | 匹配模式内容 |
---|---|
^ | Start of the string |
$ | End of the string |
\b | Boundary between word and non-word characters |
数量词 | 匹配模式内容 |
---|---|
{5} | Match expression exactly 5 times |
{2,5} | Match expression 2 to 5 times |
{2,} | Match expression 2 or more times |
{,5} | Match expression 0 to 5 times |
* | Match expression 0 or more times |
{,} | Match expression 0 or more times |
? | Match expression 0 or 1 times |
{0,1} | Match expression 0 or 1 times |
+ | Match expression 1 or more times |
{1,} | Match expression 1 or more times |
字符转义 | 转义匹配内容 |
---|---|
\. | . character |
\|\ character | |
\* | * character |
\+ | + character |
\? | ? character |
\{ | { character |
\) | ) character |
\[ | [ character |
In [9]:
m = re.findall(r'\d{3,4}-?\d{8}', '010-66677788,02166697788, 0451-22882828')
m
Out[9]:
-
或者没有。
In [10]:
m = re.findall(r'[\u4e00-\u9fa5]', '测试 汉 字,abc,测试xia,可以')
m
Out[10]:
正则表达式 | 匹配内容 |
---|---|
[A-Za-z0-9] | 匹配英文和数字 |
[\u4E00-\u9FA5A-Za-z0-9_] | 中文英文和数字及下划线 |
^[a-zA-Z][a-zA-Z0-9_]{4,15}$` | 合法账号,长度在5-16个字符之间,只能用字母数字下划线,且第一个位置必须为字母 |
3. 进阶
3.1 python正则表达式常用函数
函数 | 功能 | 用法 |
---|---|---|
re.search | Return a match object if pattern found in string | re.search(r'[pat]tern', 'string') |
re.finditer | Return an iterable of match objects (one for each match) | re.finditer(r'[pat]tern', 'string') |
re.findall | Return a list of all matched strings (different when capture groups) | re.findall(r'[pat]tern', 'string') |
re.split | Split string by regex delimeter & return string list | re.split(r'[ -]', 'st-ri ng') |
re.compile | Compile a regular expression pattern for later use | re.compile(r'[pat]tern') |
re.sub | Replaces all occurrences of the RE pattern in string with repl, substituting all occurrences unless max provided. This method returns modified string | re.sub(r'[pat]tern', repl, 'string') |
In [43]:
m = re.search(r'\d{3,4}-?\d{8}', '010-66677788,02166697788, 0451-22882828')
m
Out[43]:
In [12]:
m.group()
Out[12]:
In [15]:
m.span()
Out[15]:
span()函数返回匹配字符串的起始和结束位置
In [76]:
ms = re.finditer(r'\d{3,4}-?\d{8}', '010-66677788,02166697788, 0451-22882828')
for m in ms:
print(m.group())
In [14]:
words = re.split(r'[,-]', '010-66677788,02166697788,0451-22882828')
words
Out[14]:
正则下的split(),是一般split()函数的增强版本,可以对字符串以正则表达式匹配的字符进行切割,返回切割后的列表。
In [9]:
p = re.compile(r'[,-]')
p.split('010-66677788,02166697788,0451-22882828')
Out[9]:
3.2 分组与引用
Group Type | Expression |
---|---|
Capturing | ( ... ) |
Non-capturing | (?: ... ) |
Capturing group named Y | (?P<Y> ... ) |
Match the Y'th captured group | \Y |
Match the named group Y | (?P=Y) |
In [65]:
p = re.compile('(ab)+')
p.search('ababababab').group()
Out[65]:
In [17]:
p.search('ababababab').groups()
Out[17]:
In [18]:
p=re.compile('(\d)-(\d)-(\d)')
p.search('1-2-3').group()
Out[18]:
In [19]:
p.search('1-2-3').groups()
Out[19]:
In [20]:
s = '喜欢/v 你/x 的/u 眼睛/n 和/u 深情/n 。/w'
p = re.compile(r'(\S+)/n')
m = p.findall(s)
m
Out[20]:
In [21]:
p=re.compile('(?P<first>\d)-(\d)-(\d)')
p.search('1-2-3').group()
Out[21]:
?P<name>
的形式,给该分组命名,其中name是给该分组的命名
In [22]:
p.search('1-2-3').group('first')
Out[22]:
group('name')
,直接通过组名来获取匹配的该分组
In [23]:
s = 'age:13,name:Tom;age:18,name:John'
p = re.compile(r'age:(\d+),name:(\w+)')
m = p.findall(s)
m
Out[23]:
In [24]:
p = re.compile(r'age:(?:\d+),name:(\w+)')
m = p.findall(s)
m
Out[24]:
(?:\d+)
,匹配该模式,但不捕获该分组。因此没有捕获该分组的数字
In [25]:
s = 'abcdebbcde'
p = re.compile(r'([ab])\1')
m = p.search(s)
print('The match is {},the capture group is {}'.format(m.group(), m.groups()))
([ab])
内的a
或b
匹配成功后,将开始匹配\1
,\1
将匹配前面分组成功的字符。因此该正则表达式将匹配aa
或bb
。
In [26]:
s = '12,56,89,123,56,98, 12'
p = re.compile(r'\b(\d+)\b.*\b\1\b')
m = p.search(s)
m.group(1)
Out[26]:
\1
是引用前一个分组的匹配。
In [27]:
s = '12,56,89,123,56,98, 12'
p = re.compile(r'\b(?P<name>\d+)\b.*\b(?P=name)\b')
m = p.search(s)
m.group(1)
Out[27]:
3.3 贪婪与懒惰
数量词 | 匹配模式内容 |
---|---|
{2,5}? | Match 2 to 5 times (less preferred) |
{2,}? | Match 2 or more times (less preferred) |
{,5}? | Match 0 to 5 times (less preferred) |
*? | Match 0 or more times (less preferred) |
{,}? | Match 0 or more times (less preferred) |
?? | Match 0 or 1 times (less preferred) |
{0,1}? | Match 0 or 1 times (less preferred) |
+? | Match 1 or more times (less preferred) |
{1,}? | Match 1 or more times (less preferred) |
?
。
In [2]:
p = re.compile('<.*>')
p.search('<python>perl>').group()
Out[2]:
In [5]:
p = re.compile('<.*?>')
p.search('<python>perl>').group()
Out[5]:
In [28]:
p = re.compile('(ab)+')
p.search('ababababab').group()
Out[28]:
In [29]:
p = re.compile('(ab)+?')
p.search('ababababab').group()
Out[29]:
4. 文本处理中的一些应用实例
4.1 提取文本中的符合某种模式的字串并进行处理后替换
In [64]:
with open(r'test_re.txt', encoding = 'utf-8') as f:
text = f.read()
读入文本文件
In [54]:
p = '[\S]+/t[^/]+/t'
lines = re.findall(p, text)
lines[:10]
Out[54]:
/
的字符 /t
In [42]:
import re
p = '((?:[\S]+/t\s){2,})'
matchs = re.findall(p, text)
matchs[:10]
Out[42]:
?:
表示不捕获该分组内容
In [69]:
def express(line):
return line.group().replace('/t ', '')+'/t '
txt = re.sub(p, express, text)
txt.split('\n')[:5]
Out[69]:
4.2 网页文件(HTML)处理示例
进一步学习可参考官方文档以及《精通正则表达式(第3版)》