正则指引

正则指引

余晟 

ISBN 978-7-121-16551-1

20125月出版

定价:58.00

16

336

本书针对作者在开发中遇到的实际问题,以及其他开发人员咨询的问题,总结出一套使用正则表达式解题的办法,并通过具体的例子指导读者拆解、分析问题。全书分为三大部分:第一部分主要讲解正则表达式的基础知识,涵盖了常见正则表达式中的各种功能和结构;第二部分主要讲解关于正则表达式的更深入的知识,详细探讨了编码问题、匹配原理、解题思路;第三部分将之前介绍的各种知识落实到6种常用语言.NETJavaJavaScriptPHPPythonRuby中,不但详细介绍了语言中正则表达式的用法,更点明了版本之间的细微差异,既可以作为专门学习的教材,也可以作为有用的参考手册。

本书适合经常需要进行文本处理(比如日志分析或网络运维)的技术人员、熟悉常用开发语言的程序员,以及已经对正则表达式有一定了解的读者阅读。

作者简介

余晟,毕业于计算机系,副修中文,非正统型技术爱好者。曾任抓虾网、银杏泰克主力程序员,盛大创新院高级研究员,现任华南某电商公司技术部总监。坚信计算机可以无限延伸人的能力,前提是人必须理解计算机的逻辑,所以对任何技术都不应该浅尝辄止,仅仅满足于“会用”。

已经翻译出版《精通正则表达式(第3版)》和《技术领导之路》,审阅《软件架构师应该知道的97件事》和《REST in Practice中文版》。

读者评价

掌握正则表达式应该是IT工程师的一项标准技能,遗憾的是,过去,不少人多多少少忽视了这一点,所以在工作中总要应对正则表达式带来的「麻烦」。
我相信只有掌握并熟练运用它才有可能成为一个高效率的工程师。期待每个人手边都有一本正则表达式的参考书,当然,最好就是你现在看到的这本。

就在写这句话的几分钟前,我又从这本书中学到了一个有用的技巧。

冯大辉

配合恰当的案例,大量的反问,使读者自问、思考,扣人心扉,比较有代入感,加上配图,很容易让读者全面认识正则表达式。在原理讲解的章节,对比两种理论模型的区别,顺其自然地引入NFA引擎的关键要素——回溯,使读者从匹配原理上了解回溯,写出高效严谨的正则表达式。

陈驰 http://www.cnxct.com/

本书由浅入深的讲述了正则表达式,在正则的应用和调优方面有非常详细的介绍,特别在正则表达式处理中文方面有独到的阐述,对于需要经常处理中文的国内技术人员来说,无疑是非常值得拥有的一本手册。

贺钧 http://www.freefcw.com

正则表达式是程序员的必备知识。如果您还没有使用过这个强大的工具,或者学习正则表达式总不得要领,确实可以读读《正则指引》。

何源 http://www.cppblog.com/lambdacpp

这是一本通俗版的“精通正则表达式”。高手很难挑出毛病,一般程序员会受益匪浅,普通用户一步步读下去也能登堂入室。

张东亮 http://iregex.org

余晟在之前翻译业内名著的基础上,结合中文环境和自己的丰富经验,再接再厉推出自己的原创著作,实在是我等码农的一大幸事。

陈钢 http://gossipcoder.com

引子:关于正则表达式……

正则表达式这个名字看起来总有点古怪,概念似乎也不简单,甚至需要用一整本书来讲解;可是,它到底是什么呢?

身为技术人员,我相信你总会与字符串打交道,相应地,各种语言也都提供了与字符串有关的函数。不妨先看看下面几个问题,字符串函数是如何解决的(下面的代码使用Python语言,它很直观,正文里有基础的介绍。现在,你只需要知道def是定义函数的关键词即可)。

1. 判断字符ch是否数字字符

def isDigit(ch) :

    return ch == "0" or ch == "1" …… or ch == "9"

 

