《正则表达式经典实例(第2版)》——第 1 章 正则表达式简介 1.1正则表达式的定义

第 1 章 正则表达式简介

在你打开这本书的时候,很可能已经热切地期望,要在代码中插入本书中找到的那些包含诸多括号和问号的古怪字符串了。如果你已经准备好要“即查即用”,我们非常欢迎,第4~9章中会列出并讲解了各种实用的正则表达式。

但是如果阅读本书的前几章,你将为未来节省大量的时间。例如,本章会向读者介绍许多工具—其中一些工具是本书作者之一的Jan所开发的,这些工具可以帮你事先测试和调试正则表达式,而不用等到把它们塞到代码中之后再处理,那时候查找错误就非常困难了。而且开始这几章还会展示使用正则表达式的不同特性和选项,帮助你轻松应对遇到的问题,并帮助你理解正则表达式,从而提高它们的性能,以及学习不同语言—甚至是你最喜欢的编程语言的不同版本之间—在处理正则表达式的时候出现的细微差别。

因此,我们在这些背景知识上花费了大量的精力,相信读者在开始动手之前会阅读这些内容,或是在使用正则表达式时遇到挫折,而想要巩固你的理解的时候,会回头来阅读它们。

1.1 正则表达式的定义

在本书的上下文中,正则表达式(regular expression)是一种可以在许多现代应用程序和编程语言中使用的特殊形式的文本模式。它们可以用来验证输入是否符合给定的文本模式;在一大段文本中查找匹配该模式的文本;用其他文本来替换匹配该模式的文本或者重新组织匹配文本的片段;把一块文本切分成一系列更小的文本,当然如果使用不当也可能搬起石头砸自己的脚。本书会帮助你确切理解正在做的事情,从而避免可能会造成的灾难性后果。

学会了使用正则表达式的技巧,就可以简化许多编程和文本处理的任务,并且让许多没有正则表达式则根本无法实现的任务成为可能。从一个文档中提取所有的电子邮件地址,至少需要几十行,甚至是几百行过程式代码—这些代码编写起来费事,维护起来也麻烦。但是,如果采用了合适的正则表达式,如在实例4.1中所给的那样,就只需要很少的几行甚至只要一行代码就可以了。

术语“正则表达式”的历史

术语“正则表达式”来源于数学与计算机科学理论,它用来反映被称为“正则性”的数学表达式特点。这样一个表达式可以通过一个确定性有限自动机(DFA)用软件来实现。一个DFA是一个不使用回溯的有限状态机。

最早版本的grep工具所使用的文本模式是数学意义上的正则表达式。尽管名字看起来是一样的,然而如今流行的Perl风格的正则表达式已经完全不是数学意义上的正则表达式了。它们是采用非确定性的有限自动机(NFA)来实现的。你稍后就会学到和回溯有关的所有内容。关于这条说明,实干的程序员需要记住的所有内容就是:象牙塔里的一些计算机科学家,很不喜欢自己精心定义的术语被套用到现实世界中更为有用的技术。
但是,如果你试图用一个正则表达式来做太多的事情,或者是在根本不适合的情形中非要使用正则表达式,就会明白为什么会存在如下的说法1FF:

有些人每遇到一个问题,就会想“我知道怎么做,用正则表达式就可以了。”于是他们就有两个(而不是一个)问题需要解决了。
这些人所遇到的新问题指的就是他们并不会去阅读用户手册,也就是现在你手里的这本书。所以请继续读下去。正则表达式是一个强大的工具。如果你的工作涉及在计算机上编辑或是提取文本,牢固地掌握正则表达式就会为你少度过很多个不眠之夜。

1.1.1 众多正则表达式流派
上一小节的标题确实表述得不那么确切,我们并没有定义正则表达式到底是什么。我们也不可能给出定义。对于哪些文本模式是正则表达式,而哪些不是,并不存在正式的标准来给出严格精确的定义。可以想象得到,每种编程语言的设计人员,以及每款文本处理程序的开发人员,对于正则表达式应该是什么样子,都会有自己不同的想法。因此,我们就不得不面对这样一大堆不同的正则表达式流派。

幸运的是,绝大多数设计人员与开发人员都比较懒惰。如果可以照搬别人已经做好的工作,为什么非要自己创建一些全新的东西呢?正因为此,所有现代的正则表达式流派,包含本书要讨论的这些流派,其历史都可以追溯到Perl编程语言。我们把这些流派都称作Perl风格的正则表达式。它们的正则表达式语法都非常相似,而且在大多数情况下是相互兼容的,但也不是完全如此。

作家也都很懒。在本书中,我们通常会使用regex或regexp来指代一个单个的正则表达式,而使用regexes来指代其复数形式2。

