设为首页 友情链接
在线留言 发表文章
加入收藏 广告联系

刺猬首页

| 专案技术 | 网络技术 | 图形图象 | 网络编程 | 网页设计 | 操作系统 | 服务器 | 技术白皮书 | 在线实验室 | 刺猬论坛 |
小说专版  | 数据库 | 设计赏析 | 存储频道 | 网络安全 | 私服架设 |  Solaris | 网站评估 | PC维护技巧 | 下载中心 | 博 客 |
专   题: | Linux | java | cisco | 防病毒 | 刀片 | SOA | iscsi | ASP.NET | SQL | Oracle |
您现在的位置: 刺猬宫 >> 网络编程 >> PHP >> 教程正文 用户登录 新用户注册
专 题 栏 目
最 新 热 门
最 新 推 荐
相 关 文 章
详细介绍动态网页PHP预定…
学习PHP动态网页技术收集…
详细阐述PHP环境下如何将…
实例详细讲解PHP中使用的…
实例学习PHP如何实现在线…
新手入门:学习掌握动态…
PHP初学:实例详细学习P…
新手通过实例学习动态网…
实例学习PHP程序对用户身…
实例详细讲解PHP连接调用…
  详细学习动态网页制作PHP技术的正则表达式           
详细学习动态网页制作PHP技术的正则表达式
 

  正则表达式难于书写、难于阅读、难于维护,经常错误匹配意料不到的文本或者错过了有效的文本,这些问题都是由正则表达式的表现和能力引起的。每个元字符(metacharacter)的能力和细微差别组合在一起,使得代码不借助于智力技巧就无法解释。

     许多包含一定特性的工具使阅读和编写正则表达式变得容易了,但是它们又很不符合习惯。对于很多程序员来说,书写正则表达式就是一种魔法艺术。他们坚持自己所知道的特征并持有绝对乐观的态度。如果你愿意采用本文所探讨的五个习惯,你将可以让你设计的正则表达式经受的住反复试验。

    本文将使用Perl、PHP和Python语言作为代码示例,但是本文的建议几乎适用于任何替换表达式(regex)的执行。

    一、使用空格和注释

    对于大部分程序员来说,在一个正则表达式环境里使用空格和缩进排列都不成问题,如果他们没有这么做一定会被同行甚至外行人士看笑话。几乎每个人都知道把代码挤在一行会难于阅读、书写和维护。对于正则表达式又有什么不同呢?

    大部分替换表达式工具都具有扩展的空格特性,这允许程序员把他们的正则表达式扩展为多行,并在每一行结尾加上注释。为什么只有少部分程序员利用这个特性呢?Perl 6的正则表达式默认就是扩展空格的模式。不要再让语言替你默认扩展空格了,自己主动利用吧。

    记住扩展空格的窍门之一就是让正则表达式引擎忽略扩展空格。这样如果你需要匹配空格,你就不得不明确说明。

    在Perl语言里面,在正则表达式的结尾加上x,这样“m/foo|bar/”变为如下形式:

m/
  foo
  |
  bar
 /x

    在PHP语言里面,在正则表达式的结尾加上x,这样“"/foo|bar/"”变为如下形式:

"/
  foo
  |
  bar
 /x"

    在Python语言里面,传递模式修饰参数“re.VERBOSE”得到编译函数如下:

pattern = r'''
 foo
 |
 bar
'''
regex = re.compile(pattern, re.VERBOSE)

    处理更加复杂的正则表达式时,空格和注释就更能体现出其重要性。假设下面的正则表达式用于匹配美国的电话号码:

\(?\d{3}\)? ?\d{3}[-.]\d{4}

     这个正则表达式匹配电话号码如“(314)555-4000”的形式,你认为这个正则表达式是否匹配“314-555-4000”或者“555- 4000”呢?答案是两种都不匹配。写上这么一行代码隐蔽了缺点和设计结果本身,电话区号是需要的,但是正则表达式在区号和前缀之间缺少一个分隔符号的说明。

    把这一行代码分成几行并加上注释将把缺点暴露无疑,修改起来显然更容易一些。

    在Perl语言里面应该是如下形式:

/  
    \(?     # 可选圆括号
      \d{3} # 必须的电话区号
    \)?     # 可选圆括号
    [-\s.]? # 分隔符号可以是破折号、空格或者句点
      \d{3} # 三位数前缀
    [-.]    # 另一个分隔符号
      \d{4} # 四位数电话号码
/x

    改写过的正则表达式现在在电话区号后有一个可选择的分隔符号,这样它应该是匹配“314-555-4000”的,然而电话区号还是必须的。另一个程序员如果需要把电话区号变为可选项则可以迅速看出它现在不是可选的,一个小小的改动就可以解决这个问题。

    二、书写测试

    一共有三个层次的测试,每一层为你的代码加上一层可靠性。首先,你需要认真想想你需要匹配什么代码以及你是否能够处理错误匹配。其次,你需要利用数据实例来测试正则表达式。最后,你需要正式通过一个测试小组的测试。

     决定匹配什么其实就是在匹配错误结果和错过正确结果之间寻求一个平衡点。如果你的正则表达式过于严格,它将会错过一些正确匹配;如果它过于宽松,它将会产生一个错误匹配。一旦某个正则表达式发放到实际代码当中,你可能不会两者都注意到。考虑一下上面电话号码的例子,它将会匹配“800-555-4000  = -5355”。错误的匹配其实很难发现,所以提前规划做好测试是很重要的。

    还是使用电话号码的例子,如果你在Web表单里面确认一个电话号码,你可能只要满足于任何格式的十位数字。但是,如果你想从大量文本里面分离电话号码,你可能需要很认证的排除不符合要求的错误匹配。

    在考虑你想匹配的数据的时候,写下一些案例情况。针对案例情况写下一些代码来测试你的正则表达式。任何复杂的正则表达式都最好写个小程序测试一下,可以采用下面的具体形式。

    在Perl语言里面:

