正则表达式(Regular Expression,常简写为 regex 或 regexp)是一种用于描述文本模式的工具。它们可以用来查找、编辑或操作字符串以及文本数据。正则表达式广泛应用于各种编程语言和文本处理工具中,如搜索工具、文本编辑器、命令行工具等。
一、正则表达式的几个关键概念
1.字符类:
.
:匹配除换行符外的任何单个字符。[abc]
:方括号内的任意一个字符(在这个例子中是 a, b 或 c)。[^abc]
:不在方括号内的任意一个字符(在这个例子中是除了 a, b 和 c 之外的字符)。[a-z]
:指定范围内的任意一个字符(在这个例子中是从 a 到 z 的任意一个小写字母)。
2.预定义字符类:
\d
:匹配一个数字字符,相当于 [0-9]。\D
:匹配一个非数字字符,相当于 [^0-9]。\s
:匹配一个空白字符,包括空格、制表符、换页符等等。\S
:匹配一个非空白字符。\w
:匹配字母、数字及下划线,相当于 [A-Za-z0-9_]。\W
:匹配非字母、非数字、非下划线字符。
3.重复量词:
*
:匹配前面的子表达式零次或多次。+
:匹配前面的子表达式一次或多次。?
:匹配前面的子表达式零次或一次。{n}
:匹配确定的 n 次。{n,}
:至少匹配 n 次。{n,m}
:最少匹配 n 次且最多匹配 m 次。
4.分组与捕获:
()
:圆括号内的内容被视为一个整体,可作为后续量词的作用对象,也可用于捕获子表达式进行替换或引用。(?:...)
:非捕获分组,只做分组不捕获。\1
,\2
, …:引用之前捕获的组。
5.锚点:
^
:匹配字符串的开始位置。$
:匹配字符串的结束位置。\b
:匹配单词边界。\B
:匹配非单词边界。
6.选择:
|
:逻辑或,用于表示两个或多个模式之间的选择。
7.转义字符:
\
:用于转义特殊字符,使得其失去特殊意义而成为普通字符。
8.零宽断言:
正向肯定的零宽断言(positive lookahead 和 positive lookbehind)是正则表达式中的一个特性,它们允许你在匹配模式时指定某些条件必须满足,但这些条件本身不会成为最终匹配结果的一部分。换句话说,它们用来检查某个位置前后的内容是否符合特定模式,而不会“消耗”字符——即,这些字符不会被包含在匹配结果中。
二、针对以上概念的详解
锚点(Anchors)
锚点用于匹配字符串的特定位置,而不是字符本身。例如:
^
锚点表示匹配字符串的开始。$
锚点表示匹配字符串的结束。
假设我们有一个文本文件,其中包含以下内容:
apple
banana
orange
apple pie
如果我们想要找到所有以 “apple” 开头的行,我们可以使用如下的正则表达式:
^apple
这将匹配第一行和第四行中的 “apple”,因为它出现在行的开头。
如果我们要找以 “pie” 结尾的行,我们可以使用:
pie$
这将只匹配第四行中的 “pie”,因为它是在行的末尾。
单词边界
\b
表示单词边界(word boundary),即一个单词字符和非单词字符之间的位置。\B
表示非单词边界(non-word boundary),意味着它匹配的是两个都是单词字符或两个都不是单词字符之间的地方。
- 使用
\bat\b
这个正则表达式用于匹配 “at” 作为一个独立的单词,而不是作为其他单词的一部分。具体来说:
- 会匹配:当 “at” 是一个单独的词时,例如在
"Look at that."
中的 “at”。 - 不会匹配:当 “at” 是另一个单词的一部分时,例如在
"catalog"
或"hat"
中的 “at”。
- 使用
\Bat\B
这个正则表达式用于匹配 “at” 作为另一个单词的一部分,而不是在单词的开头或结尾。具体来说:
- 会匹配:当 “at” 出现在其他单词内部时,例如在
"catalog"
中的 “at”。 - 不会匹配:当 “at” 是一个独立的单词时,例如在
"Look at that."
中的 “at”。
捕获组(Capturing Groups)
捕获组用来提取匹配的一部分,通常用圆括号 ()
来定义。当一个模式被匹配时,捕获组内的内容会被单独保存下来,以便后续处理。
考虑以下URL列表:
https://www.example.com/path?query=123
http://example.org/path
如果我们想要从这些URL中提取域名部分,我们可以使用这样的正则表达式:
https?:\/\/(www\.)?([a-zA-Z0-9.-]+)
这里有两个捕获组:(www\.)?
和 ([a-zA-Z0-9.-]+)
。第二个捕获组会捕获域名部分,即 example.com
或 example.org
。
总之:捕获组(Capturing Groups)在正则表达式中用于获取符合匹配内容中的特定部分。
结合python的实例:
Python中你可以这样做:
import re
text = "Contact us at support@example.com or sales@example.org."
pattern = r"(\w+)@(\w+\.\w+)"
matches = re.finditer(pattern, text)
for match in matches:
print("Username:", match.group(1))
print("Domain:", match.group(2))
这段代码会输出:
Username: support
Domain: example.com
Username: sales
Domain: example.org
这里 match.group(1)
和 match.group(2)
分别引用了第一个和第二个捕获组所匹配的内容。
非捕获组
有时候你可能想要分组但不希望捕获这些内容以供后续使用。这时可以使用非捕获组 (?:...)
。例如:
(?:\w+)@(\w+\.\w+)
这里的 \w+
被放在 (?:...)
内,表示它只是用于分组而不作为捕获组,因此不会保存这部分匹配的结果。
正向肯定零宽断言 (positive lookahead)
- 正向肯定零宽断言 (positive lookahead) 的语法是
(?=...)
,它确保当前匹配的位置之后紧跟着指定的模式,但不包括该模式作为匹配结果的一部分。 - 正向肯定零宽后瞻断言 (positive lookbehind) 的语法是
(?<=...)
,它确保当前匹配的位置之前紧跟着指定的模式,同样也不包括该模式作为匹配结果的一部分。
总之,(?<=...)匹配内容(?=...)
同时使用可以获取符合条件的匹配内容而不包括搜索条件
正向肯定零宽断言
假设你有一个字符串 "The price is 100 dollars"
,你想匹配数字,但只在它们后面跟着 “dollars” 的时候。你可以使用正向肯定零宽断言来实现这个需求:
\d+(?= dollars)
这将匹配 100
,因为它是跟在 “dollars” 前面的,但 “dollars” 不会被包含在匹配结果中。
正向肯定零宽后瞻断言
如果你想匹配以特定字符或模式结尾的单词,但不包括那个特定字符或模式,可以使用正向肯定零宽后瞻断言。
例如,从 <p>Hello World!</p>
提取 “Hello World!”:
(?<=<[^>]+>)(.*?)(?=</[^>]+>)
这会匹配 "Hello World!"
,因为它们包括在
标签中,且标签不会成为匹配的一部分。