正则
2022-01-12
- 完整的正则表达式由两种字符构成,特殊字符(元字符)和文字(普通字符)。
egrep命令 #
元字符 | 案例 |
---|---|
^ 匹配行的开始,$ 匹配行的结束。 |
^cat$ 匹配以c作为一行的第一个字符,紧接着一个a,紧接着以t结尾。 |
[abc] (字符组)匹配多个字符之一 |
[-a-cA-D1.?_] 匹配-abcABCD1.?_ 中的一个。- (连字符)表示一个范围,只有在字符组内他才可能是一个元字符,如果连字符出现在字符组开头,那么它表示的就是一个普通符号。在字符组中,`?.* |
[^ABC] 排除型字符组 |
q[^u] 匹配q后面的字符不为u的情况,注意:这要求q后面必须要有一个字符。[^- 中的连字符也不是一个元字符。 |
. 匹配任意字符 |
注意:在字符组中的. 是普通字符。 |
` | ` 表示或 |
egrep的-i 参数表示忽略大小写。 |
|
\< 和\> 单词的起始位置和结束位置。 |
\<cat 匹配以cat开头的单词,cat\> 匹配以cat结尾的单词。 |
? 表示可选 |
July? 表示y是可选的,该正则可以匹配July和Jul。July? 4(th)? 中的th是可选的,可以匹配July 4 和July 4th 。 |
+ 表示紧邻的元素出现一次或多次* 表示紧邻的元素出现0次或多次 |
.+ 表示匹配任意字符至少一次。.* 表示匹配任意字符0次到无数次。 |
{min,max} 区间量词 |
允许重现的次数在[min,max]之间。 |
反向引用 | 如([a-z]{3})([0-9]).+\1\2 括号() 可以记住子表达式的文本,元字符\1\2 可以引用这些文本。从而这个正则可以匹配the9 the9 。 |
\ 转义:将元字符转义为普通字符 |
\. 表示一个普通的. |
() 限制多选结构、分组、捕获文本 |
限制多选结构指的是和` |
(?:…) | 仅用于分组,但是不捕获 |
Perl #
使用正则匹配文本 #
- Perl用
$variable =~ m/regex/
来判断一个正则表达式能否匹配某个字符串,m
表示匹配match
,可以省略,斜线用来标注正则表达式的边界。 - 元字符是具有特殊意义的字符,各个语言中对元字符的定义并不是统一的。
- Perl和其他流派的正则表达式提供了许多有用的简记法。
\b | 作为单词起始和单词结束的元字符 |
---|---|
\t | 制表符 |
\n | 换行符 |
\r | 回车符 |
\s | 任何空白字符,如空格符、制表符等 |
\S | 除了\s 外的任何字符 |
\w | [a-zA-Z0-9_] |
\W | 除了\w 外的任何符号 |
\d | [0-9] |
\D | 除了\d 外的任何符号 |
/i
修饰符表示此测试不区分大小写。(?:...)
表示可以用来分组文本,但是不捕获。- 匹配成功后,Perl可以使用
$1
$2
$3
之类的变量来保存相对应的(...)
括号内的子表达式匹配的文本。 - 子表达式的编号按照小括号出现的先后排序,从1开始,子表达式可以嵌套。
# perl温度转换
print "enter a temperature(e.g., 32F, 100C):\n";
$input = <STDIN>; #接收用户输入
chomp($input); #去掉输入末尾的换行
if ($input =~ m/^([-+]?[0-9]+(\.[0-9]*)?)\s*([CF])$/i)
{
$InputNum = $1;
$type = $3;
if ($type =~ m/c/i) {
#输入的是摄氏温度,计算华氏温度
$celsius = $InputNum;
$fahrenheit = ($celsius * 9 / 5) + 32;
} else {
#输入的是华氏温度,计算摄氏温度
$fahrenheit = $InputNum;
$celsius = ($fahrenheit - 32) * 5 / 9;
}
printf "%.2f C is %.2f F\n", $celsius, $fahrenheit;
} else {
print "expecting a number followed by \"C\" or \"F\",\n";
print "so I don't understand \"$input\" .\n";
}
使用正则修改文本 #
$var =~ s/regex/replacement/
当正则表达式能够匹配$var中的某段文本,则将这段文本替换为replacement。此替换只发生一次。/g
用于在s/.../.../
第一次替换完成后继续搜索更多的匹配文本,进行更多的替换。
环视功能 #
- 环视是在文本的特定位置上匹配左边或者右边的文本,但是不会占用字符,类似
\b
^
$
,但是更加通用。
介绍 | |
---|---|
(?=...) 肯定顺序环视 |
在当前位置向右查看文本,尝试匹配子表达式,如果能匹配,就返回匹配成功信息。 |
(?!...) 否定顺序环视 |
在当前位置向右查看文本,尝试匹配子表达式,如果不能够匹配,就返回成功信息。 |
(?<=...) 肯定逆序环视 |
在当前位置向左查看文本,尝试匹配子表达式,如果匹配成功,就返回匹配成功信息。 |
(?<!...) 否定逆序环视 |
在当前位置向左查看文本,尝试匹配子表达式,如果不能匹配,就返回成功信息。 |
匹配原理 #
优先选择最左边的匹配结果 #
- 起始位置最靠左的匹配结果总是优于其他可能的匹配结果。
- 匹配会从需要查找的字符串的起始位置开始尝试匹配,在起始位置测试正则表达式不能匹配后,就从第二个字符开始测试匹配,直到找到能匹配成功的情况或到了字符串的最后一个字符。
正则:fat|cat|belly|your
文本:the dragging belly indicates that your cat is too fat
结果:belly
标准量词?、*、+、{min,max}是优先匹配的。 #
- 例:
^.*([0-9][0-9])
匹配abot24characterslong的过程。.*
匹配整个字符串以后,第一个[0-9]
的匹配要求.*
吐出来一个字符g
,但这并不能让[0-9]
匹配,所以.*
必须继续吐字符,接下来的字符是n
,如此循环15次,直到.*
吐出来了4
。- 即使第一个
[0-9]
能匹配4
,但是第二个[0-9]
仍然不能匹配,为了匹配正则表达式,[.*]
必须再次释放一个字符,这次是2
,第一个由[0-9]
匹配,4
能够由[0-9]
匹配,所以刺配成功,\1的值是24
。
传统NFA的多选结构是匹配优先的 #
- 传统NFA遇到多选结构是,会按照从左到右的顺序检查表达式中的多选分支。如
^(subject|date):*
,当遇到此选择分支时,首先尝试匹配subject,如果可以匹配,就匹配接下来的:*
。如果无法匹配,就尝试其他多选分支(尝试匹配date)。即多选结构既不是匹配优先,也不是忽略优先,而是按照多选结构的顺序。 - 用
tour|to|tournament
来匹配three tournaments
时会得到什么呢?
回溯 #
- NFA最重要的性质是,它会依次处理各个子表达式或组成元素,需要在两个可能成功的可能中进行选择的时候,它会选择其一,同时记住另一个,以备稍后可能的需要。
- 面对多个选择时,选择哪个分支呢?如果在进行尝试和跳过尝试之间选择,对于匹配优先量词,引擎会优先选择进行尝试,而对于忽略优先量词,会选择跳过尝试。
- 当发生回溯时,距离当前最近存储的选项就是当本地失败强制回溯返回的,使用的原则是LIFO(后进先出)。
- 回溯不但需要重新计算正则表达式和文本的对应位置,也需要维护括号内的子表达式所匹配的文本的状态。
固化分组 #
- 固化分组可能会放弃某些可能的路径,使用
(?>...)
。 - 使用固化分组与正常的匹配毫无差别,但是当匹配到固化分组结构之后,在固化分组中的所有备用状态都会被放弃。在固化分组匹配结束时,他已匹配的文本已经固化为一个单元,只能作为整体而保留或放弃。
(?>.*?)
永远无法匹配任何字符。
占有优先量词?=
*+
++
{m,n}+
#
- 占有优先量词和匹配优先量词很相似,但是占有优先量词从不归还已匹配的字符。
- 占有优先量词和固化分组非常相似,如
w++
和(?>w+)
的匹配结果完全相同。