Files
python-flask/project09/01.正则表达式.md
2025-09-11 16:52:33 +08:00

222 lines
14 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 正则表达式
- 正则表达式(Regular Expression)是一种文本模式包括普通字符例如a 到 z 之间的字母)和特殊字符(称为"元字符"),可以用来描述和匹配字符串的特定模式。
- 正则表达式是一种用于模式匹配和搜索文本的工具。
- 正则表达式提供了一种灵活且强大的方式来查找、替换、验证和提取文本数据。
- 正则表达式可以应用于各种编程语言和文本处理工具中,如 JavaScript、Python、Java、Perl 等。
- 正则表达式在线工具:
- https://regexr.com/
- https://regex101.com/
# 语法
- 正则表达式是一种用于匹配和操作文本的强大工具,它是由一系列字符和特殊字符组成的模式,用于描述要匹配的文本模式。
- 正则表达式可以在文本中查找、替换、提取和验证特定的模式。
- 例如:`happ+y` ,可以匹配 `happy``happppy``happpppppy` 等,+ 号代表前面的字符必须至少出现一次1次或多次
![image-20241209113003949](./01.%E6%AD%A3%E5%88%99%E8%A1%A8%E8%BE%BE%E5%BC%8F/image-20241209113003949.png)
- 例如:`happ*y` ,可以匹配 `hapy``happppy``happppppy` 等,* 号代表前面的字符可以不出现也可以出现一次或者多次0次、或1次、或多次
![image-20241209113419009](./01.%E6%AD%A3%E5%88%99%E8%A1%A8%E8%BE%BE%E5%BC%8F/image-20241209113419009.png)
- 例如:`colou?r` 可以匹配 `color` 或者 `colour``?` 问号代表前面的字符最多只可以出现一次0次或1次
![image-20241209113707759](./01.%E6%AD%A3%E5%88%99%E8%A1%A8%E8%BE%BE%E5%BC%8F/image-20241209113707759.png)
构造正则表达式的方法和创建数学表达式的方法一样。也就是用多种元字符与运算符可以将小的表达式结合在一起来创建更大的表达式。正则表达式的组件可以是单个的字符、字符集合、字符范围、字符间的选择或者所有这些组件的任意组合。
正则表达式是由普通字符(例如字符 a 到 z以及特殊字符称为"元字符")组成的文字模式。模式描述在搜索文本时要匹配的一个或多个字符串。正则表达式作为一个模板,将某个字符模式与所搜索的字符串进行匹配。
## 普通字符
普通字符包括没有显式指定为元字符的所有可打印和不可打印字符。这包括所有大写和小写字母、所有数字、所有标点符号和一些其他符号。
| 字符 | 描述 |
| :--------- | :----------------------------------------------------------- |
| **[ABC]** | 匹配 **[...]** 中的所有字符,例如 **[aeiou]** 匹配字符串 "google runoob taobao" 中所有的 e o u a 字母。 |
| **[^ABC]** | 匹配除了 **[...]** 中字符的所有字符,例如 **[^aeiou]** 匹配字符串 "google runoob taobao" 中除了 e o u a 字母的所有字符。 |
| **[A-Z]** | [A-Z] 表示一个区间,匹配所有大写字母,[a-z] 表示所有小写字母。 |
| **.** | 匹配除换行符(\n、\r之外的任何单个字符相等于 `[^\n\r]` 。 |
| **[\s\S]** | 匹配所有。\s 是匹配所有空白符,包括换行,\S 非空白符,不包括换行。 |
| **\w** | 匹配字母、数字、下划线。等价于 [A-Za-z0-9_] |
| **\d** | 匹配任意一个阿拉伯数字0 到 9。等价于 **[0-9]** |
## 非打印字符
非打印字符也可以是正则表达式的组成部分。下表列出了表示非打印字符的转义序列:
| 字符 | 描述 |
| :--- | :----------------------------------------------------------- |
| \cx | 匹配由x指明的控制字符。例如 \cM 匹配一个 Control-M 或回车符。x 的值必须为 A-Z 或 a-z 之一。否则,将 c 视为一个原义的 'c' 字符。 |
| \f | 匹配一个换页符。等价于 \x0c 和 \cL。 |
| \n | 匹配一个换行符。等价于 \x0a 和 \cJ。 |
| \r | 匹配一个回车符。等价于 \x0d 和 \cM。 |
| \s | 匹配任何空白字符,包括空格、制表符、换页符等等。等价于 [ \f\n\r\t\v]。注意 Unicode 正则表达式会匹配全角空格符。 |
| \S | 匹配任何非空白字符。等价于 `[^ \f\n\r\t\v]`。 |
| \t | 匹配一个制表符。等价于 \x09 和 \cI。 |
| \v | 匹配一个垂直制表符。等价于 \x0b 和 \cK。 |
## 特殊字符
所谓特殊字符,就是一些有特殊含义的字符,如上面说的 `happ*y` 中的 `*`,简单的说就是表示任何字符串的意思。如果要查找字符串中的 * 符号,则需要对 * 进行转义,即在其前加一个 `\``happ\*y` 匹配字符串 `happ*y`
许多元字符要求在试图匹配它们时特别对待。若要匹配这些特殊字符,必须首先使字符"转义",即,将反斜杠字符`\` 放在它们前面。下表列出了正则表达式中的特殊字符:
| 特别字符 | 描述 |
| :------- | :----------------------------------------------------------- |
| $ | 匹配输入字符串的结尾位置。如果设置了 RegExp 对象的 Multiline 属性,则 $ 也匹配 '\n' 或 '\r'。要匹配 $ 字符本身,请使用 \$。 |
| ( ) | 标记一个子表达式的开始和结束位置。子表达式可以获取供以后使用。要匹配这些字符,请使用 \( 和 \)。 |
| * | 匹配前面的子表达式零次或多次。要匹配 * 字符,请使用 \*。 |
| + | 匹配前面的子表达式一次或多次。要匹配 + 字符,请使用 \+。 |
| . | 匹配除换行符 \n 之外的任何单字符。要匹配 . ,请使用 \. 。 |
| [ | 标记一个中括号表达式的开始。要匹配 [,请使用 \[。 |
| ? | 匹配前面的子表达式零次或一次,或指明一个非贪婪限定符。要匹配 ? 字符,请使用 \?。 |
| \ | 将下一个字符标记为或特殊字符、或原义字符、或向后引用、或八进制转义符。例如, 'n' 匹配字符 'n'。'\n' 匹配换行符。序列 '\\' 匹配 "\",而 '\(' 则匹配 "("。 |
| ^ | 匹配输入字符串的开始位置,除非在方括号表达式中使用,当该符号在方括号表达式中使用时,表示不接受该方括号表达式中的字符集合。要匹配 ^ 字符本身,请使用 \^。 |
| { | 标记限定符表达式的开始。要匹配 {,请使用 \{。 |
| \| | 指明两项之间的一个选择。要匹配 \|,请使用 \|。 |
## 限定符
限定符用来指定正则表达式的一个给定组件必须要出现多少次才能满足匹配。有 `*``+``?``{n}``{n,}``{n,m}` 共6种。
正则表达式的限定符有:
| 字符 | 描述 |
| :---- | :----------------------------------------------------------- |
| * | 匹配前面的子表达式零次或多次。例如,**zo\*** 能匹配 **"z"** 以及 **"zoo"**。***** 等价于 **{0,}**。 |
| + | 匹配前面的子表达式一次或多次。例如,**zo+** 能匹配 **"zo"** 以及 "**zoo"**,但不能匹配 **"z"**。**+** 等价于 **{1,}**。 |
| ? | 匹配前面的子表达式零次或一次。例如,**do(es)?** 可以匹配 **"do"** 、 **"does"**、 **"doxy"** 中的 **"do"** 和 **"does"**。**?** 等价于 **{0,1}**。 |
| {n} | n 是一个非负整数。匹配确定的 **n** 次。例如,**o{2}** 不能匹配 **"Bob"** 中的 **o**,但是能匹配 **"food"** 中的两个 **o**。 |
| {n,} | n 是一个非负整数。至少匹配n 次。例如,**o{2,}** 不能匹配 **"Bob"** 中的 **o**,但能匹配 **"foooood"** 中的所有 **o**。**o{1,}** 等价于 **o+**。**o{0,}** 则等价于 **o\***。 |
| {n,m} | m 和 n 均为非负整数,其中 n <= m。最少匹配 n 次且最多匹配 m 次。例如,**o{1,3}** 将匹配 **"fooooood"** 中的前三个 **o**。**o{0,1}** 等价于 **o?**。请注意在逗号和两个数之间不能有空格。 |
以下正则表达式匹配一个正整数,`[1-9]` 设置第一个数字不是 0`[0-9]*` 表示任意多个数字:
![image-20241209171353890](./01.%E6%AD%A3%E5%88%99%E8%A1%A8%E8%BE%BE%E5%BC%8F/image-20241209171353890.png)
- 请注意,限定符出现在范围表达式之后。因此,它应用于整个范围表达式,在本例中,只指定从 0 到 9 的数字(包括 0 和 9
- 这里不使用 + 限定符,因为在第二个位置或后面的位置不一定需要有一个数字。也不使用 ? 字符,因为使用 ? 会将整数限制到只有两位数。
- 如果你想设置 0~99 的两位数,可以使用下面的表达式来至少指定一位但至多两位数字。
```
/[0-9]{1,2}/
```
上面的表达式的缺点是,只能匹配两位数字,而且可以匹配 0、00、01、10 99 的章节编号仍只匹配开头两位数字。
改进下,匹配 1~99 的正整数表达式如下:
```
/[1-9][0-9]?/
```
```
/[1-9][0-9]{0,1}/
```
`*``+` 限定符都是贪婪的,因为它们会尽可能多的匹配文字,只有在它们的后面加上一个 ? 就可以实现非贪婪或最小匹配。
例如,可能搜索 HTML 文档,以查找在 **h1** 标签内的内容。HTML 代码如下:
```html
<h1>欢迎来学习Python</h1>
```
**贪婪:**下面的表达式匹配从开始小于符号 (<) 到关闭 h1 标记的大于符号 (>) 之间的所有内容。
```
/<.*>/
```
![image-20241209171914936](./01.正则表达式/image-20241209171914936.png)
非贪婪:如果只需要匹配开始和结束 h1 标签,下面的非贪婪表达式只匹配 `<h1>`
![image-20241209171957088](./01.正则表达式/image-20241209171957088.png)
也可以使用以下正则表达式来匹配 h1 标签,表达式则是:
```
/<\w+?>/
```
![image-20241209172111729](./01.正则表达式/image-20241209172111729.png)
通过在 `*``+``?` 限定符之后放置 `?`,该表达式从"贪婪"表达式转换为"非贪婪"表达式或者最小匹配。
## 定位符
- 定位符使您能够将正则表达式固定到行首或行尾。它们还使您能够创建这样的正则表达式,这些正则表达式出现在一个单词内、在一个单词的开头或者一个单词的结尾。
- 定位符用来描述字符串或单词的边界,`^``$` 分别指字符串的开始与结束,`\b` 描述单词的前或后边界,`\B` 表示非单词边界。
- 正则表达式的定位符有:
| 字符 | 描述 |
| :--- | :----------------------------------------------------------- |
| ^ | 匹配输入字符串开始的位置。如果设置了 RegExp 对象的 Multiline 属性,^ 还会与 \n 或 \r 之后的位置匹配。 |
| $ | 匹配输入字符串结尾的位置。如果设置了 RegExp 对象的 Multiline 属性,$ 还会与 \n 或 \r 之前的位置匹配。 |
| \b | 匹配一个单词边界,即字与空格间的位置。 |
| \B | 非单词边界匹配。 |
- 注意:不能将限定符与定位符一起使用。由于在紧靠换行或者单词边界的前面或后面不能有一个以上位置,因此不允许诸如 `^*` 之类的表达式。
- 若要匹配一行文本开始处的文本,请在正则表达式的开始使用 `^` 字符。不要将 `^` 的这种用法与中括号表达式内的用法混淆。
- 若要匹配一行文本的结束处的文本,请在正则表达式的结束处使用 `$` 字符。
- 若要在搜索章节标题时使用定位点,下面的正则表达式匹配一个章节标题,该标题只包含两个尾随数字,并且出现在行首:
```
/^Chapter [1-9][0-9]{0,1}/
```
- 真正的章节标题不仅出现行的开始处,而且它还是该行中仅有的文本。它既出现在行首又出现在同一行的结尾。下面的表达式能确保指定的匹配只匹配章节而不匹配交叉引用。通过创建只匹配一行文本的开始和结尾的正则表达式,就可做到这一点。
```
/^Chapter [1-9][0-9]{0,1}$/
```
- 匹配单词边界稍有不同,但向正则表达式添加了很重要的能力。单词边界是单词和空格之间的位置。非单词边界是任何其他位置。下面的表达式匹配单词 Chapter 的开头三个字符,因为这三个字符出现在单词边界后面:
```
/\bCha/
```
- `\b` 字符的位置是非常重要的。如果它位于要匹配的字符串的开始,它在单词的开始处查找匹配项。如果它位于字符串的结尾,它在单词的结尾处查找匹配项。例如,下面的表达式匹配单词 Chapter 中的字符串 ter因为它出现在单词边界的前面
```
/ter\b/
```
- 下面的表达式匹配 Chapter 中的字符串 apt但不匹配 aptitude 中的字符串 apt
```
/\Bapt/
```
- 字符串 apt 出现在单词 Chapter 中的非单词边界处,但出现在单词 aptitude 中的单词边界处。对于 \B 非单词边界运算符,不可以匹配单词的开头或结尾,如果是下面的表达式,就不匹配 Chapter 中的 Cha
```
\BCha
```
## 选择
用圆括号 `()` 将所有选择项括起来,相邻的选择项之间用 `|` 分隔。
`()` 表示捕获分组,`()` 会把每个分组里的匹配的值保存起来, 多个匹配值可以通过数字 n 来查看(`n` 是一个数字,表示第 n 个捕获组的内容)。
```
<img src="(.*)" alt
```
![image-20241210102639516](./01.正则表达式/image-20241210102639516.png)