2. 判断字符串str是否电话号码(为简单起见,现在只考虑固定电话号码,也就是长度在7~8位之间的数字字符串,且第一位不为0

def isPhoneNum (str) :

    if len(str) >= 7 and len(str) <= 8 and str[0] != "0" :

        for ch in str :

            if not isDigit(ch) :

                return false

        return true

    return false

 

任务本身并没有增加太多,但是程序复杂了很多倍;如果你不这样看,那么,来个更复杂的。

3. 找出一段文本中所有的电话号码

最直观的办法是,在字符串中的每个位置截取7~8个字符,调用之前的isPhoneNum()。这么做看起来没问题,只是效率太低。

当然,很容易就可以做点改进,只在当前字符为数字字符的情况下调用isPhoneNum()。这样效率倒是改进了,但是还有问题没有解决:要求找到的是长度大于等于7个字符,小于等于8个字符的数字字符串,而不是子字符串”——也就是说,假如数字字符串是64240000,需要将它找出来;如果数字字符串是13800138000,则需要忽略它,以及其中的任何子串(比如1380013800138000)。

所以,用isPhoneNum()找出字符串之后,还需要保证它之前的字符不是数字字符,之后的字符也不是数字字符。看起来很简单,但为了避免越界错误,又需要判断:如果当前字符是整段文本的第一个字符,则不需要判断之前的字符,因为它不存在;同样,如果找出的字符串在整段文本的末尾,则不需要判断之后的字符,因为它同样不存在……

到现在为止,即便只是找到最简单的固定电话号码,程序也非常复杂,难以维护。如果要查找的是形式更多变的文本,比如带区号的电话号码(021-64240000或者03718888888)、手机号码(13800138000或者+8613800138000或者013800138000),程序更是不可想象,更不用说文件路径名、URL地址、电子邮件地址了!然而,日常开发中我们又确实经常需要面对这类任务,有什么更好的办法呢?

正则表达式就是解决这类问题的万灵药。虽然许多人有点看不起它,觉得不入流,科班教材里也不会花太多篇幅来介绍它,但它确实是解决问题的利器——之前提到的三个例子,用正则表达式都可以轻松解决。

1. 判断字符ch是否数字字符

def isDigit(ch) :

    return re.search(ch, "[0-9]") != None

 

看起来很复杂,其实并不复杂:这里真正要关心的就是正则表达式[0-9],它表示09之间的任意字符,很形象吧?re.search()是正则表达式运算函数,它判断ch能否由正则表达式[0-9]匹配,可以则返回一个结果,否则返回None(这些细节正文中会讲到)。

2. 判断字符串str是否电话号码

def isPhoneNum(str) :

    return re.search(str, "[1-9][0-9]{6,7}") != None

 

这个正则表达式最开始是[1-9],表示第一个字符必须是1~9之间的数字字符;之后是[0-9]{6,7},表示长度在67之间,由0~9之间的数字字符组成的字符串(两部分加起来,整个字符串的长度在78之间)。要解决的问题复杂了,正则表达式仍然直观形象。

3. 找出一段文本中所有的固定电话号码

def findNumStr(str) :

    return re.findall(str, '(?<![0-9])[1-9][0-9]{6,7}(?![0-9])')

 

这个正则表达式之前多出了(?<![0-9]),表示之前不能是[0-9]”;之后多出了(?![0-9]),表示之后不能是[0-9]”。虽然稍微复杂点,但意思明确,而且不难理解。re.findall()的意思也很明显:找到所有这样的字符串。

可以想象,循着这种思路,更复杂的电话号码、手机号码等任务都不难解决。更重要的是,之前需要许多行语句才能完成的任务,现在基本上只需要一个正则表达式,一条语句就可以完成。正因为如此,不少人虽然认为正则表达式不够花哨、漂亮,却不得不承认它是一种匕首应用”——匕首,没有十八般兵刃那么大方,关键时候却不可或缺,所以值得花时间练练。同样,正则表达式虽然不能用来显摆,但总有派得上用场的地方,花时间练练绝不是坏事。即便你的工作不是纯粹的文本处理(比如日志分析),也总会有用到正则表达式的地方(比如查找和修改源代码),所以我希望,这本书能陪伴你练出一身正则表达式的好功夫,在关键场合能亮出趁手的工具。

最后,为了尊重传统教科书的习惯,附上正则表达式的科班史

正则表达式发源于与计算机密切相关的两个领域:计算理论和形式语言。20世纪40年代,两位神经生理学家Warren McCullochWalter Pitts研究出一种数学方式来描述神经网络的办法,它们把神经系统中的神经元描述成小而简单的自动控制单元。1956年,数学家Stephen Cole Kleene在他们研究的基础上,发表了一篇名为神经网事件的表示法的论文,在其中,他采用了一些称之为正则集合(regular set的数学符号来描述神经网络模型。

之后,UNIX的主要发明人Ken Thompson将这个符号系统引入了文本编辑器QED(意思是在文本中搜索某种模式),正则表达式由此也进入了计算机世界。随后Ken Thompson又将正则表达式引入了UNIX下的文本编辑器eded最终演化为大家熟悉的grepgrep得名自ed编辑器中的正则表达式搜索命令g/re/p,其中的re表示正则表达式)。

返璞归真——评《正则指引》

第一次接触正则表达式,是2000年我在西安一家公司使用Perl做网站开发时。之前我在工作中只使用过标准的C语言,Perl这门编程语言的强大表达能力,令我印象极为深刻。Perl的力量,除了语言本身的设计之外,很大程度上来自于它对正则表达式的完美支持。当时我们开发了一个网上商城的应用,允许很多商家在这里开店,可以选择一些不同的样式模板。我很快发现,使用Perl+正则表达式是开发这类应用的利器。我们只花了大约一个月的时间,就完成了网站核心功能的开发。那时候我意识到,使用正则表达式是聪明人写程序的方法(没说我是聪明人,但是我非常希望与那些聪明人为伍),可以极大地提高代码的重用度和执行效率。如果完全不使用正则表达式,代码量会增加数倍甚至十倍。

后来因为一些原因,我告别了Perl。在之后的工作中,我使用过JavaJavaScriptRuby等编程语言。我发现这些语言对于正则表达式的支持,没有一个能够超越PerlJava这种所谓的工业主流编程语言,一直到2002JDK 1.4推出时,才正式把对正则表达式的支持加入到核心类库。因为长期缺乏对正则表达式的原生支持,以及语言本身表达能力欠缺,使用Java来做大量的文本处理,感觉非常笨拙,完全没有使用Perl那种指哪打哪的快感。直到2007年我发现了另一个更好的Perl语言——Ruby,才重新找回了2000Perl带给我的编程快感。

因为我的工作主要是做Web开发,大量的时间花在与HTML/CSS/JavaScript以及关系数据库打交道上。在这里并没有很高深的算法,只有大量繁重的文本处理。难以想象,如果没有正则表达式,我们的开发将会是何等原始。

除了Web开发领域,需要实现大量自动化功能的一些领域,例如运维领域和自动化测试领域,也是正则表达式大显身手的地方。无论使用稍显简陋的sed/awk还是更高级的Perl/Python/Ruby,实现自动化功能,都必须依赖大量的正则表达式。

自从面向对象时髦起来之后,甚至一度出现了面向对象万能论,有人试图用MDA和可执行的UML来解决一切编程问题。但是我一直认为面向对象只解决了软件开发的一小部分问题,而且是宏观方面的问题。正则表达式解决的问题,是面向对象无能为力的一些微观方面的问题。在这里不需要坐而论道的方法论争论,需要的是刺刀见红的肉搏战。这些问题即使使用完全面向对象的方式能够解决,也会是很笨拙的。如果用物理学来比喻,面向对象是广义相对论,而正则表达式则是量子力学

正则表达式已经成为了现代编程语言的基础模块,现在很难找到一种不支持正则表达式的编程语言。除了编程语言外,在很多工具软件,例如文本编辑器(ViEmacsUltraEdit)、Web服务器(ApacheNginx)之中都能找到正则表达式的身影。

余晟老师是我的朋友,我对他印象最为深刻的是他对于技术工作的严谨态度。格物致知是中国传统儒家学派所追求的一种道德修养,也是一种境界。余老师是我的朋友中最接近格物致知这种境界的一位。我虽然从未精通过任何一门技术,但是很喜欢结交余老师这样的朋友。

余老师潜心编著的这本《正则指引》深入浅出,将正则表达式的由来和分支娓娓道来。阅读这本书,我仿佛回到了11年前做Perl程序员时的快乐时光。国内很多程序员的一个通病是好高骛远,像《正则指引》这样一本详细讲解基础知识的书未必会有很好的销路。但是等你做过很多年开发之后,你会发现,对你最有价值的,正是这些基础知识和工具。软件开发的,正是隐藏在这些看起来不起眼的基础知识和工具之中的。

李锟

20111125

 

 

提到正则表达式,许多人很有点不屑一顾:这东西,不登大雅之堂,再说也不是总要用到,何必专门花时间学习?

没错,正则表达式并不总要用到,但到了需要的场合用不上,往往产生一分钱难倒英雄汉的尴尬。经常需要处理文本的程序员自然会知道正则表达式的价值,其他的程序员如果不会正则表达式,即便开发的领域与文本处理没什么关系,也难免躺着中枪的命运——前几天我遇到一个问题,将一行长长的地址拆分成多行,负责这部分的程序员的日常工作只是制作PDF而已,拆分地址是很边缘的功能,但不会正则表达式就无法准确折行(一般需要在标点符号出现的地方折行,而不能只在空白字符处折行,但是不同语言中的标点符号各有不同),结果一筹莫展;相反,如果了解正则表达式,就可以很容易地处理各种语言中的标点字符。

以我的开发经验来看,专门花点时间掌握正则表达式,确实是非常有必要的。目前可以见到的关于正则表达式的书籍和资料有不少,但又各有不足。

在互联网上,流传着一些编程语言的正则文档和《30分钟教会你正则表达式》之类的帖子。这类资料的好处是简单直接,查到了,如果有现成的例子,而且适用于自己的语言,则可以直接拿来用;然而,其坏处也是简单直接,因为缺乏背后原理的讲解,如果找不到现成的例子,或者找不到能在自己所使用语言中行得通的例子(需知道,同样的正则表达式并不能直接套用到不同的语言中),则束手无策。

在正式的出版领域,已经有《精通正则表达式》、《正则表达式必知必会》之类的书籍出版,尤其是前者,堪称关于正则表达式的经典著作,如果想认真学习正则表达式,这类书籍是必须阅读的。但这类书籍也有一个弱点,即都是由英文版本翻译而来的,更多地侧重英文文本的处理,身为中文世界的开发人员,我们经常需要处理中文文本——对于处理英文之外的字符,正则表达式已经提供了足够丰富的功能,但如何用对、用好这些功能,资料却很匮乏。

我经常需要给人讲解正则表达式的相关知识,时常惋惜的是,开发人员为这些问题所困扰;正因为如此,本书的写作动机便是着力弥补现有资料的缺陷。

相对于正则文档和速成教学帖子,本书深入讲解了匹配背后的原理,往往会举一反三,告诉读者,这里为何这样写,如果改成其他形式,会造成什么结构;并且,集中讲解和比较了多种语言中正则表达式用法的异同,方便读者把现成的正则表达式移植到自己的工作环境中。

相对于《精通正则表达式》等正式的书籍,本书辟出专门的内容讲解语言和编码,告诉读者如何设定编码,如何正确处理中文等字符。另外,本书还涵盖了.NETJavaJavaScriptPHPPythonRuby 六种常用语言,对每种语言给出专门章节,不但详细介绍了语言中正则表达式的用法,更点明了版本之间的细微差异,不但可以作为专门学习的教材,还可以成为有用的参考手册。

本书结构

本书可以分为三大部分。

第一部分主要讲解正则表达式的基础知识,覆盖常见正则表达式中的各种功能和结构。看完前面3章,就可以基本弄明白现在流行的各种正则表达式;尤其是如果你之前有一些经验,会觉得阅读起来并不困难。但是我也希望读者不要忽略其他的内容,断言和匹配模式现在已经是正则表达式的标准配备了,而且确实可以派上大用场,所以第4章和第5章的内容,即便不是很熟悉,阅读起来可能有一些麻烦,也不应该忽略。最后的第6章,则厘清了正则表达式在使用中的若干疑惑,了解它们,你就可以相对自由地在正则表达式的世界里行走了。

第二部分主要讲解关于正则表达式的更深入的知识,这一部分用3章的内容,详细探讨了编码问题、匹配原理、解题思路。这部分内容更抽象,需要多花一点时间来阅读和理解,但是它们确实可以帮你在正则表达式的世界里登堂入室,脱离的层面,掌握万变不离其宗的

第三部分的作用是接地气,将之前介绍的各种知识落实到六种常用语言.NETJavaJavaScriptPHPPythonRuby中来。每一章的开头有正则功能列表,其中的功能都对应着前面部分的讲解,这些功能的具体应用实例,以及不同版本之间的差异,则在章节中详细讲解,每一章的最后还给出了常见任务的示例代码,方便日后查询。在最后,第16章简要介绍了正则表达式在Linux下常用工具vigrepawksed中的使用,并通过一个实际的例子将这几种工具串起来,对比说明了它们适合解决的问题。

在本书的最后提供了用作参考的3个附录。

附录A是正则表达式的常用功能在不同语言中的比对,希望能给需要在多种语言中使用正则表达式或者移植正则表达式的读者提供一份有用的参考;附录B给出了若干常见的正则表达式,比如匹配邮政编码、身份证号、手机号、QQ号、电子邮件地址等,希望能成为常见问题的速查手册;附录C列出了常用正则表达式的工具和资源,方便大家调试自己的正则表达式,以及继续深入学习。

本书读者

本书适合以下几类读者。

经常需要进行文本处理(比如日志分析或网络运维)的技术人员。这些读者或许已经熟悉了正则表达式的基本用法,但面对日益复杂化和海量化的数据,阅读本书可以帮助你更准确、更高效地处理文本,提升自己工作的价值。

熟悉常用开发语言的程序员。虽然这些读者不需要专职进行文本处理,但源代码和许多数据其实也是文本,如果不会正则表达式,在偶然遇到处理源代码或文本数据的任务时,往往会产生躺着中枪的无力感。本书第三部分可以帮你迅速找到有关的例子,并落实在自己的编程语言中。当然前两部分也非常有必要,因为它们可以帮你夯实基础。

对正则表达式已经有一定了解的读者。这些读者虽然能用正则表达式解决常见的任务,但未必了解正则表达式的编码问题、匹配原理、解题思路,仔细阅读本书的第二部分,可以深化完善对正则表达式的理解;而第三部分详细比较了使用正则表达式时各种语言,以及同一种语言中各种版本的差异。所有这一切,应该可以让你对正则表达式的掌握更上一层楼。

致谢

一本书的完成,必然离不开众多人的帮忙。

首先需要感谢的是周筠老师和徐定翔、卢鸫翔两位编辑,他们在我写作的最初阶段做了大量细心、耐心的工作,完全可以说,没有他们的这些工作,我就不会有写作这本书的念头,或者坚持写完的动力。

然后要感谢的是电子工业出版社的杨福平副总编和张月萍编辑,没有他们的关照和辛劳工作,这本书的出版定然会遇到更多的困难。

感谢我的朋友霍炬和韩磊,虽然我之前阅读过《精通正则表达式》,但与翻译和写作结缘,他们给了我莫大的帮助,有了这个契机,才有了现在的《正则指引》。尤其值得一提的是霍炬的夫人西乔,精心手绘了这本书的封面,在这里表示诚挚的谢意。

感谢我曾工作过的盛大创新院以及创新院的各位同事(李骏、郝培强、庄表伟、丁宇、许式伟、莫华枫、李道兵、赵劼、樊一鹏、张一宁等),创新院给了大家宽松自由的工作环境,与各位同事的讨论加深了我对正则表达式的理解,也为我提供了许多例子。

感谢张东亮、陆亦斌、孙勇、叶劲峰等各位朋友,愿意拨冗阅读本书的草稿,并提出了大量专业的意见。

感谢何源、陈钢、贺钧、陈驰等读者,试读本书之后提出了大量的宝贵意见,在最后关头打消了我心中的许多忐忑。

在更早之前,我的父母从小就鼓励我研究和了解各种科学原理(玩也要动脑筋),我之所以有兴趣探究正则表达式背后的世界,而不满足于够用/凑合,归源都是受益于这种思维行为习惯。此外,在中小学阶段,我的语文老师罗碧玉、郭志鸿、易玺铭培养了我对于文字的兴趣,在大学阶段,东北师范大学文学院的王确老师给了我这个理科生非常多的帮助和指引。对各位师长,在此一并表示感谢,能遇到你们是我的幸运。

最后还需要感谢许多为这本书做出过贡献的人,你们的名字我可能暂时无法记起,或者无法一一罗列,但我会在心中存留对你们的谢意。

posted @ 2012-05-15 17:05  博文视点(北京)官方博客  阅读(335)  评论(0编辑  收藏  举报