php教程 php手册 详细学习动态网页制作PHP技术的正则表达式

详细学习动态网页制作PHP技术的正则表达式

Jun 21, 2016 am 09:01 AM
nbsp perl test

  正则表达式难于书写、难于阅读、难于维护,经常错误匹配意料不到的文本或者错过了有效的文本,这些问题都是由正则表达式的表现和能力引起的。每个元字符(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语言里面:

$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
;";
    }
    else {
        echo "Failed match on $test
;";
     }
}
?>;

        在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 = "(file://w+)(//d+)"可以写出下面的形式:
regex = r'''
           (\w+)
           (\d+)
         '''

    小结

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



본 웹사이트의 성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.

핫 AI 도구

Undresser.AI Undress

Undresser.AI Undress

사실적인 누드 사진을 만들기 위한 AI 기반 앱

AI Clothes Remover

AI Clothes Remover

사진에서 옷을 제거하는 온라인 AI 도구입니다.

Undress AI Tool

Undress AI Tool

무료로 이미지를 벗다

Clothoff.io

Clothoff.io

AI 옷 제거제

Video Face Swap

Video Face Swap

완전히 무료인 AI 얼굴 교환 도구를 사용하여 모든 비디오의 얼굴을 쉽게 바꾸세요!

뜨거운 도구

메모장++7.3.1

메모장++7.3.1

사용하기 쉬운 무료 코드 편집기

SublimeText3 중국어 버전

SublimeText3 중국어 버전

중국어 버전, 사용하기 매우 쉽습니다.

스튜디오 13.0.1 보내기

스튜디오 13.0.1 보내기

강력한 PHP 통합 개발 환경

드림위버 CS6

드림위버 CS6

시각적 웹 개발 도구

SublimeText3 Mac 버전

SublimeText3 Mac 버전

신 수준의 코드 편집 소프트웨어(SublimeText3)

Huawei Watch GT 5 스마트워치가 새로운 기능으로 업데이트되었습니다. Huawei Watch GT 5 스마트워치가 새로운 기능으로 업데이트되었습니다. Oct 03, 2024 am 06:25 AM

Huawei는 Watch GT 5 및 Watch GT 5 Pro 스마트워치용 소프트웨어 버전 5.0.0.100(C00M01)을 전 세계적으로 출시하고 있습니다. 이 두 스마트워치는 최근 유럽에서 출시되었으며, 표준 모델이 회사의 가장 저렴한 모델로 출시되었습니다. 이 하모니

철권의 샌더스 대령이 KFC에서 튀긴 꿈 철권의 샌더스 대령이 KFC에서 튀긴 꿈 Oct 02, 2024 am 06:07 AM

철권 시리즈 디렉터인 하라다 카츠히로(Katsuhiro Harada)는 한때 샌더스 대령을 상징적인 격투 게임에 도입하려고 진지하게 노력한 적이 있습니다. TheGamer와의 인터뷰에서 Harada는 패스트푸드의 전설을 g로 추가하기 위해 KFC Japan에 아이디어를 제안했다고 밝혔습니다.

Cybertruck FSD는 빠른 차선 전환 및 전체 화면 시각화를 칭찬합니다. Cybertruck FSD는 빠른 차선 전환 및 전체 화면 시각화를 칭찬합니다. Oct 01, 2024 am 06:16 AM

Tesla는 최신 완전 자율 주행(감독) 버전 12.5.5를 출시하고 있으며 Foundation Series 트림 가격에 포함된 기능으로 픽업이 판매된 지 10개월 만에 마침내 약속된 Cybertruck FSD 옵션이 제공됩니다. 에프

Garmin, 새로운 업데이트를 통해 여러 스마트워치에 대한 Adventure Racing 활동 개선 사항 출시 Garmin, 새로운 업데이트를 통해 여러 스마트워치에 대한 Adventure Racing 활동 개선 사항 출시 Oct 01, 2024 am 06:40 AM

Garmin은 최신 고급 스마트워치에 대한 새로운 안정적인 업데이트 세트로 이번 달을 마감합니다. 요약하자면, 회사는 Enduro 3, Fenix ​​E 및 Fenix ​​8(Amazon에서 현재 $1,099.99)의 높은 배터리 소모를 해결하기 위해 시스템 소프트웨어 11.64를 출시했습니다.

첫 번째 모습: 곧 출시될 Anker Zolo 4포트 140W 벽면 충전기(디스플레이 포함)의 개봉 동영상 유출 첫 번째 모습: 곧 출시될 Anker Zolo 4포트 140W 벽면 충전기(디스플레이 포함)의 개봉 동영상 유출 Oct 01, 2024 am 06:32 AM

2024년 9월 초, Anker의 Zolo 140W 충전기가 유출되었는데, 이 충전기가 회사의 디스플레이를 탑재한 최초의 벽면 충전기였기 때문에 큰 화제가 되었습니다. 이제 YouTube의 Xiao Li TV에서 제공하는 새로운 언박싱 동영상을 통해 안녕하세요.

HyperOS가 탑재된 새로운 Xiaomi Mijia 그래핀 오일 히터 출시 HyperOS가 탑재된 새로운 Xiaomi Mijia 그래핀 오일 히터 출시 Oct 02, 2024 pm 09:02 PM

샤오미는 곧 중국에서 Mijia 그래핀 오일 히터를 출시할 예정입니다. 이 회사는 최근 Youpin 플랫폼에서 호스팅되는 스마트 홈 제품에 대한 크라우드 펀딩 캠페인을 성공적으로 진행했습니다. 페이지에 따르면 기기는 이미 배송을 시작했습니다.

삼성 갤럭시 Z 폴드 스페셜 에디션, 상충되는 이름 등장으로 10월 말 출시 예정 삼성 갤럭시 Z 폴드 스페셜 에디션, 상충되는 이름 등장으로 10월 말 출시 예정 Oct 01, 2024 am 06:21 AM

오랫동안 기다려온 삼성의 '스페셜 에디션' 폴더블 출시가 또 다른 반전을 가져왔습니다. 최근 몇 주 동안 이른바 갤럭시 Z 폴드 스페셜 에디션에 대한 소문은 다소 잠잠해졌습니다. 대신 갤럭시 S25 시리즈로 초점이 옮겨졌습니다.

Manjaro 24.1 \'Xahea\'는 KDE Plasma 6.1.5, VirtualBox 7.1 등과 함께 출시됩니다. Manjaro 24.1 \'Xahea\'는 KDE Plasma 6.1.5, VirtualBox 7.1 등과 함께 출시됩니다. Oct 02, 2024 am 06:06 AM

10년 이상의 역사를 지닌 Manjaro는 초보자와 고급 사용자 모두에게 적합한 가장 사용자 친화적인 Linux 배포판 중 하나로 간주되며 설치 및 사용이 쉽습니다. 주로 오스트리아, 독일, 프랑스에서 개발된 이 Arch 기반 배포판은

See all articles