正则

正则

2022-01-12
java
  • 完整的正则表达式由两种字符构成,特殊字符(元字符)和文字(普通字符)。

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 4July 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+)的匹配结果完全相同。