正则流派并不是和编程语言一一对应的。脚本语言倾向于拥有它们自己内置正则表达式流派,其他语言则会依赖于函数库来提供正则表达式支持。有些函数库是对多种语言可用的,而某些特定的语言则会选用一些不同的函数库。

本章要讲解的内容只与正则表达式的流派有关,因此会彻底忽略任何与编程有关的考虑事项。从第3章开始,我们会给出一些代码片段,所以你可以先跳到第3章的第一节,以了解你将会使用哪些流派。但是现在请先忽略所有与编程相关的内容。下面一小节中列出的工具将会通过“动手学习”的方式,让你更方便地探索正则表达式的语法。

1.1.2 本书涵盖的正则流派
在本书中,我们选择了如今最为流行的正则流派。这些都是Perl风格的正则流派。有些流派会比其他流派多一些特性。但是如果两种流派拥有同一个特性的话,那么它们通常都会使用相同的语法。当然也会有例外,当我们遇到这些烦人的不一致情况时,在书中会加以提示。

所有这些正则流派都属于目前正在活跃开发中的编程语言和函数库的一部分。下面的流派列表会告诉你本书所用到的是哪些版本。在本书后面,如果所讲解的正则表达式在所有流派中效果都一样,那么我们在提到该流派时就不会区分其版本。绝大多数场合都是这种情况。除了会影响到一些边界情况的bug修复之外,正则表达式流派一般都不会改变,唯一例外是添加新的特性,原来认为是错误的语法,现在会被赋予新的含义。

.NET

微软公司的.NET框架通过System.Text.RegularExpressions包,提供了一个功能全面的Perl风格正则流派。本书涵盖了.NET 1.0~4.0版。而严格来讲,.NET正则流派只存在两个版本:1.0和2.0版。在 .NET 1.1、3.0和3.5版本中,并没有对正则类进行任何修改。.NET 4.0的正则类有一些新方法,但没有改变正则语法。
任何.NET编程语言,包括C#、VB.NET、Delphi for .NET,甚至包括COBOL.NET,都可以完整使用.NET正则流派。如果一个使用.NET开发的程序提供了正则表达式支持,即使它号称使用的是“Perl正则表达式”,你也可以非常确定它使用的就是.NET正则流派。很长时间里,令人大跌眼镜的一个例外则是Visual Studio(VS)自身。直到Visual Studio 2010,VS集成开发环境(IDE)中依然使用的是它从一开始就在用的一个老版本的正则流派,而这个实现根本就不是Perl风格的。本书写作时正处于测试版的Visual Studio 113FF,最终也在IDE中使用了.NET正则流派。

Java

Java 4是通过java.util.regex包提供内置正则表达式支持的第一个Java版本。这个包很快就超越了各种第三方Java正则库。除了它是标准的和内置的之外,它还支持了功能完整的Perl风格正则流派,其卓越的性能甚至可以媲美使用C语言编写的正则程序。本书会讲解Java 4、5、6和Java 7中的java.util.regex包。如果你在过去几年中用过Java语言开发的软件的话,那么其支持的正则表达式很可能是Java流派。

JavaScript

在本书中,我们使用JavaScript这个术语指代在ECMA-262标准的第3版和第5版中定义的正则表达式流派。这个标准定义了ECMAScript编程语言,而这个语言更广为人知的是它在不同网页浏览器中的JavaScript与JScript实现。Internet Explorer(自5.5版)、Firefox、Opera与Safari都实现了ECMA-262的第3版或第5版。尽管在正则表达式功能方面,JavaScript 3与JavaScript 5的区别很小。然而,所有的浏览器都拥有各种与标准不同的边界情形bug。我们会在必要的地方指出这些问题。

如果一个网站允许使用正则表达式进行查找或者过滤,并且不用等待网站服务器的响应,那么它使用的就是JavaScript正则流派,这是唯一的跨浏览器客户端正则流派。即使是微软的VBScript与Adobe公司的ActionScript 3使用的也是它,不过ActionScript 3添加了一些额外特性。

XRegExp

XRegExp是Steven Levithan开发的开源JavaScript库,可以从http://xregexp.com下载它。XRegExp扩展了JavaScript的正则表达式语法,并且消除了一些Web浏览器间的不一致。本书中的实例在用到标准JavaScript不支持的正则表达式特性时,会附加使用XRegExp的解决方案。如果一个解决方案的正则流派中列出了XRegExp,就意味着这个解决方案在JavaScript代码中使用XRegExp库时可以正常工作,而不使用XRegExp的标准JavaScript代码无法正常工作。如果一个解决方案的正则流派中列出了JavaScript,则意味着无论JavaScript代码中是否使用XRegExp,都可以正常工作。

