正则表达式入门 — 一个通过例子来说明的备忘单

  • 2019 年 11 月 26 日
  • 笔记

正则表达式(regex 或 regexp)在通过搜索特定搜索模式的一个或多个匹配(即 ASCII 或 unicode 字符的特定序列)从任何文本中提取信息时非常有用。

应用领域从验证到解析/替换字符串,将数据转换为其他格式以及网络爬虫。

最有趣的功能之一是,一旦你学会了语法,你就可以在(几乎)所有编程语言中使用这个工具(JavaScript,Java,VB,C#,C / C ++,Python,Perl,Ruby,Delphi,R,Tcl等等),对引擎支持的最高级功能和语法版本的支持有一点区别)。

让我们首先看一些例子和解释。

基本知识点

锚 — ^ 以及 $

The      匹配任意字符串以 The 为开头-> **[试一下!](https://regex101.com/r/cO8lqs/2)**    end$       匹配任意字符串以 end 为结尾    ^The end$    匹配字符串的(开头和结尾分别是 The end)    roar       匹配任意具有 roar 的字符串

量词 — * + ? 以及 {}

abc*       匹配一个字符串具有 ab 其后有0个或者多个 c->[试一下!](https://regex101.com/r/cO8lqs/1)    abc+       匹配一个字符串具有 ab 其后有1个或者多个 c    abc?       匹配一个字符串具有 ab 其后有0个或者1个 c    abc{2}     匹配一个字符串具有 ab 其后有2个 c    abc{2,}    匹配一个字符串具有 ab 其后有2个或者多个 c    abc{2,5}   匹配一个字符串具有 ab 其后有2到5个 c    a(bc)*       匹配一个字符串具有 a 其后有0到多个 bc 的副本    a(bc){2,5}   匹配一个字符串具有 a 其后有0到5个 bc 的副本

OR 操作符 — | 或 []

a(b|c)     匹配一个字符串具有 a 其后有 b 或者 c -> [试一下!](https://regex101.com/r/cO8lqs/3)    a[bc]      与上一条相同

字符类 — d w s 以及 .

d         匹配一个数字字符-> [试一下!](https://regex101.com/r/cO8lqs/4)    w         匹配一个单词字符(字母以及下划线) -> [试一下!](https://regex101.com/r/cO8lqs/4)    s         匹配空白字符(包括 tab 以及换行)    .          匹配任意字符->[试一下!](https://regex101.com/r/cO8lqs/5)

仔细使用 . 操作符,因为类或者否定类字符(我们在下面会提到)将会更快更准确。

d, w 以及 s 分别对应其否定类 D, W 以及 S

例如, D 将执行与 d 获得的相反的匹配。

D         匹配一个非数字字符->[试一下!](https://regex101.com/r/cO8lqs/6)

为了获取字面上疑似的字符,你必须使用反斜杠 来转义字符 ^.[$()|*+?{,因为它们具有特殊含义。

$d*       匹配一个字符具有一个数字字符其前面是一个 $>[试一下!](https://regex101.com/r/cO8lqs/9)

注意你也可以匹配非打印字符比如 tabs t,换行 n,回车 r

标志

我们正在学习如何构建一个正则表达式但是却忘记了一个基础的概念:标志

一个正则表达式的格式通常是这个样子的 /abc/,搜索模式通过两个斜杠符 / 进行区分。在末尾我们可以规定一个标志使用以下的值(我们也可以将它们相互结合):

  • g(全局的) 在第一匹配之后不会立即返回,从前面匹配之后继续搜索
  • m (多行的) 当使用 ^ 以及 $ 的时候将会匹配行首和行尾而不是整个字符串
  • i (大小写不敏感的) 让整个表达式大小写不敏感(比如 /aBc/i 将匹配 Abc

中级知识点

分组以及捕获 — ()

a(bc)           括号产生一个值为 bc 的捕获分组-> [试一下!](https://regex101.com/r/cO8lqs/11)    a(?:bc)*        我么可以使用 ?:  让捕获分组不起作用->[试一下!](https://regex101.com/r/cO8lqs/12)    a(?<foo>bc)     我们可以使用 ?<foo> 将名字放在分组中 -> [试一下!](https://regex101.com/r/cO8lqs/17)

当我们需要使用你首选的编程语言从字符串或数据中提取信息时,此运算符非常有用。 由几个组捕获的任何多次出现都将以经典数组的形式公开:我们将使用匹配结果的索引来访问它们的值。

如果我们选择为组添加名称(使用( ?<foo> …)),我们将能够使用匹配结果检索组值,如字典,其中字典的名称就是刚才添加的名称。

方括号表达式 — []

[abc]            匹配一个具有 要么一个 a 或者一个 b 或者一个 c 的字符串-> 等同于 `a|b|c`-> [试一下!](https://regex101.com/r/cO8lqs/7)    [a-c]            与前一条相同    [a-fA-F0-9]      字符串代表一个十六进制数,大小写不敏感-> [试一下!](https://regex101.com/r/cO8lqs/22)    [0-9]%           一个具有从0到9其后后一个 % 符号    [^a-zA-Z]         一个不是大小写字母的字符串。在这种情况下,^ 被用为 表达式的否定。->[试一下!](https://regex101.com/r/cO8lqs/10)

请记住,在括号内表达式中,所有特殊字符(包括反斜杠)都会失去它们的特殊功能:因此我们不会应用“转义规则”。

贪婪和惰性匹配

量词 ( *+{}) 是贪婪操作符, 所以他们尽可能地通过提供的文本扩展匹配.

比如, <.+> 会从 Thisisa**<div>simple div</div>**test 中匹配 <div>simple div</div>。 为了仅仅匹配 div 标签, 我们可以使用一个 ? 让它变为惰性:

<.+?>            匹配 任意字符被包含在 < 以及 >之中,出现一到多次, 需要的时候才会扩展-> [试一下!](https://regex101.com/r/cO8lqs/24)

注意更好的解决方案是避免使用 .来构建一个更严格的正则表达式: <[^<>]+> 匹配任意的字符除了 < 或者 > 一次或者多次被包含在 < 以及 > 之中-> 试一下!

高级知识点

边界 — b 以及 B

babcb        执行“仅限整个单词”搜索->[试一下!](https://regex101.com/r/cO8lqs/25)

b 代表一个锚类似于符号 (等同于 $ 以及 ^) 的匹配位置, 其中一侧是单词字符(如 w),另外一侧不是单词字符(例如它可能是字符串的开头或空格字符)。

随之而来是它的否定, B. 这将会匹配所有 b 不会匹配的位置如果我们希望搜索模式可以被单词字符所匹配。

BabcB          仅在搜索模式被单词字符包围的时候才会匹配 -> [试一下!](https://regex101.com/r/cO8lqs/26)

返回引用— 1

([abc])1              使用 `1` 将会匹配与第一个捕获分组相同的文本 -> [试一下!](https://regex101.com/r/cO8lqs/14)    ([abc])([de])21      我们可以使用 2 (3, 4, 等等)来获取被第二个(第三个, 第四个, 等等.)捕获分组相同的文本 -> [试一下!](https://regex101.com/r/cO8lqs/15)    (?<foo>[abc])k<foo>   我们将分组名称命名为`foo` 并随后使用 `(k<foo>)` 来进行引用。结果与第一个正则表达式相同 -> [试一下!](https://regex101.com/r/cO8lqs/16)

前瞻和后瞻 — (?=) 以及 (?<=)

d(?=r)       匹配一个 `d` 并且其后有一个 `r`, 但是 `r` 将不会是整个正则表达式匹配的一部分-> [试一下!](https://regex101.com/r/cO8lqs/18)    (?<=r)d      匹配一个 `d` 并且前面有一个 `r`, 但是 `r` 将不会是整个正则表达式匹配的一部分-> [试一下!](https://regex101.com/r/cO8lqs/19)

我们也可以使用否定符号 !

d(?!r)         匹配一个 `d` 并且其后不是一个 `r`, 但是 `r` 将不会是整个正则表达式匹配的一部分-> [试一下!](https://regex101.com/r/cO8lqs/20)    (?<!r)d        匹配一个 `d` 并且前面不是一个 `r`, 但是 `r` 将不会是整个正则表达式匹配的一部分-> [试一下!](https://regex101.com/r/cO8lqs/21)

总结

正如你所见,正则表达式的应用程序字段可以是多个,我确信你已经认识到在开发人员职业生涯中看到的这些任务中的至少一个,这里有一个快速列表:

  • 数据验证 (比如检查一个时间字符串 i 的格式是正确的)
  • 数据抓取(特别是网页抓取,最终按特定顺序查找包含特定单词集的所有页面)
  • 数据转换(将数据从“原始”转换为另一种格式)
  • 字符串解析(例如捕获所有URL GET参数,捕获一组括号内的文本
  • 字符串替换(即使在使用通用 IDE 的代码会话期间,例如在相应的 JSON 对象中转换 Java 或 C# 类 – 将“;”替换为“,”将其设为小写,避免类型声明等)
  • 语法高亮,文件重命名,数据包嗅探和涉及字符串的许多其他应用程序(其中数据不必是文本)