概念&作用
正则表达式是一种用来描述文本模式的工具,它可以帮助你在字符串中查找
、匹配
、替换
符合某种模式的文本。通过使用正则表达式,你可以定义一些规则,然后在文本中查找符合这些规则的内容。正则表达式可以用于各种编程语言和文本处理工具中,例如Python、JavaScript、Perl等。
python中的正则表达式
在python中,内置的 re 模块提供了对正则表达式的支持,这使得在 Python 中处理文本数据变得非常方便和高效。正则表达式在 Python 中非常有用,主要是因为re模块具有强大的模式匹配功能、文本处理的灵活性、替换和修改文本的能力以及Python 的易用性和广泛应用。
基础构成
字符
正则表达式中的字符可以是字母、数字或符号,它们代表自身字符。例如,a
匹配字符 "a"。
元字符
元字符是具有特殊含义的字符,它们不仅仅代表自身字符。一些常见的元字符包括
元字符 | 作用含义 |
---|---|
. |
匹配除换行符以外的任意字符。 |
^ |
匹配字符串的开头。 |
$ |
匹配字符串的结尾。 |
\d |
匹配任意一个数字字符,相当于 [0-9]
|
\D |
匹配任意一个非数字字符,相当于 [^0-9]
|
\w |
匹配任意一个字母、数字或下划线字符,相当于 [a-zA-Z0-9_]
|
\W |
匹配任意一个非字母、数字或下划线字符,相当于 [^a-zA-Z0-9_]
|
\s |
匹配任意一个空白字符,包括空格、制表符、换行符等 |
\S |
匹配任意一个非空白字符 |
\b |
匹配单词边界,即字与空格之间的位置 |
\B |
匹配非单词边界 |
* |
匹配前一个字符的零个或多个。 |
+ |
匹配前一个字符的一个或多个。 |
? |
匹配前一个字符的零个或一个。 |
[] |
匹配方括号内的任意一个字符。例如,[aeiou] 匹配任意一个元音字母。 |
| |
匹配两个或多个模式之一。 |
() |
用于创建捕获组,可以对匹配的内容进行分组。 |
量词
量词用于指定匹配字符重复出现的次数,常见的量词包括:
量词 | 作用含义 |
---|---|
{m} |
精确匹配前一个字符出现 m 次 |
{m,n} |
匹配前一个字符出现至少 m 次,最多 n 次 |
{m,} |
匹配前一个字符出现至少 m 次 |
* |
匹配前一个字符的零个或多个 |
+ |
匹配前一个字符的一个或多个 |
? |
匹配前一个字符的零个或一个 |
原始字符串
在Python中,r字符串是一种特殊的字符串表示形式,被称为原始字符串(raw string)
。在原始字符串中,反斜杠 \ 不会被解释为转义字符,而是作为普通字符对待。这意味着在原始字符串中,反斜杠后面的字符会保持原样,不会被转义。
例如,在普通字符串中,要表示一个Windows文件路径,你需要使用双反斜杠来转义,如下所示:
path = "C:\\Users\\username\\Documents\\file.txt"
而在原始字符串中,你可以直接写成:
path = r"C:\Users\username\Documents\file.txt"
这使得处理一些特殊字符串,如正则表达式模式或文件路径,更加方便,因为你无需担心转义字符带来的影响。
re模块
Python 的正则表达式模块 re 提供了一系列功能,让你可以在字符串中进行模式匹配、查找和替换等操作。你提到的 re.match()
、re.search()
、re.findall()
、 re.sub()
和re.compile()
是其中常用的几个函数。
re.match
re.match(pattern, string, flags=0)
尝试从字符串的开头匹配一个模式。如果字符串开头匹配成功,则返回一个匹配对象;否则返回 None。这意味着它只匹配字符串的开头位置。
import re
pattern = r'hello'
string = 'hello world'
match_obj = re.match(pattern, string)
if match_obj:
print("Match found:", match_obj.group())
else:
print("No match found")
# Match found: hello
re.search
re.search(pattern, string, flags=0)
与re.match(pattern, string, flags=0)
类似,但是它在整个字符串中搜索匹配项,返回第一个匹配到的对象。这意味着它可以匹配字符串的任意位置。
import re
pattern = r'world'
string = 'hello world'
search_obj = re.search(pattern, string)
if search_obj:
print("Match found:", search_obj.group())
else:
print("No match found")
# Match found: world
re.findall
re.findall(pattern, string, flags=0)
在字符串中找到所有匹配项,并以列表的形式返回。它不同于 re.match()
和 re.search()
,它不返回匹配对象,而是直接返回匹配的字符串列表。
import re
pattern = r'lo'
string = 'hello world'
matches = re.findall(pattern, string)
print("Matches found:", matches)
# Matches found: ['lo']
re.sub
re.sub(pattern, repl, string, count=0, flags=0)
用来在字符串中查找匹配项,并替换为指定的字符串。它返回替换后的新字符串。
import re
pattern = r'world'
replacement = 'universe'
string = 'hello world'
new_string = re.sub(pattern, replacement, string)
print("New string:", new_string)
# New string: hello universe
re.compile
re.compile(pattern, flags=0)
是Python 中用于预编译正则表达式模式的函数。这个函数接受一个正则表达式作为参数,并返回一个正则表达式对象,这个对象可以被用来执行匹配操作。这个函数诸多优点:
- 提高效率: 编译后的正则表达式对象可以在多次使用时提高匹配效率。因为编译后的对象已经将正则表达式模式编译为内部数据结构,避免了每次匹配都要重新解析模式的开销。
- 可重用性: 编译后的正则表达式对象可以在多个地方使用,而不需要每次都重新编译正则表达式模式,提高了代码的可重用性。
- 简化代码: 将常用的正则表达式模式预先编译,可以简化代码,使代码更加清晰易懂。
import re
# 编译正则表达式模式
pattern = re.compile(r'\d{3}-\d{3}-\d{4}')
# 使用编译后的对象进行匹配操作
result = pattern.search("John's phone number is 123-456-7890.")
if result:
print("Phone number found:", result.group())
else:
print("Phone number not found.")
# Phone number found: 123-456-7890
flag的可选值
-
re.IGNORECASE
或re.I
: 忽略大小写匹配。 -
re.MULTILINE
或re.M
: 多行匹配,使^
和$
匹配字符串的每行的开头和结尾,而不是整个字符串的开头和结尾。 -
re.DOTALL
或re.S
: 让.
匹配任何字符,包括换行符。 -
re.UNICODE
或re.U
: 使\w
,\W
,\b
,\B
,\d
,\D
,\s
,\S
与Unicode
字符集合匹配。 -
re.ASCII
: 使\w
,\W
,\b
,\B
,\d
,\D
,\s
,\S
与ASCII
字符集合匹配。 -
re.VERBOSE
或re.X
: 忽略空白和注释,使得更易读的正则表达式能够被编写。
正/反向前瞻
正向前瞻(Positive Lookahead):正向前瞻用 (?=…)
表示,它指定了一个条件,在匹配位置的右侧必须满足这个条件才能匹配成功。但匹配的位置本身不会被包含在匹配结果中。
反向前瞻(Negative Lookahead):反向前瞻用 (?!…)
表示,它指定了一个条件,在匹配位置的右侧必须不满足这个条件才能匹配成功。同样,匹配的位置本身不会被包含在匹配结果中。
接下来演示一下,假设我们要匹配一个字符串中的所有数字,但是要排除包含小数点的数字。我们可以使用正向前瞻来实现这个匹配:
import re
# 匹配所有不包含小数点的数字
pattern = r'\d+(?!\.)'
text = "123 456 7.89 10"
matches = re.findall(pattern, text)
print(matches)
# ['123', '456', '10']
在这个示例中,r'\d+(?!\.)'
匹配的是一串数字 \d+
,但是它的右侧不能跟着一个小数点 (?!\.)
,这样就排除了包含小数点的数字。
反向前瞻也可以用来排除某些特定情况的匹配。例如,假设我们要匹配所有不是以字母 "a" 开头的单词:
import re
# 匹配所有不以字母 "a" 开头的单词
pattern = r'\b(?!a)\w+\b'
text = "apple banana orange"
matches = re.findall(pattern, text)
print(matches)
# ['banana', 'orange']
在这个示例中,r'\b(?!a)\w+\b'
匹配的是一个单词,但是它的左侧不能以字母 "a" 开头 (?!a)
,这样就排除了以 "a" 开头的单词 "apple"。
捕获组和非捕获组的概念
在正则表达式中,捕获组和非捕获组是用来对匹配的文本进行分组和提取的工具, 比如Django/Flask等框架的url路由匹配就是用了这个。
捕获组:捕获组用括号 ( )
表示,它可以将匹配的文本分组并保存到一个单独的组中,以便后续引用或提取。捕获组可以在整个正则表达式中通过反向引用(backreference)或在匹配结果中进行访问。
非捕获组:非捕获组也用括号 ( )
表示,但是在左括号后紧跟着一个问号和冒号 (?: )
,它用于对文本进行分组,但不会在匹配结果中保存分组的内容,也不会创建一个新的捕获组。非捕获组通常用于提高正则表达式的效率,因为它不会占用额外的内存来存储匹配结果。
接下来演示一下, 假设我们有一个包含邮箱地址的字符串,我们想从中提取用户名和域名部分。我们可以使用捕获组来实现:
import re
# 匹配邮箱地址的正则表达式
pattern = r'(\w+)@(\w+\.\w+)'
text = "jeff@xmishu.com, alice@baidu.com"
# 使用捕获组提取用户名和域名
matches = re.findall(pattern, text)
for match in matches:
username, domain = match
print("Username:", username)
print("Domain:", domain)
在这个示例中,(\w+)
和 (\w+\.\w+)
分别是两个捕获组,用来提取用户名和域名。re.findall()
函数会返回一个列表,其中每个元素是一个包含用户名和域名的元组。通过遍历这个列表,我们可以轻松地获取用户名和域名部分。
实际应用
正则表达式在python应用中非常广泛,它能辅助我们高效地处理很多问题
- 数据提取(网页数据采集等)
- 文本处理(数据清洗、格式化入库等)
- web表单中用户输入内容验证(邮箱密码格式验证等)。
注意事项
-
预编译正则表达式:如果你要在多个地方使用相同的正则表达式,可以使用
re.compile()
预先编译它,以提高匹配效率,生产环境中很有用。 -
选择最适合的函数:Python的re模块提供了多个函数用于正则表达式操作,如
re.match()
、re.search()
、re.findall()
等。根据需求选择最适合的函数,以避免不必要的性能开销。 -
使用非贪婪匹配:在量词后面加上?可以将匹配模式变为非贪婪模式,尽可能少地匹配字符。例如,
*?
、+?
、??
。 - 避免过度使用 .:.可以匹配任意一个字符,但在某些情况下可能会导致性能下降。如果可能,尽量使用更具体的字符集或模式来限制匹配范围。
-
使用原始字符串:在编写正则表达式时,最好使用
原始字符串
(以r开头),以避免不必要的转义,提高代码可读性。 -
使用字符类:字符类(如
[0-9]
、[a-zA-Z]
)比单个字符更有效率,因为它们允许正则引擎更快地确定是否匹配。