本书涵盖了XRegExp 2.0版。所有实例都假设读者使用的是包含全部XRegExp Unicode特性的xregexp-all.js。

PCRE

PCRE是由Philip Hazel开发的“与Perl兼容的正则表达式” (Perl-Compatible Regular Expressions)的C语言函数库。这个开源代码库可以从http://www.pcre.org下载。本书涉及的PCRE版本包括第4版到第8版。

虽然PCRE号称是与Perl兼容的,而且与本书中的其他流派相比,它也是与Perl兼容性最好的,但是实际上它也只能称为是“Perl风格”的正则流派。有些特性,比如Unicode支持,会与Perl稍微有些不同,并且不能像在Perl中所允许的那样,把Perl代码混合到你的正则表达式之中。

因为它采用了开源许可,并且拥有稳定可靠的实现,所以PCRE被应用到了许多编程语言和程序中。它被内置到PHP中,并且被包装到了许多个Delphi的组件中。如果一个应用程序号称它支持“与Perl兼容”的正则表达式,而却没有具体列出它实际使用的正则流派,那么就很可能是PCRE。

Perl

Perl对于正则表达式的内置支持是正则表达式今天得以流行的主要原因。本书会涉及Perl 5.6、5.8、5.10、5.12和5.14版。上述每个版本都为Perl正则表达式语法添加了新的特性。当本书中标明某正则式在特定Perl版本中工作时,那该正则式可在该特定版本及本书所涉及的所有更新版本中正常工作。

许多应用程序和正则库都号称它们使用的是Perl或者与Perl兼容的正则表达式,而事实上它们仅仅是使用了Perl风格的正则表达式。它们使用与Perl相似的正则语法,但是所支持正则特性集并不重叠。最有可能的情形是,它们使用的是这一特性集中的正则表达式流派之一,而这些流派都是属于Perl风格的。

Python

Python通过它的re模块来支持正则表达式。本书会讲到Python 2.4至3.2版。Python 2.4、2.5、2.6和2.7版间的区别非常小。Python 3.0增强了Python处理正则表达式中Unicode的能力。Python 3.1和3.2则没有与正则相关的改进。

Ruby

与Perl语言类似,Ruby的正则表达式支持是Ruby语言自身的一部分。本书会涉及Ruby 1.8和Ruby 1.9。Ruby 1.8的默认编译会使用由Ruby源代码直接提供的正则表达式流派。而Ruby 1.9的默认编译则会使用Oniguruma正则表达式库。Ruby 1.8也可以编译使用Oniguruma,而Ruby 1.9也可以编译使用旧版本的Ruby正则流派。在本书中,我们会把原生Ruby流派称为Ruby 1.8,而把Oniguruma流派称为Ruby 1.9。

如果想测试一下你的站点使用的是哪个Ruby正则流派,可以尝试用一下正则表达式‹a++›。Ruby 1.8会说这个正则表达式是非法的,因为它并不支持占有量词(possessive quantifier),而它在Ruby 1.9中则会匹配一个或者多个字符a组成的字符串。

Oniguruma库设计为与Ruby 1.8向后兼容,它只是在其上添加了新的功能,而不会破坏已有的正则表达式。该实现甚至原样保留了大家认为应该修改的功能,例如:它依然使用‹ (?m) ›表示“点号匹配换行符”,即使其他正则表达式流派使用的都是‹ (?s) ›。

时间: 2024-10-20 11:44:52

《正则表达式经典实例(第2版)》——第 1 章 正则表达式简介 1.1正则表达式的定义的相关文章

《正则表达式经典实例(第2版)》——1.3 正则表达式工具

1.3 正则表达式工具 除非已经拥有了相当长的使用正则表达式编程的经验,否则建议你先在一个工具中试验一下正则表达式,而不是直接在源代码中使用它们.本章和第2章中提供的正则表达式示例都是原始正则表达式,其中并不包含编程语言(即使是Unlx shell)所必需的额外的转义符号.因此可以直接把这些正则表达式输入到一个应用程序的查找框中. 第3章讲解如何把正则表达式整合到源代码中.把一个字面的(literal)正则表达式作为一个字符串引用,会让它更加难懂,因为字符串的转义规则会与正则表达式的转义规则混杂

《正则表达式经典实例(第2版)》——2.19 在替代文本中添加字面文本

2.19 在替代文本中添加字面文本 问题描述查找并把任何正则表达式匹配从字面上替换为这8个字符:$%*$11. 解决方案 $%\*$$1\1 替代文本流派:.NET.JavaScript \$%\\*\$1\\1 替代文本流派:Java $%\*\$1\\1 替代文本流派:PHP \$%\*\$1\\1 替代文本流派:Perl $%\*$1\\1 替代文本流派:Python.Ruby 讨论在替代文本中转义字符的时机和方式这个实例显示了各种替代文本流派中使用的不同转义规则.在替代文本中,你可能会需

