正则表达式详解
文章目录
定义
正则表达式是你所定义的模式模板(pattern template),Linux工具可以用它来过滤文本。Linux 工具(比如sed编辑器或gawk程序)能够在处理数据时使用正则表达式对数据进行模式匹配。
正则表达式的类型
使用正则表达式最大的问题在于有不止一种类型的正则表达式。Linux中的不同应用程序可 能会用不同类型的正则表达式。
正则表达式是通过正则表达式引擎(regular expression engine)实现的。正则表达式引擎是 一套底层软件,负责解释正则表达式模式并使用这些模式进行文本匹配。
在Linux中,有两种流行的正则表达式引擎:
- POSIX基础正则表达式(basic regular expression,BRE)引擎
- POSIX扩展正则表达式(extended regular expression,ERE)引擎
大多数Linux工具都至少符合POSIX BRE引擎规范,能够识别该规范定义的所有模式符号。 遗憾的是,有些工具(比如sed编辑器)只符合了BRE引擎规范的子集。这是出于速度方面的考 虑导致的,因为sed编辑器希望能尽可能快地处理数据流中的文本。
POSIX BRE引擎通常出现在依赖正则表达式进行文本过滤的编程语言中。它为常见模式提供 了高级模式符号和特殊符号,比如匹配数字、单词以及按字母排序的字符。gawk程序用ERE引擎 来处理它的正则表达式模式。
定义BRE模式
纯文本
正则表达式并不关心模式在数据流中的位置。它也不关心模式出现了多少次。一旦正则表达式匹配了文本字符串中任意位置上的模式,它就会将该字符串传回Linux工具。
关键在于将正则表达式模式匹配到数据流文本上。重要的是记住正则表达式对匹配的模式非 常挑剔。第一条原则就是:正则表达式模式都区分大小写。这意味着它们只会匹配大小写也相符 的模式。
你也不用局限于在正则表达式中只用单个文本单词,可以在正则表达式中使用空格和数字。甚至可以创建匹配多个连续空格的正则表达式模式。
特殊字符
正则表达式识别的特殊字符包括:
.*[]^${}\+?|()
如果要用某个特殊字符作为文本字符,就必须转义。在转义特殊字符时,你需要在它前面加 一个特殊字符来告诉正则表达式引擎应该将接下来的字符当作普通的文本字符。这个特殊字符就 是反斜线()。
由于反斜线是特殊字符,如果要在正则表达式模式中使用它,你必须对其转义,这样就产生 了两个反斜线。
要使用正斜线,也需要进行转义。
锚字符
有两个特殊字符可以用来将模式锁定在数据流中的行首或行尾。
1. 锁定在行首
脱字符(^)定义从数据流中文本行的行首开始的模式。如果模式出现在行首之外的位置, 正则表达式模式则无法匹配。
要用脱字符,就必须将它放在正则表达式中指定的模式前面。
$ echo "The book store" | sed -n '/^book/p'
脱字符会在每个由换行符决定的新数据行的行首检查模式。
只要模式出现在新行的行首,脱字符就能够发现它。 如果你将脱字符放到模式开头之外的其他位置,那么它就跟普通字符一样,不再是特殊字符了:
$ echo "This is a test" | sed -n '/s ^/p'
如果指定正则表达式模式时只用了脱字符,就不需要用反斜线来转义。但如果你在模式中先指定了脱字符,随后还有其他一些文本,那么你必须在脱字符前用转义字符。
2. 锁定在行尾
跟在行首查找模式相反的就是在行尾查找。特殊字符美元符($)定义了行尾锚点。将这个 特殊字符放在文本模式之后来指明数据行必须以该文本模式结尾。
$ echo "This is a good book" | sed -n '/book$/p'
3. 组合锚点
可以在同一行中将行首锚点和行尾锚点组合在一起使用。在第一种情况 中,假定你要查找只含有特定文本模式的数据行。
$ sed -n '/^this is a test$/p' data4
sed编辑器忽略了那些不单单包含指定的文本的行。
将两个锚点直接组合在一起,之间不加任何 文本,这样过滤出数据流中的空白行.
$ sed '/ $/d' data5
点号字符
特殊字符点号用来匹配除换行符之外的任意单个字符。它必须匹配一个字符,如果在点号字 符的位置没有字符,那么模式就不成立。在正则表达式中, 空格也是字符.
$ sed -n '/.at/p' data6
字符组
可以定义字符组用来匹配文本模式中某个位置的一组字符。如果字符组中的某个字符出现在了数据 流中,那它就匹配了该模式。
使用方括号来定义一个字符组。方括号中包含所有你希望出现在该字符组中的字符。然后你 可以在模式中使用整个组,就跟使用其他通配符一样。
$ sed -n '/[ch]at/p' data6
排除型字符组
在正则表达式模式中,也可以反转字符组的作用。可以寻找组中没有的字符,而不是去寻找组中含有的字符。要这么做的话,只要在字符组的开头加个脱字符。
$ sed -n '/[^ch]at/p' data6
通过排除型字符组,正则表达式模式会匹配c或h之外的任何字符以及文本模式。由于空格字 符属于这个范围,它通过了模式匹配。但即使是排除,字符组仍然必须匹配一个字符,所以以at开头的行仍然未能匹配模式。
区间
可以用单破折线符号在字符组 中表示字符区间。只需要指定区间的第一个字符、单破折线以及区间的最后一个字符就行了。根据Linux系统采用的字符集,正则表达式会包括此区间内的任意字符。
现在你可以通过指定数字区间来简化邮编的例子。
$ sed -n '/^[0-9][0-9][0-9][0-9][0-9]$/p' data8
还可以在单个字符组指定多个不连续的区间。
$ sed -n '/[a-ch-m]at/p' data6
该字符组允许区间a~c、h~m中的字母出现在at文本前,但不允许出现d~g的字母。
特殊的字符组
除了定义自己的字符组外,BRE还包含了一些特殊的字符组,可用来匹配特定类型的字符。
可以在正则表达式模式中将特殊字符组像普通字符组一样使用。
$ echo "abc" | sed -n '/[[:digit:]]/p'
星号
在字符后面放置星号表明该字符必须在匹配模式的文本中出现0次或多次。
$ echo "ik" | sed -n '/ie*k/p'
模式中的u*表明字母u可能出现或不出现在匹配模式的文本中。类似地,如果你知道一个单 词经常被拼错,你可以用星号来允许这种错误。
星号还能用在字符组上。它允许指定可能在文本中出现多次的字符组或字符区间。
$ echo "bt" | sed -n '/b[ae]*t/p'
只要a和e字符以任何组合形式出现在b和t字符之间(就算完全不出现也行),模式就能够匹配。如果出现了字符组之外的字符,该模式匹配就会不成立。
扩展正则表达式
POSIX ERE模式包括了一些可供Linux应用和工具使用的额外符号。gawk程序能够识别ERE模式,但sed编辑器不能。
记住,sed编辑器和gawk程序的正则表达式引擎之间是有区别的。gawk程序可以使用大多 数扩展正则表达式模式符号,并且能提供一些额外过滤功能,而这些功能都是sed编辑器 所不具备的。但正因为如此,gawk程序在处理数据流时通常才比较慢。
问号
问号类似于星号,不过有点细微的不同。问号表明前面的字符可以出现0次或1次,但只限于 此。它不会匹配多次出现的字符。
$ echo "bt" | gawk '/be?t/{print $0}'
如果字符e并未在文本中出现,或者它只在文本中出现了1次,那么模式会匹配。 与星号一样,你可以将问号和字符组一起使用。
加号
加号是类似于星号的另一个模式符号,但跟问号也有不同。加号表明前面的字符可以出现1次或多次,但必须至少出现1次。
$ echo "beeet" | gawk '/be+t/{print $0}'
如果字符e没有出现,模式匹配就不成立。加号同样适用于字符组,与星号和问号的使用方 式相同。
使用花括号
ERE中的花括号允许你为可重复的正则表达式指定一个上限。这通常称为间隔(interval)。 10 可以用两种格式来指定区间。
- m:正则表达式准确出现m次。
- m, n:正则表达式至少出现m次,至多n次。
这个特性可以精确调整字符或字符集在模式中具体出现的次数。
默认情况下,gawk程序不会识别正则表达式间隔。必须指定gawk程序的–re-interval 命令行选项才能识别正则表达式间隔。
$ echo "bet" | gawk --re-interval '/be{1}t/{print $0}'
通过指定间隔为1,限定了该字符在匹配模式的字符串中出现的次数。如果该字符出现多次, 模式匹配就不成立。
很多时候,同时指定下限和上限也很方便。
$ echo "bt" | gawk --re-interval '/be{1,2}t/{print $0}'
管道符号
管道符号允许你在检查数据流时,用逻辑OR方式指定正则表达式引擎要用的两个或多个模 式。如果任何一个模式匹配了数据流文本,文本就通过测试。如果没有模式匹配,则数据流文本 匹配失败。
使用管道符号的格式如下:
expr1|expr2|...
这里有个例子。
$ echo "The cat is asleep" | gawk '/cat|dog/{print $0}'
这个例子会在数据流中查找正则表达式cat或dog。正则表达式和管道符号之间不能有空格, 否则它们也会被认为是正则表达式模式的一部分。
管道符号两侧的正则表达式可以采用任何正则表达式模式(包括字符组)来定义文本。
表达式分组
正则表达式模式也可以用圆括号进行分组。当你将正则表达式模式分组时,该组会被视为一 个标准字符。可以像对普通字符一样给该组使用特殊字符。
$ echo "Sat" | gawk '/Sat(urday)?/{print $0}'
结尾的urday分组以及问号,使得模式能够匹配完整的Saturday或缩写Sat。
将分组和管道符号一起使用来创建可能的模式匹配组是很常见的做法。
$ echo "cat" | gawk '/(c|b)a(b|t)/{print $0}'
模式(c|b)a(b|t)会匹配第一组中字母的任意组合以及第二组中字母的任意组合。
文章作者 Forz
上次更新 2017-08-12