作为编程语言的正则表达式
04-13Ctrl+D 收藏本站
Regular Expressions as a Language
如果没有正则表达式相关经验,读者可能无法理解上个例子中正则表达式「^(From|Subject):」的意义,但是这个表达式并没有什么神奇之处。其实魔术本身也不神奇,只是缺乏训练的普通观众不明白魔术师掌握的那些技巧而已。如果你也懂得如何在手中藏一张牌,那么,熟练之后,你也可以“变魔术”。外语也是这样——一旦掌握了一门外语,你就不会觉得它像天书了。
以文件名做类比
The Filename Analogy
选择这本书的读者,大概对“正则表达式” 多少有点认识。即便没有,也应该熟悉其中的基本概念。
我们都知道,report.txt是一个文件名,但是,如果你用过Unix或者DOS/Windows的话,就会知道“*.txt”能够用来选择多个文件。在此类文件名(称为“文件群组”file globs或者“通配符”wildcards)中,有些字符具有特殊的意义。星号表示“任意文本”,问号表示“任意单个字符”。所以,文件群组“*.txt”以能够匹配字符的「*」符号开头,以普通文字「.txt」结尾,所以,它的意思是:选择以任意文本开头,以.txt结尾的所有文件。
大多数系统都提供了少量的附加特殊字符(additional special characters),但是,总的来说,这些文件名模式(filename patterns)的表达能力还很有限。不过,因为这类问题的领域很狭窄——只涉及文件名,所以这算不上缺陷。
不过,处理普通的文本就没有这么简单了。散文、诗、程序代码、报表、HTML、表格、单词表……到你想得出的任何文本。如果某种特殊的需求足够专业,例如“选择文件”,我们可以发明一些特殊的办法和工具来解决问题。不过,近年来,一种“通用的模式语言”(generalized pattern language)已经发展起来,它功能强大,描述能力也很强,可以用来解决各种问题。不同的程序以不同的方式来实现和使用这种语言,但是综合来说,这种功能强大的模式语言和模式本身被称为“正则表达式”(regular expression)。
以语言做类比
The Language Analogy
完整的正则表达式由两种字符构成。特殊字符(special characters,例如文件名例子中的*)称为“元字符”(metacharacters),其他为“文字”(literal),或者是普通文本字符(normal text characters)。正则表达式与文件名模式(filename pattern)的区别就在于,正则表达式的元字符提供了更强大的描述能力。文件名模式只为有限的需求提供了有限的元字符,但是正则表达式“语言”为高级应用提供了丰富而且描述力极强的元字符。
为了便于理解,我们可以把正则表达式想象为普通的语言,普通字符对应普通语言中的单词,而元字符对应语法。根据语言的规则,按照语法把单词组合起来,就会得到能传达思想的文本。在 E-mail 的例子中,我用正则表达式来寻找以‘From:’或者‘Subject:’开头的行。下画线标注的就是特殊字符,稍后我们将解释它们的含义。
就像学习任何一门外语一样,第一眼看上去,正则表达式很不好理解。这也是那些对它只有粗浅了解或者根本不了解的人觉得正则表达式很神奇的原因。但是,就像学日语的人很快就能理解正規表現は簡簡だよ!(注 2)一样,读者很快也能够彻底明白下面这个正则表达式的含义:
s!<emphasis>([0-9]+(\.[0-9]+){3})</emphasis>!<inet>$1</inet>!
这个例子取自一个 Perl 脚本,我的编辑器用它来修改手稿。手稿的作者错误地使用了<emphasis>这个tag来标注IP地址(类似209.204.146.22这样由数字和点号构成的字符串)。其中的奥妙就在于使用Perl的文本替换命令,使用:
「<emphasis>([0-9]+(\.[0-9]+){3})</emphasis>」
把IP地址两端的tag替换为<inet>,而不改动其他的<emphasis>标签。在后面的章节中,读者会了解这个表达式的构造细节,然后就能按照自己的需求,在自己的应用程序或者开发语言中应用这些技巧。
本书的目的
你或许不需要重复把<emphasis>替换为<inet>的工作,不过很可能需要解决“把这些文字替换为那些文字”的问题。本书的目的不是提供具体问题的解决办法,而是教会读者利用正则表达式来思考,解决遇到的各种问题。