《正则表达式经典实例(第2版)》——导读

** 前言 **正则表达式在过去十多年间越来越普及.如今,所有常用的编程语言都会包含一个强大的正则表达式函数库,甚至在语言本身就内嵌了对于正则表达式的支持.许多开发人员都会利用这些正则表达式的功能,在应用程序中为用户提供使用正则表达式对其数据进行查找或者过滤的能力.正则表达式已经无处不在.随着正则表达式的广泛采用,出现了许多相关的著作.其中大多数都很好地讲解了正则表达式的语法,并且还会提供一些例子以及参考实现.然而,我们还没有看到有任何一本书能够针对处理计算机和不同因特网应用上的文本所遇到的各种

《正则表达式经典实例(第2版)》——2.15 避免失控重复

2.15 避免失控重复 问题描述使用一个正则表达式来匹配一个完整的HTML文件,并检查其中的html.head.title和body标签是否进行了正确嵌套.当HTML文件中不拥有正确标签的时候,该正则表达式必须能够高效地宣布匹配失败. 解决方案 <html>(?>.*?<head>)(?>.*?<title>)(?>.*?</title>) (?>.*?</head>)(?>.*?<body[^>]*&g

《正则表达式经典实例(第2版)》——第 2 章 正则表达式的基本技能 2.1匹配字面文本

第 2 章 正则表达式的基本技能 本章要讲解的问题并不是老板或客户会要求你解决的那一类现实世界中的问题.相反,它们是在你创建和编辑正则表达式来解决现实世界问题的过程中会遇到的技术性问题.例如,第一个实例会解释如何使用一个正则表达式来匹配字面文本(literal text),以及如何处理正则表达式中有特殊含义的字符.这个问题本身并不是很重要,因为如果你只是要查找字面文本,并不需要使用正则表达式.但是,当创建正则表达式的时候,你可能会需要按照字面来匹配某些文本,那么你就需要知道哪些字符需要转义.实例

《正则表达式经典实例(第2版)》——2.2 匹配不可打印字符

2.2 匹配不可打印字符 问题描述 匹配一个包含下列ASCII控制字符的字符串:振铃符(bell).转义符(escape).换页符(form feed).换行符(line feed).回车符(carriage return).水平制表符(horizontal tab)和垂直制表符(vertical tab).这些字符的十六进制ASCII编码分别是07.1B.0C.0A.0D.09.0B. 下面演示了转义序列的用法,以及如何以十六进制编码代表字符. 解决方案 \a\e\f\n\r\t\v 正则选项

《正则表达式经典实例(第2版)》——2.14 消除不必要的回溯

2.14 消除不必要的回溯 问题描述上一个实例解释了贪心和懒惰量词之间的区别,以及它们是如何进行回溯的.在有些情形下,这种回溯是不必要的. ‹bd+b›使用了一个贪心量词,而‹bd+?b›使用的是懒惰量词.它们都会匹配相同的内容:一个整数.如果给它们同样的目标文本,它们都会找到完全一样的匹配.在这里所做的任何回溯都是不必要的.试着改写这个正则表达式,明确地消除所有回溯,使正则表达式更加高效. 解决方案 \b\d++\b 正则选项:无 正则流派:Java.PCRE.Perl 5.10.Ruby 1

《正则表达式经典实例(第2版)》——2.9 分组和捕获匹配中的子串

2.9 分组和捕获匹配中的子串 问题描述 改进匹配Mary.Jane或Sue的正则表达式,使之只能匹配完整单词.使用分组来实现这个功能,整个正则表达式只需要一对单词分界符,而不是给每个选择分支都使用一对分界符. 创建一个正则表达式,使之匹配yyyy-mm-dd格式的任意日期,并且分别捕获年.月和日.目标是在处理匹配的代码中可以更容易处理这些分别捕获的值.你可以假设目标文本中的所有日期都是合法的.正则表达式不必要考虑去掉像9999-99-99这样的非法数据,因为它们根本不可能出现在目标文本中. 解

《正则表达式经典实例(第2版)》——2.3 匹配多个字符之一

2.3 匹配多个字符之一 问题描述 创建一个正则表达式来匹配calendar的所有常见的错误拼写形式,使你能够在一份文档中找到这个单词而无需依赖作者的拼写能力.在每个元音位置都允许使用a或者e.创建另外一个正则表达式来匹配一个单个的十六进制字符.再创建一个正则表达式来匹配不属于十六进制字符的单个字符. 本节中的这个问题用于解释一个重要的.经常使用的正则结构-字符组(character class). 解决方案 错误拼写的calendar c[ae]l[ae]nd[ae]r 正则选项:无 正则流派