学习过程中,可以到这篇文章推荐的网站进行正则表达式的练习。点击这里
正则表达式是一种思想——各种工具以各种方式来实现它。如果理解了正则表达式的基本思想,掌握就是易如反掌的事情。
弄明白“如何使用”及“这么做的原因”。真正懂得正则表达式后,就能够在任何时间、任何地点,应用关于它的知识。
在进行详细的和复杂的文本处理时,保持精确性是很重要的。差一、二个空格字符,可能导致截然不同的结果。
举一个例子,处理某些E-mail,如果希望把邮箱文件中的消息作为列表整理出来。可以把整个文件导入文本编辑器,手工删除所有信息,只留下邮件头中的几行,作为列表的内容。尽管文件不是很大,连接速度也不算慢,这样的任务还是很耗费时间而且很乏味。
利用正则表达式可以很简单处理这个任务!用一个简单的命令显示每封邮件的(From:)和(Subject:)字段。为了告诉egrep需要提取哪些行,使用以下正则表达式就可完成:
^(From|Sbuject):
一旦掌握了正则表达式,你就会知道到它简直是工具中的无价之宝。全面掌握正则表达式是很有用的。
正则表达式(RegularExpression)是强大、便捷、高效的文本处理工具。正则表达式本身,再加上如同一门袖珍编程语言的通用模式表示法,赋予使用者描述和分析文本的能九。配合特定工具提供的额外支持,正则表达式能够添加、删除、分离、叠加、插入和修整各种类型的文本和数据。正则表达式的使用难度只相当于文本编辑器的搜索命令,但功能却与完整的文本处理语言二样强大。
宿主语言(例如java、VB.NET等)提供了外围的处理支持,但是真正的能力来自正则表达式。为了驾驭这种语言,满足自己的需求,必须知道如何构建正则表达式,才能识别符合要求的文本,同时忽略不需要的文本。然后,就可以把表达式和语言支持的构建方式结合起来,真正处理这些文本(加入合适的高亮标记代码,删除文本,修改文本,等等)。
我们都知道,repont.txt是一个文件名,“.txt”能够用来选择多个文件。其中有些字符具有特殊的意义。星号表示“任意文本”,问号表示“任意单个字符”。所以,“.txt”以能够匹配字符的*符号开头,以普通文字.txt结尾,所以,它的意思是:选择以任意文本开头,以.txt结尾的所有文件。
完整的正则表达式由两种字符构成。特殊字符(special characters),例如文件名例子中的*,称为“元字符”(metacharacters),其他为“文字”(literal),或者是普通文本字符(normal text characters)。正则表达式与文件名模式(filename pattern)的区别就在于,正则表达式的元字符提供了更强大的描述能力。文件名模式只为有限的需求提供了有限的元字符,住是正则表达式“语言”为高级应用提供了丰富而且描述力极强的元字符。
完整的正则表达式由小的构建模块单元组成。每个单独的构建模块都很简单,不过因为它们能够以无穷多种方式组合,将它们结合起来实现特殊目标必须依靠经验。就像真正懂演奏和仅仅会弹奏之间差别一样、工解正则表达式和真正理解正则表达式并不是一回事。
文本检索是正则表达式最简单的应用之一,许多文本编辑器和文字处理软件都提供了正则表达式检索的功能。最简单的就是egrep。在指定了正则表达式和需要检索的文件之后,egrep会尝试用正则表达式来匹配每个文件的每一行,并显示能够匹配的行。
% egrep '^(From|Subject):' mailbox-file
刚开始说的E-mail的例子,真正用来从E-mail文件中提取结果的命令如上所示。egrep把第一个命令行参数视为一个正则表达式,剩下的参数作为待搜检索的文件名。注意,图中的单引号并不是正则表达式的一部分,而是根据commandshell需要添加的。使用egrep时,通常用单引号来包围正则表达式。如果要在支持对正则表达式提供了完整支持的程序设计语言中使用正则表达式,重要的问题是知道特殊宇符有哪些,具体文本是什么,针对什么对象(什么表达式,什么工具软件),以及按何种顺序解释这此字符(这些在后面再说)。
看完上面语句,我们基本可以明白,这个正则表达式的各个部分都是什么意思。在这里,^ 和 | 都是正则表达式的元字符,它们与其他字符结合起来,实现我们期望的功能。
如果一个正则表达式不包括任何egrp支持的元字符,它就成了一个简单的“纯文本”检索。
例如,在一个文件中检素cat,会显示任何包含cat这3个连续字母的行。例如,它包括所有出现了vacation的行(单词当中含有连续的cat三个字母)。
行的开始 ^ 和结束 $
最容易理解的元字符就是脱字符号 ^ 和美元符号 $ 了, ^代表一行的开始,$代表一行的结束。
脱字符号和美元符号的特别之处就在于,它们匹配的是一个位置,而不是具体的文本。
比如:
^car
匹配的是以字母c开头,后面接字母a,跟着字母r开始的行。
^$
匹配的就是一个空行(没有任何字符,包括空白字符)。
如果我们需要搜索的是单词grey,同时又不确定它是否写作gray。就可以使用正则表达式结构体[ ],它容许使用者列出在某处期望匹配的字符,通常称为字符组。e匹配字符e,a匹配字符a,而正则表达式[ea]能匹配 a 或者 e ,
字符组的内容是在同一个位置能够匹配的若干字符,所以它的意思是“或”,a 或者 e
在一个字符组中可以列举任意多个字符。例如 [123456] 匹配1到6中的任意一个数字。
在字符组内部,字符组元字符 -(连字符)表示一个范围:[1-6] 与 [123456] 是完全一样的。[0-9] 和[a-z] 是常用的匹配数字和小写学母的简便方式。多重范围也是容许的,例如[0123456789abcdefABCDEFJ] 可以写作[0-9a-fA-F] (或者也可以写作[A-Fa-f0-9],顺序无所谓)。
我们还可以随心所欲地把字符范围与普通文本结合起来:
'[0-9A-Z_!.?]'
上试就能够匹配一个数字、大写字母、下画线、惊叹号、点号,或者是问号。
**注意,只有在字符组内部,连字符才是元字符否则它就只能匹配普通的连字符号。其实,即使在字符组内部,它也不一定就是元字符。如果连字符出现在字符组的开头,它表示的就只是一个普通字符,而不是一个范围。同样的道理,问号和点号通常被当作元字符处理,但在字符组中则不是如此,说明白一点就是,上式里面,真正的特殊字符就只有那两个连字符。
用[^ ]
取代[ ]
这个字符组就会匹配任何未列出的字符。例如,[^1-6]
匹配除了1到6以外的任何字符。这个字符组中开头的 ^ 表示“排除”,所以这里列出的不是希望匹配的字符,而是不希望匹配的字符。
这里的 ^ 和前面说的表示行首的脱字符 ^ 是一样的字符。但意义截然不同。
比如连字符 - ,在字符组内部(而且不是第一个字符的情况下),连字符才能表示范围。同样,在字符组外部,^表示一个行锚点(line anchor),但是在字符组内部(而且必须是紧接在字符组的第一个方括号之后),它就是一个排除型的元字符。
来看另一个例子,我们需要在一堆英文单词中搜索出一些特殊的单词:在这些单词中,字母q后面的单词不是u,用正则表达式来表示,就是q[^u]
,完整的命令如下:
% egrep 'q[^u] word.list
排除型字符组,就是表示匹配一个未列出的字符。
元字符.
(通常称为点号或者point)是用来匹配任意字符的字符组的简便写法。如果需要在表达式中使用一个“匹配任何字符”的占位符(placeholder),用点号就很方便。例如,如果我们需要搜索03/19/76、03-19-76或者03.19.76,不怕麻烦的话用一个明确容许/ . -
的字符组来构建正则表达式,例如03[-./]19[-./]76
也可以简单地尝试03.19.76
在这个表达式中,点号并不是元字符,因为它们在字符组内部(记住,在字符组里面和外面,元字符的定义和意义是不一样的)。这里的连字符同样也不是元字符,因为它们都紧接在[
或者[^
之后。如果连字符不在字符组的开头,例如[.-/]
,就是用来表示范围的。
在03.19.76
中,点号是元字符,它能够匹配任意字符。所以这个正则表达式也能够匹配下面的字符串:‘19 203319 7639’。所以,'03[-./]19[-./]76
,更加精确,但是更难读,也更难写。03.19.76
更容易理解,但是不够细致。我们应该选择哪一个呢?这取决于你对需要检索的文本的了解,以及你需要达到的准确程度。一个重要但常见的向题是,写正侧表达式时,我们需要在对欲检索文本的了解程度与检索精确性之间求得平衡。要想正确使用正则表达式,清楚地了解目标文本是非常重要的。
|
是一个非常简捷的元字符,它的意思是“或”。
gr[ea]y
,它还可以写作grey|gray
或者是gr(e|a)y
。后者用括号来划定多选结构的范围。
gr[ea]y
,与gr(e|a)y
的例子可能会让人觉得多选结构与字符组没太大的区别,但是一定不要混淆这两个概念。一个字符组只能匹配目标文本中的单个字符,而每个多选结构自身都可能是完整的正则表达式,都可以匹配任意长度的文本。
在提取Email文件时,匹配以“From:.,‘Subject:.或者“Date:.开头的文本行,可以如下写:
% egrep '^(From|Subject|Date):' mailbox
但是上面的示例,没有区分大小写。一种办法是用'[Ff][Rr][Oo][Mm]'
,取代From,这样就能匹配任何形式的“from”,但缺点之一就是很不方便。幸好,我们有一种办法告诉egrp在比较时忽略大小写,也就是进行不区分大小写的匹配,这样就能忽略大小写字母的差异。该功能并不是正则表达式语言的一部分,却是许多工具软件提供的有用的相关特性。egrep的命令行参数“-i”表示进行忽略大小写的匹配。把-i写在正则表达式之前:% egrep -i '^(From|Subject|Date):' mailbox
使用正则表达式时经常会遇到的一个问题,期望匹配的“单词”包含在另一个单词之中。基些版本的erep对单词识别提供了有限的支持:也就是单词分界符(单词开头和结束的位置)的匹配。
如果你的egrep支持“元字符序列(metasequences)\<
和\>
就可以使用它们来匹配单词分界的位置。分别用来匹配单词的开头和结束位置。就像作为行锚点的脱字符和美元符一样。表达式\
的意思是匹配单词的开头位置,然后是car这3个字母,然后是单词的结束位置。更直接点说就是“匹配cat这个单词”。如果你愿意,也可以用\
car\>
来匹配以car开头和结束的单词。
请注意,<
和>
本身并不是元字符,只有当它们与斜线结合起来的时候,整个序列才具有特殊意义。但是,要留意并不是所有版本的egrep都支持单词分界符。
几点需要注意的事项:
1、在字符组内部,元字符的定义规则(及它们的意义)是不一样的。例如,在字符组外部,点号是元字符,但是在内部则不是。相反,连字符只有在字符组内部(这是普遍情况)才是元字符,否则就不是。脱字符在字符组外部表示一个意思,在字符组内部紧接着[
时表示另一个意思,其他情况下又表示别的意思。
2、不要混滑多选项和字符组。字符组[abc]
和多选项(a|b|c)
固然表示同一个意思,但是这个例子中的相似性并不能推广开来。无论列出的字符有多少,字符组只能匹配一个字符。相反,多选项可以匹配任意长度的文本,每个多选项可能匹配的文本都是独立的,例如\<(1,000,000|mil1ion|thousand thou)\>
。不过,多选项没有像字符组那样的排除功能。
3、排除型字符组是表示所有未列出字符的字符组的简便方法。因此,[^x]
的意思并不是“只有当这个位置不是×时才能匹配”,而是说“匹配一个不等于x的字符”。其中的差别很细微,但很重要。
4、-i
参数规定在匹配时不区分大小写。