#!/usr/bin/perl

my @tests = ( "314-555-4000",
              "800-555-4400",
       "(314)555-4000",
              "314.555.4000",
              "555-4000",
              "aasdklfjklas",
              "1234-123-12345"          
            );

foreach my $test (@tests) {
    if ( $test =~ m/
                   \(?     # 可选圆括号
                     \d{3} # 必须的电话区号
                   \)?     # 可选圆括号
                   [-\s.]? # 分隔符号可以是破折号、空格或者句点
                     \d{3} # 三位数前缀
                   [-\s.]  # 另一个分隔符号
                     \d{4} # 四位数电话号码
                   /x ) {
        print "Matched on $test\n";
     }
     else {
        print "Failed match on $test\n";
     }
}

    在PHP语言里面:

<?php
$tests = array( "314-555-4000",
           "800-555-4400",
           "(314)555-4000",
           "314.555.4000",
           "555-4000",
           "aasdklfjklas",
           "1234-123-12345"
          );

$regex = "/
            \(?     # 可选圆括号
              \d{3} # 必须的电话区号
            \)?     # 可选圆括号
            [-\s.]? # 分隔符号可以是破折号、空格或者句点
              \d{3} # 三位数前缀
            [-\s.]  # 另一个分隔符号
              \d{4} # 四位数电话号码
           /x";

foreach ($tests as $test) {
    if (preg_match($regex, $test)) { 
        echo "Matched on $test<br />;";
    }
    else {
        echo "Failed match on $test<br />;";
     }
}
?>;

        在Python语言里面:

import re

tests = ["314-555-4000",
         "800-555-4400",
         "(314)555-4000",
         "314.555.4000",
         "555-4000",
         "aasdklfjklas",
         "1234-123-12345"        
        ]

pattern = r'''
\(?                 # 可选圆括号
              \d{3} # 必须的电话区号
            \)?     # 可选圆括号
            [-\s.]? # 分隔符号可以是破折号、空格或者句点
              \d{3} # 三位数前缀
            [-\s.]  # 另一个分隔符号
              \d{4} # 四位数电话号码
           '''

regex = re.compile( pattern, re.VERBOSE )

for test in tests:
    if regex.match(test):
        print "Matched on", test, "\n"
    else:
        print "Failed match on", test, "\n"

    运行测试代码将会发现另一个问题:它匹配“1234-123-12345”。

     理论上,你需要整合整个程序所有的测试到一个测试小组里面。即使你现在还没有测试小组,你的正则表达式测试也会是一个小组的良好基础,现在正是开始创建的好机会。即使现在还不是创建的合适时间,你也应该在每次修改以后运行测试一下正则表达式。这里花费一小段时间将会减少你很多麻烦事。

    三、为交替操作分组

    交替操作符号(|)的优先级很低,这意味着它经常交替超过程序员所设计的那样。比如,从文本里面抽取Email地址的正则表达式可能如下:

^CC:|To:(.*)

    上面的尝试是不正确的,但是这个bug往往不被注意。上面代码的意图是找到“CC:”或者“To:”开始的文本,然后在这一行的后面部分提取Email地址。

     不幸的是,如果某一行中间出现“To:”,那么这个正则表达式将捕获不到任何以“CC:”开始的一行,而是抽取几个随机的文本。坦白的说,正则表达式匹配 “CC:”开始的一行,但是什么都捕获不到;或者匹配任何包含“To:”的一行,但是把这行的剩余文本都捕获了。通常情况下,这个正则表达式会捕获大量 Email地址,所有没有人会注意这个bug。

    如果要符合实际意图,那么你应该加入括号说明清楚,正则表达式如下:

(^CC:)|(To:(.*))

    如果真正意图是捕获以“CC:”或者“To:”开始的文本行的剩余部分,那么正确的正则表达式如下:

^(CC:|To:)(.*)

    这是一个普遍的不完全匹配的bug,如果你养成为交替操作分组的习惯,你就会避免这个错误。

    四、使用宽松数量词

    很多程序员避免使用宽松数量词比如“*?”、“+?”和“??”,即使它们会使这个表达式易于书写和理解。

     宽松数量词可以尽可能少的匹配文本,这样有助于完全匹配的成功。如果你写了“foo(.*?)bar”,那么数量词将在第一次遇到“bar”时就停止匹配,而不是在最后一次。如果你希望从“foo###bar+++bar”中捕获

[1] [2] 下一页

频道声明:本频道的文章除部分特别声明禁止转载的专稿外,可以自由转载.但请务必注明出出处和原始作者 文章版权归本频道与文章作者所有.对于被频道转载文章的个人和网站,我们表示深深的谢意。

原始作者:佚名 录入时间:2007-6-29 10:58:28
信息来源:不详 投稿信箱:itqoo@126.com
教程录入:itqoo    责任编辑:itqoo 
  • 上一个教程:

  • 下一个教程:
  • 【字体: 】【发表评论】【加入收藏】【告诉好友】【打印此文】【关闭窗口
      网友评论:(只显示最新10条。评论内容只代表网友观点,与本站立场无关!)
    - 关于我们 - 合作伙伴 - 友情链接 - 广告刊登 - 投稿热线 - 在线留言版权声明联系方式 -
    IT公社版权所有 粤ICP备05127012号
    Copyrigh@2005-2006 itqoo.com.Inc All Rights Reserved  推荐分辨率 1024*768
    联系站长:E-Mail:itqoo@126.com     MSN:urchincc@hotmail.com    QQ:点击这里给我发消息
    特别感谢:亿太网络提供空间支持