读书笔记《Mastering Regular Expressions》(四)

MasteringRegEx
一、问题

将一个数值字串,从右向左,每三位用逗号分开,比如,将“123456789”替换为“123,456,789”。

二、分析

替换算法的本质是将字串从右向左,每3个字符为一组进行分组,然后在组与组之间插入逗号。

三、语法

正则表达式引入了“预测”和“回顾”这两个概念,它们不是匹配字串,而是匹配一个“位置”,其中预测是正向(自左向右)查看,回顾是反向(自右向左)查看。

(下表中圆括号及其括住的内容,只匹配位置,不匹配文本)
正则表达式名称说明示例
(?=...)预测断言子表达式在此位置的右侧abcd(?=/d)匹配右侧紧跟数字的abcd
(?!...)否定预测断言子表达式不在此位置的右侧abcd(?!/d)匹配右侧紧跟的不是数字的abcd
(?<=...)回顾断言子表达式在此位置的左侧 (?<=/d)abcd匹配左侧是数字的abcd
(?<!...)否定回顾断言子表达式不在此位置的左侧(?<!/d)abcd匹配左侧不是数字的abcd

预测和回顾只是匹配一个位置,其中的子表达式并不被匹配。比如有字符“ABCDEFG”,则正则表达式

(?=EFG)(?<=ABCD)(?<=ABCD)(?=EFG)

就匹配D与E之间的那个位置,而不是匹配EFG,也不匹配ABCD,或其他任何一个字串。 注意,由于只是断言一个位置,所以上述预测和回顾的先后次序并不重要,二者同样都是匹配D与E之间的那个位置。前者的含义是“匹配一个位置,它的右边是 EFG,左边是ABCD”,后者的含义是“匹配一个位置,它的左边是ABCD,右边是EFG”,两者都是同一个意思。下面的Perl在ABCD和EFG之 间插入一个逗号:

$foo = "ABCDEFG";
$foo =~ s/(?<=ABCD)(?=EFG)/,/;
print $foo;

假定要把某段文本中所有的Jeffs都替换为Jeff's(匹配到单词边界),那么,就会有如下几种方案:

方案
说明
s//bJeffs/b/Jeff's/g这是最直观的方法,直接把匹配到的Jeffs替换为Jeff's
s//b(Jeff)(s)/b/$1'$2/g分组构造,在两个组之间插入单引号
s//bJeff(?=s/b)/Jeff'/g预测,仅匹配“后面紧跟s并在单词边界的Jeff”的右侧那个位置
(其它的如Jeffrey中的Jeff不被匹配)
s/(?<=/bJeff)(?=s/b)/'/g同时使用预测和回顾,在匹配位置处插入单引号
s/(?=s/b)(?<=/bJeff)/'/g与上面的行为完全相同,由于预测和回顾只是匹配位置而不是匹配字串,所以先后次序不重要了。
四、解决问题

利用预测和回顾,就可以解决“在数值中插入逗号”的问题了。插入算法是:从右向左把数字,每3个分为一组,在组与组之间插入逗号。也就是说,只要匹配到这些位置,就可以在这些位置上插入逗号。

$foo = "123456789";
$foo =~ s/(?=/d/d/d)/,/g;
print $foo;

上面的结果是“,1,2,3,4,5,6,789”,显然不是想要的。

正则表达式结果说明
s/(?=/d/d/d)/,/g
s/(?=(/d/d/d)+)/,/g
,1,2,3,4,5,6,7891左边的位置被匹配,因为该位置右边有3个数字(123)
同理,1右边的位置也被匹配,该位置右边有3个数字(234)……
而7的右边位置不再被匹配,因为该位置右边不再有3个数字
s/(?=/d/d/d$)/,/g123456,789只有7左边的位置被匹配,因为该点符合以下两个条件:
其右边是3个数字,
且这3个数字在行尾(其右侧什么也没有了)。
s/(?=(/d/d/d)+$)/,/g,123,456,7891左边的位置符合条件“其右侧有N组(每组3个数字),且最后一组在行尾”;
2左边的位置不符合该条件,因为其后面是234、567和89,最后的89无法构成一组;
其它同理……
s/(?=(/d/d/d)+$)(?<=/d)/,/g123,456,7891左边的位置不再被匹配,因为(?<=/d)的存在,它指示该位置的左侧必须是数字,
而1左边的位置显然不符合。

这个正则表达式替换单个的数字串没问题,但如果想要将“The population of 281421906 is growing”中的281421906替换成281,421,906就无能为力了,原因是$限定了最后一组(3个)数字必须在行尾,因此需要把条件修正为“最后一组数字的右边不是数字”,这就要用到“否定预测”:

s/(?=(/d/d/d)+(?!/d))(?<=/d)/,/g

(微软VBScript.DLL提供的COM对象IRegExp2,文档里说支持“回顾”语法,但实际运用却会抛出一个异常,不知何故)

 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值