中国源码网首页 > 在线文章 > 正则学习 >
学无止尽: 精通正则表达式

作者:ll_efort      发布时间:2008-07-29 08:49:21      来源:网络

你觉得自己的正则表达式足够精通了么,当遇上实际问题的时候,能很熟练的解决问题么,即使解决问题了,解决问题的方法足够漂亮么,效率够高么。jRC源码网
我这段时间正在学习和内容抽取方面的东东,当然少不了大量和正则表达式打交道了,刚开始的时候还以为自己以前用过不少正则表达式,用起来应该能得心应手,可是面对一些奇怪而特殊的要求时,写起来就特别的别扭,才发现自己那三脚猫功夫根本不够用了。正好在公司的书架上翻到了 这本 《精通正则表达式 第三版》,由Friedl,J.E.F. 著作,余晟译著。信手翻阅之后,立即深深的被它吸引了。这本书绝对不是那种只能让你入门,让你只懂得怎么用却不懂得原理的书。这本书详细的讲解了正则表达式实现的原理,NFA引擎和DFA引擎的区别,更难能可贵的是,书中全面而详细的讲解了正则表达式的各种知识点,读过之余,绝对让你重新认识正则表达式!jRC源码网
嘿嘿,现在这书被我“据为己有”啦,每天课余有时间都要好好翻阅...jRC源码网

嗯啊,要是觉得自己的正则表达式很牛了,麻烦再看看一下的这堆代码,对你肯定会有帮助的,这些代码可是出自余晟老师之手的哦,jRC源码网

IMG_LINK_RE = re.compile('(?is)(?:<img[^>]*?\ssrc\s*=\s*['"]?)([^'"\s>]+)(?:[^>]*>)')jRC源码网
RELATIVE_LINK_RE = re.compile('(?i)(?:href|src)s*=s*['"]?(?!http:)(?!mailto:)(?!javascript)(?:/[^'"s]+|[^'"s./]+/[^'"s]*|[^'"s./]+?.[^'"s./]+)(?=['"s])')jRC源码网
RELATIVE_SUB_RE = re.compile('(?i)(?<=['"=s])(?=[0-9a-z.])')jRC源码网
TITLE_RE = re.compile('(?i)(?<=<title>)[^<]+')jRC源码网
EMAIL_RE = re.compile('(?i)[a-z0-9][-a-z0-9._]*@(?:w[-w]+.)+[a-z]{2,4}')jRC源码网
HTML_ENTITY_RE = re.compile('(&#(\d{5});)')jRC源码网
LINE_RE = re.compile('(?is)<tr.*?</tr>')jRC源码网
SPACE_RE = re.compile(r'(?is)(<br/s*/?>|</?p>|(?<=n)s*)')jRC源码网
CONTENT_SUB_RE = re.compile(r"(?i)(<[^>]+>|&[0-9a-z]{3,5};|<(?:no)?script.*?</(?:no)?script>)")jRC源码网
HYPERLINK_RE = re.compile('(?i)(?<=href)(s*=s*['"]?)(?!mailto:)([^'"s]+)')jRC源码网
EMPTY_ELEMENT_RE = re.compile('(?is)<([^s>]+)[^>]*>[srn]*(</?s*br>)*</\1s*>')jRC源码网
LINEFEED_RE = re.compile(r'(?is)(?<=n)s*')jRC源码网
RELATIVE_RE = re.compile('(?<=['"s])(?=/)')jRC源码网
ENCODE_RE = re.compile('(?i)(?<=content=["']text/html; charset=)[^'"]+')jRC源码网
SCRIPT_RE = re.compile('(?is)<(?:no)?script.*?</(?:no)?script>')jRC源码网

嘿嘿,还是上点读书笔记吧..jRC源码网
简单的正则入门就不写啦,需要的话看我以前的笔记吧,正则表达式基础 http://hi.baidu.com/ismayday/blog/item/7f5b86942741d11dd21b708f.htmljRC源码网

环视(look-around)jRC源码网

环视是很有意思的功能,它用来检查两端的字符,但不会把检查时匹配的字符加入匹配的最终结果。jRC源码网
例如,表达式『bJeffb』只能匹配“Jeff”这个单词,如果我们需要精确匹配“Jeffrey”这个单词中的“Jeff”,就可以使用环视『Jeff(?=rey)』,后面的『(?=rey)』表示,如果匹配成功,“Jeff”之后必须出现“rey”。有的读者可能会说,那我直接使用『(Jeff)rey』,先找出来,再提取分组,不是一样吗?请注意,环视的对象又可以是正则表达式,『Jeff(?=(rey|erson))』就可以找到“Jeffrey”或“Jefferson”中的“Jeff”,这种灵活性是前一种做法无法提供的,而且,『(Jeff)rey』使用括号来捕获文本,效率有所降低。jRC源码网
按照环视的方向不同,可以分为顺序环视(lookahead,表示从左向右检查)和逆序环视(lookbehind,从右向左检查);按照环视成立的条件不同,又可分为肯定环视(positive lookaround,只有在环视对象能匹配时才成功)和否定环视(negative lookaround,只有在环视对象无法匹配时才成功)。两者组合起来,就得到四种环视:jRC源码网

* 肯定顺序环视jRC源码网
* 肯定逆序环视jRC源码网
* 否定顺序环视jRC源码网
* 否定逆序环视jRC源码网

所使用的标记也很好识别,『(?=Regex)』表示肯定顺序环视,『(?!Regex)』表示否定顺序环视,『(?<=Regex)』表示肯定逆序环视,『(?<!Regex)』表示否定逆序环视。jRC源码网

在日常的HTML解析中,如果我们需要精确获得“src=...”中的资源地址(这里假定“src=...”的格式统一规范,等号两端没有空格,也没有引号),可以在表达式之前添加『(?<=).*?(?=< /B>)』来精确匹配“...”之中的内容。在这两个例子中,当然也可以使用匹配-括号提取的办法,但使用环视的效率更高,也更切合程序的本意。jRC源码网
环视还可以多个连用,我曾遇到过这样的情形:有站点siteA.com,需要在Apache的配置文件中设定重定向规则,以一个正则表达式匹配除sub1, sub23之外的所有子域名(注意,是匹配所有子域名),首先我想到的是jRC源码网
『^[^.]*(?<!--b(1|23)).siteA.com$』jRC源码网
但这行不通,因为多数系统都不容许在逆序环视中使用变长表达式(只有.NET容许),所以必须连用多个逆序环视jRC源码网
『^[^.]*(?<!--ub1)(?<!^sub23).siteA.com$』jRC源码网

jRC源码网
固化分组(Atomic-grouping)jRC源码网

回溯(back-tracking)是匹配过程中常见的现象,如果用『.*ab』来匹配“123456ab”,『.*』首先会匹配整个字符串,之后轮到『ab』,『.*』需要“依次释放”之前匹配的两个字符,供『ab』匹配,整个表达式才能匹配成功。这样“依次释放”的过程,就叫做回溯。但有时回溯完全是徒劳的,例如我们用『w+:』来匹配“Subject”。因为字符串中不存在冒号,匹配肯定会失败,但引擎仍然必须依次回溯,最终得出失败的结果,而我们知道,『w』“释放”的字符,:肯定无法匹配。此时可以使用固化分组,将『w+』匹配的内容“固定”下来,禁止回溯:

评论】【加入收藏夹】【打印】【关闭
相关新闻
最新文章
频道推荐
本站热门