--- date: 2020-12-01 --- - [简介](#简介) - [定义](#定义) - [分类](#分类) - [Linux 常用文本工具](#linux-常用文本工具) - [grep, egrep](#grep-egrep) - [sed](#sed) - [awk(gawk)](#awkgawk) - [匹配单个字符](#匹配单个字符) - [匹配纯文本](#匹配纯文本) - [匹配任意字符](#匹配任意字符) - [匹配一组字符](#匹配一组字符) - [匹配多个字符中的某一个](#匹配多个字符中的某一个) - [使用字符区间](#使用字符区间) - [取非匹配](#取非匹配) - [小结](#小结) - [使用元字符](#使用元字符) - [对特殊字符进行转义](#对特殊字符进行转义) - [匹配空白字符](#匹配空白字符) - [匹配特定的字符类别](#匹配特定的字符类别) - [匹配数字(非数字)](#匹配数字非数字) - [匹配字母和数字(非字母和数字)](#匹配字母和数字非字母和数字) - [匹配空白字符(非空白字符)](#匹配空白字符非空白字符) - [小结](#小结-1) - [重复匹配](#重复匹配) - [有多少个匹配](#有多少个匹配) - [匹配一个或多个字符](#匹配一个或多个字符) - [匹配零个或多个字符](#匹配零个或多个字符) - [匹配零个或一个字符](#匹配零个或一个字符) - [匹配的重复次数](#匹配的重复次数) - [为重复匹配次数设定一个区间](#为重复匹配次数设定一个区间) - [匹配“至少重复多少次”](#匹配至少重复多少次) - [防止过度匹配](#防止过度匹配) - [小结](#小结-2) - [位置匹配](#位置匹配) - [边界](#边界) - [单词边界](#单词边界) - [字符串边界](#字符串边界) - [小结](#小结-3) - [使用子表达式](#使用子表达式) - [什么是子表达式](#什么是子表达式) - [子表达式的嵌套](#子表达式的嵌套) - [小结](#小结-4) - [回溯引用:前后一致匹配](#回溯引用前后一致匹配) - [作用](#作用) - [回溯引用匹配](#回溯引用匹配) - [应用](#应用) - [大小写转换](#大小写转换) - [小结](#小结-5) - [前后查找](#前后查找) - [前后查找](#前后查找-1) - [向前查找](#向前查找) - [向后查找](#向后查找) - [把向前查找和向后查找结合起来](#把向前查找和向后查找结合起来) - [对前后查找取非](#对前后查找取非) - [小结](#小结-6) - [思维导图](#思维导图) - [公众号](#公众号)  # 简介 推荐阅读:Jeffrey Friedl 《精通正则表达式(第 3 版)》,本文是该书的`读书笔记`。 ## 定义 正则表达式:regular expression(regex)是用来描述或者匹配一系列符合某个句法规则的字符串的单个字符串。正则表达式这个概念最初是由 Unix 中的工具软件(例如 sed 和 grep)普及开的。 ## 分类 - `BREs`:基本的正则表达式(Basic Regular Expression) - `EREs`:扩展的正则表达式(Extended Regular Expression) - `PREs`:Perl 的正则表达式(Perl Regular Expression) ## Linux 常用文本工具 只有掌握了正则表达式,才能全面地掌握 Linux 下的常用文本工具(例如:grep、egrep、GUN sed、 Awk 等)的用法。 ### grep, egrep 1. grep 支持 BREs、EREs、PREs 正则表达式: - grep 指令后不跟任何参数,则表示要使用 `BREs` - grep 指令后跟 `-E` 参数,则表示要使用 `EREs` - grep 指令后跟 `-P` 参数,则表示要使用 `PREs` 2. egrep 支持 EREs、PREs 正则表达式: - egrep 指令后不跟任何参数,则表示要使用 `EREs` - egrep 指令后跟 `-P` 参数,则表示要使用 `PREs` ### sed 详细信息可参考 [sed, a stream editor](https://www.gnu.org/software/sed/manual/sed.html) sed 文本工具支持:BREs、EREs - 指令默认是使用 BREs - 命令参数 -r ,则表示要使用 EREs sed 功能与作用: - 处理的对象:文本文件 - 处理操作:对文本文件的内容进行查找、替换、删除和增加等操作 - 在处理文本文件的时候,也是按行处理的 ### awk(gawk) [awk 入门教程](https://www.ruanyifeng.com/blog/2018/11/awk.html) awk 文本工具处理文本的特点 - awk 处理的对象:文本文件 - awk 处理操作:主要是对列进行操作 # 匹配单个字符 ## 匹配纯文本 文本 ``` Hello, my name is Ben. ``` 正则表达式 ``` Ben ``` 结果  ## 匹配任意字符 `.`字符可以匹配任何一个单个的字符。 文本 ``` sales1.xls sales2.xls sales3.xls na1.xls na2.xls orders3.xls apac1.xls europe2.xls ``` 正则表达式 ``` sales. ``` 结果   # 匹配一组字符 ## 匹配多个字符中的某一个 ``` sales1.xls sales2.xls sales3.xls na1.xls na2.xls orders3.xls apac1.xls europe2.xls ``` 正则表达式 ``` [ns]a.\.xls ``` 结果  ## 使用字符区间 在使用正则表达式的时候,会频繁地用到一些字符区间(0-9、A-Z)。为了简化字符区间的定义,正则表达式提供一个特殊的元字符:`-`作为连字符。 正则表达式 ``` [ns]a[0-9]\.xls ``` 结果  匹配任何一个字母(无论大小写)或数字 ``` [A-Za-z0-9] ``` ## 取非匹配 字符集合通常用来指定一组必须匹配其中之一的字符。但是在某些场合下,我们需要反过来做,给出一组不需要得到的字符。换句话说:`除了那个字符集合里的字符,其他字符都可以匹配`,使用字符`^`。 正则表达式 ``` [ns]a[^0-9]\.xls ``` 结果:上述输入没有结果,因为没有匹配字符串。 ## 小结 元字符`[`和`]`用来定义一个字符集合,必须匹配该集合里的字符之一。定义一个字符集合的具体方法有两种: 1. 把所有的字符都列举出来 2. 利用元字符`-`以字符区间的方式给出 字符集合可以用元字符`^`来求非,这将把给定的字符集合强行排除在匹配操作外——除了该字符集合里的字符,其他字符都可以被匹配。 # 使用元字符 ## 对特殊字符进行转义 元字符是一些在正则表达式里有特殊含义的字符。英文句号`.`是一个元字符,用来匹配任何一个单个字符;左方括号`[`也是一个元字符,表示一个字符集合的开始。 因为元字符在正则表达式中有特殊的含义,所以这些字符无法代表它们本身。需要在元字符的前面加上一个`反斜杠`进行转义——转义序列`\.`将匹配`.`本身。 文本 ``` \home\ben\sales\ ``` 正则表达式 ``` \\ ``` 结果  ## 匹配空白字符 在进行正则表达式搜索的时候,我们经常会遇到需要对原始文本里的非打印空白字符进行匹配的情况。比如找出所有的制表符或换行符,这类字符很难被直接输入到正则表达式里,可以用如下的特殊元字符来输入。 | 元字符 | 说明 | | :----: | :------------------------------------: | | [\b] | 回退(并删除)一个字符(Backspace 键) | | \f | 换页符 | | \n | 换行符 | | \r | 回车符 | | \t | 制表符 | | \v | 垂直制表符 | 文本 ``` "101","Ben","Forta" "102","Jim","James" ``` 正则表达式 ``` \n\n ``` 结果  ``` "101","Ben","Forta" "102","Jim","James" ``` 分析 `\r\n`匹配一个“回车+换行”组合,Windows 操作系统都把这个组合作为文本行的结束标签。使用正则表达式`\r\n\r\n`进行的搜索将匹配两个连续的行尾标签,正是两条记录之间的空白行。 匹配文本结束标签 - Windows:\r\n - Linux : \n ## 匹配特定的字符类别 字符集合(匹配多个字符中的某一个)是最常见的匹配形式,而一些常用的字符集合可以用`特殊元字符`来替代。 ### 匹配数字(非数字) | 元字符 | 说明 | | ------ | ----------------------------------- | | \d | 任何一个数字字符(等价于 [0-9]) | | \D | 任何一个非数字字符(等价于 [^0-9]) | ### 匹配字母和数字(非字母和数字) | 元字符 | 说明 | | ------ | --------------------------------------------------- | | \w | 任何一个字母数字字符或下划线字符([a-zA-Z0-9_]) | | \W | 任何一个非字母数字字符或下划线字符([^a-zA-Z0-9_]) | 文本 ``` 11213 A1C2E3 48075 M1B4F2 ``` 正则表达式 ``` \w\d\w\d\w\d ``` 结果  ### 匹配空白字符(非空白字符) 另一种常见的字符类别是`空白字符`。 | 元字符 | 说明 | | ------ | ------------------------------------------ | | \s | 任何一个空白字符(等价于 [\f\n\r\t\v]) | | \S | 任何非一个空白字符(等价于 [^\f\n\r\t\v]) | ## 小结 主要讲解用来匹配特定字符(制表符、换行符)和用来匹配一个字符集合或字符类(数字、字母数字字符)的元字符。这些简短的元字符可以用来简化正则表达式模式。 # 重复匹配 ## 有多少个匹配 需要一种能够匹配多个字符的方法,可以通过几个特殊的元字符来实现。 ### 匹配一个或多个字符 要想匹配同一个字符(或字符集合)的多次重复,只要简单地给这个字符(或字符集合)加上一个`+`字符作为后缀就可以了。`+`匹配`一个或多个字符`(至少一个,不匹配零个字符的情况)。 比如:a 匹配 a 本身,a+将匹配一个或多个连续出现的 a。 文本 ``` Send personal email to ben@forta.com. For questions about a book use support@forta.com. Feel free to send unsolicited email to spam@forta.com. ``` 正则表达式 ``` \w+@\w+\.\w+ ``` 结果  这个模式把原始文本里的 3 个电子邮件地址全都正确匹配出来了。正则表达式中第一个 `\w+` 匹配一个或多个字母数字字符,再用第二个 `\w+` 匹配@后面的一个或多个字符,然后匹配一个 `.` 字符(使用转义序列 `\.`),最后用第三个 `\w+` 匹配电子邮件地址的剩余部分。 ### 匹配零个或多个字符 `+`匹配一个或多个字符,但不匹配零个字符(最少也要匹配一个字符)。那么,如果你想匹配一个可有可无的字符—,也就是该字符可以出现零次或多次的情况,该怎么办呢? 这种匹配需要用`*`元字符来完成,把它放在一个字符(或一个字符集合)的后面,就可以匹配该字符(或字符集合)连续出现零次或多次的情况。 ### 匹配零个或一个字符 `?`只能匹配一个字符(或字符集合)的零次或一次出现,最多不超过一次。如果需要在一段文本里匹配某个特定的字符,而该字符可能出现、也可能不出现,`?`无疑是最佳的选择。 文本 ``` The URL is http://www.forta.com/, to connect securely use https://www.forta.com/ ``` 正则表达式 ``` https?: ``` 结果  这个模式的开头部分是`https?`。? 在这里的含义是:前面的字符 s 要么不出现,要么最多出现一次。 ## 匹配的重复次数 正则表达式里的`+ * ?`解决了许多问题,但是光靠这些还不够。比如: - `+` 和 `*` 匹配的字符个数没有上限。我们无法为它们将匹配的字符个数设定一个最大值。 - `+`、`*` 和 `?` 至少匹配零个或一个字符。我们无法为它们将匹配的字符个数另行设定一个最小值。 - 如果只使用 `+`和 `*`,我们无法把它们将匹配的字符个数设定为一个精确的数字。 为了解决这些问题并且对重复性匹配有更多的控制,正则表达式语言提供了一个用来设定`重复次数`的语法。重复次数要用 `{}` 来给出——把数值写在它们之间。 ### 为重复匹配次数设定一个区间 为重复匹配次数设定一个最小值和最大值,这种区间必须以`{2, 4}`这样的形式给出,含义是最少重复 2 次、最多重复 4 次。 文本 ``` 4/8/03 10-6-2004 2/2/2 01-01-01 ``` 正则表达式 ``` \d{1,2}[-\/]\d{1,2}[-\/]\d{2,4} ``` 结果  ### 匹配“至少重复多少次” `{3,}` 表示至少重复 3 次。 ## 防止过度匹配 文本 ``` AK and HI ``` 正则表达式 ``` <[Bb]>.*[Bb]> ``` 结果  这个正则表达式匹配了所有字符,而不是预期的标签内的内容。为什么会这样?因为`*`和`+`都是所谓的`贪婪型`元字符,它们在进行匹配时的行为模式是多多益善而不是适可而止的。 在不需要这种“贪婪行为”的时候该怎么办?答案是使用这些元字符的“懒惰型”版本。懒惰型元字符的写法很简单,只要给贪婪型元字符加上一个`?`后缀即可。 | 贪婪型元字符 | 懒惰型元字符 | | ------------ | ------------ | | * | *? | | + | +? | | {n,} | {n,}? | 对于上面的例子,使用正则表达式 ``` <[Bb]>.*?[Bb]> ``` 结果  ## 小结 正则表达式的真正威力体现在重复次数匹配方面。 - `+`:匹配字符的一次或多次出现 - `?`:匹配字符的 0 次或一次出现 - `*`:匹配字符的 0 次或多次出现 - `{}`:精确地设定重复次数 元字符分`贪婪型`和`懒惰型`两种;在需要防止过度匹配的场合下,使用`懒惰型`元字符来构造你的正则表达式。 # 位置匹配 ## 边界 位置匹配用来解决`在什么地方`进行字符串匹配操作的问题。例如使用`cat`正则搜索文本,`scatter`也会被匹配到,如果只是想搜索`cat`这个单词,就需要边界。 ## 单词边界 单词边界由限定符`\b`指定,匹配一个单词的开始或结尾。\b 匹配的是这样的位置,这个位置位于一个能够用来构成单词的字符(字母、数字和下划线,也就是与、w 相匹配的字符)和一个不能用来构成单词的字符(\W)之间。 如果不想匹配单词边界,使用`\B`。 文本 ``` The cat scattered his food. ``` 正则表达式 ``` \bcat\b ``` 结果  ## 字符串边界 单词边界可以用来进行与单词有关的位置匹配(单词的开头、单词的结束、整个单词)。字符串边界有着类似的用途,用来进行与字符串有关的位置匹配(字符串的开头、字符串的结束、整个字符串)。用来定义字符串边界的元字符有两个: - `^`:定义字符串开头 - `$`:定义字符串结尾 文本 ``` xmlns:blablabla ``` 正则表达式 ``` ^\s*<\?xml.*\?> ``` 结果  ## 小结 正则表达式不仅可以用来匹配任意长度的文本块,还可以用来匹配出现在字符串中特定位置的文本。`\b`用来指定一个单词边界(`\B` 刚好相反)。`^`和`$`用来指定字符串边界(字符串的开头和结束)。 # 使用子表达式 ## 什么是子表达式 我们已经知道了如何匹配一个字符的连续多次重复。比如`\d+`将匹配一个或多个数字字符,而`https?`将匹配 http 或 https。但是这两个用来表明重复次数的元字符`只作用于紧挨着它的前一个字符或元字符`。 子表达式是一个更大的表达式的一部分;把一个表达式划分为一系列子表达式的目的是为了把那些子表达式当作一个独立的元素来使用。子表达式必须用`()`括起来。 文本 ``` Hello, my name is yano I like 123 ``` 正则表达式 ``` (l){2,} ``` 结果  `(l)`是一个子表达式,它是一个独立的元素,紧跟在它后面的`{2,}`将作用于这个子表达式。 ## 子表达式的嵌套 子表达式允许嵌套,多重嵌套的子表达式可以构造出功能极其强大的正则表达式来,但是难免会让模式变得难以阅读和理解。 如何匹配一个`IP 地址`? 一个合法的 IP 地址里的各组数字必须满足: - 任何一个 1 位或 2 位数字 - 任何一个以 1 开头的 3 位数字 - 任何一个以 2 开头、第 2 位数字在 0~5 之间的 3 位数字 - 任何一个以 25 开头、第 3 位数字在 0~5 之间的 3 位数字 正则表达式 ``` (((\d{1,2})|(1\d{2})|(2[0-4]\d)|(25[0-5]))\.){3}((\d{1,2})|(1\d{2})|(2[0-4]\d)|(25[0-5])) ``` ## 小结 子表达式的作用是把同一个表达式的各个相关部分组合在一起。子表达式必须用`()`来定义。 # 回溯引用:前后一致匹配 ## 作用 首先看一个例子。HTML 程序员经常使用标题标签(`