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

正则表达式难于书写、难于阅读、难于维护经常匹配意料不到文本或者错过了有效文本这些问题都是由正则表达式表现和能力引起每个元(metacharacter)能力和细微差别组合在使得代码不借助于智力窍门技巧就无法解释
许多包含定特性工具使阅读和编写正则表达式变得容易了但是它们又很不符合习惯对于很多员来说书写正则表达式就是种魔法艺术他们坚持自己所知道特征并持有绝对乐观态度如果你愿意采用本文所探讨 5个习惯你将可以让你设计正则表达式经受住反复试验
本文将使用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} # 3位数前缀
[-.] # 另个分隔符号
\d{4} # 4位数电话号码
/x
改写过正则表达式现在在电话区号后有个可选择分隔符号这样它应该是匹配“314-555-4000”然而电话区号还是必须员如果需要把电话区号变为可选项则可以迅速看出它现在不是可选个小小改动就可以解决这个问题
2、书写测试
共有 3个层次测试层为你代码加上层可靠性首先你需要认真想想你需要匹配什么代码以及你是否能够处理匹配其次你需要利用数据例子来测试正则表达式最后你需要正式通过个测试小组测试
决定匹配什么其实就是在匹配结果和错过正确结果的间寻求个平衡点如果你正则表达式过于严格它将会错过些正确匹配;如果它过于宽松它将会产生匹配旦某个正则表达式发放到实际代码当中你可能不会两者都注意到考虑下上面电话号码例子它将会匹配“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) {
( $test =~ m/
\(? # 可选圆括号
\d{3} # 必须电话区号
\)? # 可选圆括号
[-\s.]? # 分隔符号可以是破折号、空格或者句点
\d{3} # 3位数前缀
[-\s.] # 另个分隔符号
\d{4} # 4位数电话号码
/x ) {
pr "Matched _disibledevent=> "800-555-4400",
"(314)555-4000",
"314.555.4000",
"555-4000",
"aasdklfjklas",
"1234-123-12345"
);
$regex = "/
\(? # 可选圆括号
\d{3} # 必须电话区号
\)? # 可选圆括号
[-\s.]? # 分隔符号可以是破折号、空格或者句点
\d{3} # 3位数前缀
[-\s.] # 另个分隔符号
\d{4} # 4位数电话号码
/x";
foreach ($tests as $test) {
(preg_match($regex, $test)) {
echo "Matched _disibledevent=> "800-555-4400",
"(314)555-4000",
"314.555.4000",
"555-4000",
"aasdklfjklas",
"1234-123-12345"
]
pattern = r'''
\(? # 可选圆括号
\d{3} # 必须电话区号
\)? # 可选圆括号
[-\s.]? # 分隔符号可以是破折号、空格或者句点
\d{3} # 3位数前缀
[-\s.] # 另个分隔符号
\d{4} # 4位数电话号码
'''
regex = re.compile( pattern, re.VERBOSE )
for test in tests:
regex.match(test):
pr "Matched _disibledevent=>正则表达式测试也会是个小组良好基础现在正是开始创建好机会即使现在还不是创建合适时间你也应该在每次修改以后运行测试下正则表达式这里花费小段时间将会减少你很多麻烦事
3、为交替操作分组
交替操作符号(|)优先级很低这意味着它经常交替超过员所设计那样比如从文本里面抽取Email地址正则表达式可能如下:
^CC:|To:(.*)
上面尝试是不正确但是这个bug往往不被注意上面代码意图是找到“CC:”或者“To:”开始文本然后在这后面部分提取Email地址
不幸如果某行中间出现“To:”那么这个正则表达式将捕获不到任何以“CC:”开始而是抽取几个随机文本坦白正则表达式匹配 “CC:”开始但是什么都捕获不到;或者匹配任何包含“To:”但是把这行剩余文本都捕获了通常情况下这个正则表达式会捕获大量 Email地址所有没有人会注意这个bug
如果要符合实际意图那么你应该加入括号介绍说明清楚正则表达式如下:
(^CC:)|(To:(.*))
如果真正意图是捕获以“CC:”或者“To:”开始文本行剩余部分那么正确正则表达式如下:
^(CC:|To:)(.*)
这是个普遍不完全匹配bug如果你养成为交替操作分组习惯你就会避免这个
4、使用宽松数量词
很多员避免使用宽松数量词比如“*?”、“+?”和“??”即使它们会使这个表达式易于书写和理解
宽松数量词可以尽可能少匹配文本这样有助于完全匹配成功如果你写了“foo(.*?)bar”那么数量词将在第次遇到“bar”时就停止匹配而不是在最后如果你希望从“foo###bar+bar”中捕获“###”点就很重要个严格数量词将捕获“###bar +”
假设你要从HTML文件里面捕获所有电话号码你可能会使用我们上文讨论过电话号码正则表达式例子但是如果你知道所有电话号码都在个表格列里面你可以使用宽松数量词写出更简单正则表达式:
<tr>;<td>;(.+?)<td>;
很多刚起步员不使用宽松数量词来否定特定种类他们能写出下面代码:
<tr>;<td>;([^>;]+)</td>;
这种情况下它可以正常运行但是如果你想捕获文本包含有你分隔公共(这种情况下比如</td>;)这将会带来很大麻烦如果你使用了宽松数量词你只要花上很少时间组装种类就能产生新正则表达式
在你知道你要捕获文本环境结构时宽松数量词是具有很大价值
5、利用可用分界符
Perl 和PHP语言常常使用左斜线(/)来标志个正则表达式开头和结尾Python语言使用组引号来标志开头和结尾如果在Perl和PHP中坚持使用左斜线你将要避免表达式中任何斜线;如果在Python中使用引号你将要避免使用反斜线(\)选择区别分界符或引号可以允许你避免正则表达式这将使得表达式易于阅读减少由于忘记避免符号而潜在bug
Perl和PHP语言允许使用任何非数字和空格作为分界符如果你切换到个新分界符在匹配URL或HTML标志(如“http://”或“<br/>;”)时你就可以避免漏掉左斜线了
例如“/http:\/\/(\S)*/”可以写为“#http://(\S)*#”
通用分界符是“#”、“!”和“|”如果你要使用方括号、尖括号或者花括号只要保持前后配对出现就可以了下面就是些通用分界符举例:
#…# !…! {…} s|…|…| (Perl _disibledevent=>正则表达式首先会被当作如果你使用引号作为分界符你将漏掉所有反斜线但是你可以使用“r''”串避免这个问题如果针对“re.VERBOSE”选项使用 3个连续单引号它将允许你包含换行例如 regex = "(\\w+)(\\d+)"可以写出下面形式:
regex = r'''
(\w+)
(\d+)
'''
小结:本文建议主要着眼于正则表达式可读性在开发中养成这些习惯你将会更加清晰考虑设计和表达式结构这将有助于减少bug和代码维护如果你自己就是这个代码维护者你将倍感轻松
Tags:  php正则表达式教程 php正则表达式函数 php正则表达式详解 php正则表达式

延伸阅读

最新评论

发表评论