当前位置:  编程技术>其它

[php]正则表达式的五个成功习惯

    来源: 互联网  发布时间:2014-10-13

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

正则表达式难于书写、难于阅读、难于维护,经常错误匹配意料不到的文本或者错过了有效的文本,这些问题都是由正则表达式的表现和能力引起的。每个元字符(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 $testn"; 
     } 
     else { 
        print "Failed match on $testn"; 
     } 


    在PHP语言里面: 


        在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”中捕获“###”,这一点就很重要。一个严格数量词将捕获“###bar++ +”。 
    假设你要从HTML文件里面捕获所有电话号码,你可能会使用我们上文讨论过的电话号码正则表达式的例子。但是,如果你知道所有电话号码都在一个表格的第一列里面,你可以使用宽松数量词写出更简单的正则表达式: 
;;(.+?); 
    很多刚起步的程序员不使用宽松数量词来否定特定种类。他们能写出下面的代码: 
;;([^>;]+); 
    这种情况下它可以正常运行,但是如果你想捕获的文本包含有你分隔的公共字符(这种情况下比如;),这将会带来很大麻烦。如果你使用了宽松数量词,你只要花上很少的时间组装字符种类就能产生新的正则表达式。 
    在你知道你要捕获文本的环境结构时,宽松数量词是具有很大价值的。 

    五、利用可用分界符 
    Perl 和PHP语言常常使用左斜线(/)来标志一个正则表达式的开头和结尾,Python语言使用一组引号来标志开头和结尾。如果在Perl和PHP中坚持使用左斜线,你将要避免表达式中的任何斜线;如果在Python中使用引号,你将要避免使用反斜线()。选择不同的分界符或引号可以允许你避免一半的正则表达式。这将使得表达式易于阅读,减少由于忘记避免符号而潜在的bug。 
    Perl和PHP语言允许使用任何非数字和空格字符作为分界符。如果你切换到一个新的分界符,在匹配URL或HTML标志(如“http://”或“
;”)时,你就可以避免漏掉左斜线了。 
    例如,“/http://(S)*/”可以写为“#http://(S)*#”。 
    通用分界符是“#”、“!”和“|”。如果你要使用方括号、尖括号或者花括号,只要保持前后配对出现就可以了。下面就是一些通用分界符的示例: 
#…# !…! {…} s|…|…| (Perl only) s[…][…] (Perl only) s;/…/ (Perl only)  
     在Python中,正则表达式首先会被当作一个字符串。如果你使用引号作为分界符,你将漏掉所有反斜线。但是你可以使用“r''”字符串避免这个问题。如果针对“re.VERBOSE”选项使用三个连续单引号,它将允许你包含换行。例如 regex = "(\w+)(\d+)"可以写出下面的形式: 
regex = r''' 
           (w+) 
           (d+) 
         ''' 

    小结:本文的建议主要着眼于正则表达式的可读性,在开发中养成这些习惯,你将会更加清晰的考虑设计和表达式的结构,这将有助于减少bug和代码的维护,如果你自己就是这个代码的维护者你将倍感轻松。 


    
 
 

您可能感兴趣的文章:

  • PHP html标签正则替换并可自定义正则规则
  • php使用正则过滤js脚本代码实例
  • 正则表达式php函数库 ABNF to REGEX
  • php正则验证邮箱的函数
  • php利用正则表达式取出图片的URL
  • 一个正则的写法 php
  • php正则表达式转义字符的例子
  • PHP正则匹配图片并给图片加链接详解
  • php 正则 不包含某字符串的正则表达式
  • php password密码验证正则表达式(8位长度限制)
  • php正则表达式中的非贪婪模式匹配
  • PHP下ereg实现匹配ip的正则
  • php 手机号码验证正则表达式
  • PHP过滤★等特殊符号的正则
  • php正则表达式验证手机电话
  • PHP 正则表达式验证中文的问题
  • php正则过滤特殊字符的方法
  • php正则之函数 preg_replace()参数说明
  • php+正则将字符串中的字母数字和中文分割
  • php正则表达式匹配img中任意属性的方法
  • PHP把空格、换行符、中文逗号等替换成英文逗号的正则表达式
  • PHP匹配多行的正则表达式分析
  • 学习php中的正则表达式
  • PHP 正则表达式后面接的/isU, /is, /s含义
  • php下常用表单验证的正则表达式
  • php使用正则表达式提取字符串中尖括号、小括号、中括号、大括号中的字符串
  • PHP中的递归正则表达式用法分享
  • PHP 正则表达式分析RSS
  • PHP和javascript常用正则表达式及用法实例
  • php正则之函数 preg_replace()参数说明 iis7站长之家
  • PHP 正则表达式特殊字符 [:alnum:] [:alpha:] 等
  •  
    本站(WWW.)旨在分享和传播互联网科技相关的资讯和技术,将尽最大努力为读者提供更好的信息聚合和浏览方式。
    本站(WWW.)站内文章除注明原创外,均为转载、整理或搜集自网络。欢迎任何形式的转载,转载请注明出处。












  • 相关文章推荐
  • PHP 正则表达式函数库(两套)
  • php 正则表达式学习笔记
  • CFC4N小试php正则表达式
  • PHP 正则表达式 推荐
  • php半小时精通正则表达式
  • PHP 正则表达式常用函数使用小结
  • PHP常用正则表达式集锦
  • php使用curl和正则表达式抓取网页数据示例
  • php中正则表达式中的特殊符号
  • php正则表达式的基本语法总结
  • PHP正则表达式的逆向引用与子模式分析
  • PHP和正则表达式教程集合之一第1/2页
  • php的正则表达式完全手册
  • 最常用的PHP正则表达式收集整理
  • PHP和正则表达式教程集合之二第1/2页
  • 修改配置真正解决php文件上传大小限制问题(nginx+php)
  • IIS7配置PHP图解(IIS7+PHP_5.2.17/PHP_5.3.5)
  • PHP 5.4.19 和 PHP 5.5.3 发布及下载地址
  • php输入流php://input使用示例(php发送图片流到服务器)
  • 修改配置真正解决php文件上传大小限制问题(apache+php)
  • PHP转换器 HipHop for PHP
  • PHP去除html标签,php标记及css样式代码参考
  • PHP 框架 Pop php
  • PHP 'ext/soap/php_xml.c'不完整修复存在多个任意文件泄露漏洞
  • PHP的JavaScript框架 PHP.JS
  • php通过socket_bind()设置IP地址代码示例
  • php服务器探针显示php服务器信息
  • php安装完成后如何添加mysql扩展
  • PHP缓存加速器 Alternative PHP Cache (APC)
  • PHP的substr() 函数用法
  • PHP源文件加密工具 PHP Screw


  • 站内导航:


    特别声明:169IT网站部分信息来自互联网,如果侵犯您的权利,请及时告知,本站将立即删除!

    ©2012-2021,,E-mail:www_#163.com(请将#改为@)

    浙ICP备11055608号-3