本博客来自精通正则表达式第三版
第一章:正则表达式入门(Introduction to regex)
正则表达式概述
首先需要理解这玩意是干啥的,为啥我们要学这玩意。学编程是避不开字符串的各种杂七杂八的处理的,说到底程序还是字符串堆砌,无论是搞爬虫是我们需要定向检索某一类的资源,或是数据库对各种信息的处理,亦或是最单纯的提取我们需要的各类文字、数字,我们总是希望使用某种方式直接获得我们想要的东西,跟其他方法比起来,正则有得天独厚的优势,具体细节暂且不表。
下面是书上对正则表达式正式的定义:
正则表达式是强大便捷高效的文本处理工具。正则表达式本身,再加上如同袖珍编程语言的通用模式表示法,赋予使用者描述和分析文本的能力。配合上特定工具提供的额外支持,可以方便高效的添加,删除,分离,叠加,插入和修整各种类型的文本和数据。
与文件名作类比,比如在文件管理界面,使用诸如“.txt”的字符串能够用来选择多个文件,部分字符我们把它们称为通配符。有些特定字符有其特殊意义,比如星号表示任意文本,问号表示任意单个字符。所以“.txt”能够匹配任意字符开头,以“.txt”结尾的文件。但这些文件名模式表达能力有限,只提供了少量附加特殊字符。而正则表达式是一种更为通用和强大的,各种编程语言都支持的文本处理工具。
在操作系统中,最常用的文本检索就是egrep,在指定了正则表达式和需要检索的文件后,egrep会尝试用正则表达式类匹配每个文件的每一行,并显示能够匹配的行。如:
%egrep '^word' word.txt
会查询word.txt文件中以word开始的行。
正则语句的元字符
完整的正则表达式由元字符(metacharacters)和普通文本字符(normal text characters)组成,简单来说,元字符就是语法功能,普通文本字符就是文本,元字符对文本的操作控制是以一个单元(unit)为基础的,元字符会对整个单元起作用。
下面主要讲一下最核心的元字符的种类。注意如下内容只是初步介绍,在后续内容中会逐渐补充。
脱字符号 ⌈^⌋:表示匹配行的起始,如 ⌈^cat⌋ 表示行起始后的第一个字符是 ⌈c⌋,然后是 ⌈a⌋,然后是⌈t⌋。
美元符号 ⌈$⌋:表示匹配行的结束,如⌈t$⌋表示行的结束之前的最后一个字符是⌈t⌋。
以上两种符号的特殊之处在于他们匹配的是位置,而不是具体的文本。
字符组 (character classes)
字符组[……]可以理解为一个“或运算”,字符组中的字符有且仅有一个字符可以匹配,如⌈ca[rtw]⌋能匹配且仅能匹配⌈car⌋或⌈cat⌋,⌈caw⌋。
字符组元字符(character-class metacharacter)是仅在字符组中适用的元字符,连字符‘-’就是典型的例子,在字符组中表示范围,如⌈[0-9]⌋、⌈[a-z]⌋,需要注意的是:‘-’在字符组内部中时才是元字符,而且前后必须是合法范围,如⌈[-az]⌋表示⌈-⌋或⌈a⌋或⌈z⌋,一定要注意逻辑上是否存在谬误。‘-’在字符组外部就是一个普通文本字符,不可混淆。同样道理,问好和点号通常被当做元字符处理,但在字符组中则不是(例如⌈[0-9A-Z_!.?]⌋真正的特殊字符只有0-9与A-Z的连字符,其余字符都是或运算的一个选项)
排除型字符组:⌈[^……]⌋表示一个“或非”操作,如⌈[^0-9]⌋表示匹配一个非0到9的字符,需要注意的是:排除型字符组一定会有一个匹配,匹配不到是不可接受的(可以理解为匹配一个排除了字符组中所有字符的字符)。⌈^⌋只有在字符组内部才是元字符,在外部是行锚点(line anchor)。比如[^x]并不是只有当这个位置不是x时才能匹配,而是说匹配一个不等于x的字符。具体体现在这个正则表达式是无法匹配空行(也就是什么字符都没有的行)的。
点号⌈.⌋
点号可以匹配任意字符,这里包括空格,需要强调空格符也是一个字符。点号的匹配范围过于宽泛,可能会获得大量希望之外的结果,需要多加限制。但有时需要在表达式中使用一个匹配任何字符的占位符,用点号就很方便。
多选结构(alternation)
⌈|⌋,或运算,跟括号配合,可以起到比字符组更灵活的筛选功能,字符组只能对字符或运算,⌈|⌋可以筛选子表达式。比如gr(a|e)y,gr[ae]y,grey|gray这三个式子其实代表着相同含义,都匹配grey或者gray。需要注意gr[a|e]y与此不同,它额外匹配gr|y。同时gr(a|e)y中的括号是必须的,括号相当于规定了|的界限。在一个包含多选结构的表达式中使用脱字符和美元符中,比如需要提取email相关信息的行,使用正则表达式如
^(From|Subject|Date):
而不可以用
^From|Subject|Date:
(后者相当于匹配^From或Subject或Date:)
此时括号限制|的作用范围的作用就体现出来了。多选结构跟字符组比较,字符组只能匹配单个字符,而多选项可以匹配任意长度文本,比如上面可以改为gr(a|e|kk)y。但并没有字符组的排除功能。
忽略大小写的方式
比如想要匹配的不区分大小写的FROM代表的4个字母,一种方法是使用[Ff][Rr][Oo][Mm],但缺点就是很不方便。第二种是在使用egrep时,使用命令行参数-i进行忽略大小写的匹配。如:
%egrep -i '^from'
单词分界符
为了避免希望匹配的单词在另一个单词里面,可采用⌈<⌋,⌈>⌋定位单词开始和结束位置。这是一个元字符序列,用来匹配单词分界的位置,可以把它们当做单词版本的^和$。(注意这里的分界是指,起始位置之后是一串英文字母或数字,结束位置之后不能是英文字母或数字)比如:
*&'34 sss!
第一个起始位置是'之后,结束位置是4之后,然后第二个起始位置是两个空格后,结束位置是最后一个s后。
可选项元素-量词(?+*)
⌈?⌋表示其作用的的单元可出现一次或0次,如⌈colou?r⌋表示u可有可无,⌈color⌋,⌈colour⌋均可匹配成功。但semicolon无法匹配,因为无法匹配最后的r。当需要扩大?的范围时,需要加括号。比如4(th)?等价于4th|4。
⌈+⌋表示其作用的的单元至少出现一次,如⌈[0-9]+⌋可匹配所有数字。若一次都没有匹配成功就会报错。
⌈*⌋表示其作用的单元爱出现几次都行,0到任意大都是可以的,也不会报错。
比如需要匹配<HR SIZE=14>这样的html标签,表示一条高度为14像素的水平线。在最后的尖括号之前可以出现任意多个空格,此外在等号两边也容许出现任意多个空格,HR和SIZE之间必须至少有一个空格。就可以写作
<HR +SIZE *= *14 *>(注意在+和*前是有空格的)。
不限制SIZE具体数值时可以写作
<HR +SIZE *= *[0-9]+ *>。
当SIZE属性也是可选的时,可以写作
<HR( +SIZE *= *[0-9]+)? *>
有时用{min,max}这样的区间量词限定其前面的单元出现的次数(闭区间)。比如
[a-zA-Z]{1,5}
限定了英文字母出现的次数在1~5次间
{mid}中如果只有一个数mid,则限定必须出现mid次
{min,}表示至少出现min次
注意没有{,max}这样的用法
括号和反向应用
在前面只介绍了括号的两种用途:限制多选项|的范围,将若干字符组合为一个单元受?+*的量词作用。现在讲一个新的功能,使括号能记住它们包含的子表达式所匹配的内容。
当用小括号指定子表达式后,匹配这个子表达式的文本(分组捕获)会自动拥有一个组号,从左到右,分组的左括号为标志,出现的第一个分组为 1,其后 2,3,4 类推。
后向引用则可以利用分组捕获的文本来匹配比如连续重复的两个单词
\<([A-Za-z]+) +\1\>
其中的 \1 在表示引用前面([A-Za-z]+) 括号中捕获的文本,比如 go go 则会被匹配。分组捕获通常用在想要获取匹配字符串中部分子串时使用。
但是在使用捕获时一定要注意,过多的捕获会影响正则表达式的效率。同时显然是无法做到匹配第一个单词在行末尾,第二个单词在下一行开头的情况。更灵活的工具后面会介绍。
转义字符
网友评论