有一类规格说明频繁出现在Unix 工具和脚本语言中:正则表达式(简写为"regexp")。我们在此将其视为描述文本模型的一种生命性微型语言;它经常内嵌在其它微型语言中。Regexp无处不在,几乎没有人认为它是微型语言,但他们取代了那种执行不同(而且不可互用)搜索功能的大堆代码。
以上介绍跳过了POSIX扩展、反向引用和国际化特性等细节。更详细的介绍请参见 Mastering Regular Expressions(精通正则表达式)[Fried1]。
正则表达式描述了匹配或不匹配字符串的模式。最简单的正则表达式工具是 grep(1),它用在每行输入和输出之间匹配指定regexp。正则表达式符号归纳在表8.1。
regexp符号还存在一些小变种:
1. Glob 表达式:这是早期Unix shell用于文件名匹配的有限通配符约定集。只有三个通配符:“*”匹配任何序列的字符(与其它变种中的.*类似);“?”匹配任何一个字符(与其它变种中的.类似);[...]和其它变种中一样,匹配一类字符。有些shell(csh,bash,zsh)后来也允许用{}表示。这样,x{a,b}c 与xac或xbc匹配,但不匹配xc。有些shell进一步朝扩展正则表达式的方向扩展了glob。
2. 基本正则表达式:最初的grep(1)实用程序使用这些符号来从文件中提取与指定regexp匹配的文本行。行编辑器ed(1)和字符流编辑器sed(1)也使用这些符号。Unix老手认为这些符号是基本的regexp,或“最普通”口味的regexp;首先接触更现代工具的人往往采用以下描述的扩展形式。
3. 扩展的正则表达式:这是从grep公用程序扩展出的egrep(1),实用程序使用这些符号来从文件中提取与指定regexp匹配的文本行。Lex和Emacs编辑器中的正则表达式非常接近egrep风格。
4. Perl正则表达式:这是Perl和Python的regexp功能所接受的符号。它们比egrep风格的符号更强大。
看了这些启发性例子之后,表8.2总结了正则表达式的标准通配符。注意:该表不包括glob变种,因此“全部”代表的只是基本、扩展/Emacs和Perl/Python这三种变种。
支持regexp的新语言所采用的设计实践已经固定使用Perl/Python的变种。它比其它变种更透明,主要是因为非字母数字字符之前的反斜杠\始终表示该字符的字面值,因此对如何引用regexp的元素产生的混淆较少一些。
正则表达式是一个微型语言能够多么简练的极端例子。简单的正则表达式表达了识别行为,不这样做的话,这些行为必须用成百行繁琐易错的代码来实现。
正则表达式的历史
正则表达式的“祖先”可以一直上溯至对人类神经系统如何工作的早期研究。Warren McCulloch 和 Walter Pitts 这两位神经生理学家研究出一种数学方式来描述这些神经网络。
1956 年, 一位叫 Stephen Kleene 的数学家在 McCulloch 和 Pitts 早期工作的基础上,发表了一篇标题为“神经网事件的表示法”的论文,引入了正则表达式的概念。正则表达式就是用来描述他称为“正则集的代数”的表达式,因此采用“正则表达式”这个术语。
随后,发现可以将这一工作应用于使用 Ken Thompson 的计算搜索算法的一些早期研究,Ken Thompson 是 Unix 的主要发明人。正则表达式的第一个实用应用程序就是 Unix 中的 qed 编辑器。
如他们所说,剩下的就是众所周知的历史了。从那时起直至现在正则表达式都是基于文本的编辑器和搜索工具中的一个重要部分。
grep (global search regular expression(RE) and print out the line,全面搜索正则表达式并把行打印出来)是一种强大的文本搜索工具,它能使用正则表达式搜索文本,并把匹配的行打印出来。Unix的grep家族包 括grep、egrep和fgrep。egrep和fgrep的命令只跟grep有很小不同。egrep是grep的扩展,支持更多的re元字符, fgrep就是fixed grep或fast grep,它们把所有的字母都看作单词,也就是说,正则表达式中的元字符表示回其自身的字面意义,不再特殊。linux使用GNU版本的grep。它功能 更强,可以通过-G、-E、-F命令行选项来使用egrep和fgrep的功能。
grep的工作方式是这样的,它在一个或多个文件中搜索字符串模板。如果模板包括空格,则必须被引用,模板后的所有字符串被看作文件名。搜索的结果被送到屏幕,不影响原文件内容。
grep可用于shell脚本,因为grep通过返回一个状态值来说明搜索的状态,如果模板搜索成功,则返回0,如果搜索不成功,则返回1,如果搜索的文件不存在,则返回2。我们利用这些返回值就可进行一些自动化的文本处理工作。
- ^
锚定行的开始 如:'^grep'匹配所有以grep开头的行。
- $
锚定行的结束 如:'grep$'匹配所有以grep结尾的行。
- .
匹配一个非换行符的字符 如:'gr.p'匹配gr后接一个任意字符,然后是p。
- *
匹配零个或多个先前字符 如:'*grep'匹配所有一个或多个空格后紧跟grep的行。 .*一起用代表任意字符。
- []
匹配一个指定范围内的字符,如'[Gg]rep'匹配Grep和grep。
- [^]
匹配一个不在指定范围内的字符,如:'[^A-FH-Z]rep'匹配不包含A-R和T-Z的一个字母开头,紧跟rep的行。
- \(..\)
标记匹配字符,如'\(love\)',love被标记为1。
- \<
锚定单词的开始,如:'\
- \>
锚定单词的结束,如'grep\>'匹配包含以grep结尾的单词的行。
- x\{m\}
重复字符x,m次,如:'0\{5\}'匹配包含5个o的行。
- x\{m,\}
重复字符x,至少m次,如:'o\{5,\}'匹配至少有5个o的行。
- x\{m,n\}
重复字符x,至少m次,不多于n次,如:'o\{5,10\}'匹配5--10个o的行。
- \w
匹配文字和数字字符,也就是[A-Za-z0-9],如:'G\w*p'匹配以G后跟零个或多个文字或数字字符,然后是p。
- \W
\w的反置形式,匹配一个或多个非单词字符,如点号句号等。
- \b
单词锁定符,如: '\bgrep\b'只匹配grep。
- -?
同时显示匹配行上下的?行,如:grep -2 pattern filename同时显示匹配行的上下2行。
- -b,--byte-offset
打印匹配行前面打印该行所在的块号码。
- -c,--count
只打印匹配的行数,不显示匹配的内容。
- -f File,--file=File
从文件中提取模板。空文件中包含0个模板,所以什么都不匹配。
- -h,--no-filename
当搜索多个文件时,不显示匹配文件名前缀。
- -i,--ignore-case
忽略大小写差别。
- -q,--quiet
取消显示,只返回退出状态。0则表示找到了匹配的行。
- -l,--files-with-matches
打印匹配模板的文件清单。
- -L,--files-without-match
打印不匹配模板的文件清单。
- -n,--line-number
在匹配的行前面打印行号。
- -s,--silent
不显示关于不存在或者无法读取文件的错误信息。
- -v,--revert-match
反检索,只显示不匹配的行。
- -w,--word-regexp
如果被\<和\>引用,就把表达式做为一个单词搜索。
- -V,--version
显示软件版本信息。
要用好grep这个工具,其实就是要写好正则表达式,所以这里不对grep的所有功能进行实例讲解,只列几个例子,讲解一个正则表达式的写法。
- $ ls -l | grep '^a'
通过管道过滤ls -l输出的内容,只显示以a开头的行。
- $ grep 'test' d*
显示所有以d开头的文件中包含test的行。
- $ grep 'test' aa bb cc
显示在aa,bb,cc文件中匹配test的行。
- $ grep '[a-z]\{5\}' aa
显示所有包含每个字符串至少有5个连续小写字符的字符串的行。
- $ grep 'w\(es\)t.*\1' aa
如果west被匹配,则es就被存储到内存中,并标记为1,然后搜索任意个字符(.*),这些字符后面紧跟着另外一个es(\1),找到就显示该行。如果用egrep或grep -E,就不用"\"号进行转义,直接写成'w(es)t.*\1'就可以了。
阅读(1291) | 评论(0) | 转发(0) |