正则表达式进阶

来源:互联网 发布:windows用airplay 编辑:程序博客网 时间:2024/05/21 21:37

  网上讲正则表达式的教程实在是太多了,所以这里并不想重复那些被说烂的教程。我真正学习正则表达式是源于python的re模块,所以下面主要会以python语言为基础记录一下关于正则表达式的一些进阶并比较重要的用法。
  

  1.命名捕获组(Named Capturing Groups)

  其实我们经常使用捕获组,比如:
  re.search("(\w).*","a1234").groups() //捕获结果为 ('a',)
  当使用单纯的()捕获结果时需要使用0,1之类的下标来获取其实并不是很方便。所以在同样的情况下我们可以通过命名捕获组来根据名称获得捕获的结果。
  re.search("name:(?P<name>\w+).*","name:zhou").groupdict() //捕获结果为dict {‘name’: ‘zhou’}
  通过(?P<捕获组名称>捕获规则)的命名,我们可以根据字典获取捕获结果。
  

  2.零宽断言(Lookahead and Lookbehind Zero-Length Assertions)

  在 正则表达式 的百度百科中其实就有提到零宽断言一条。零宽断言主要用于对被捕获目标的前后两面字符进行判断但并不想捕获。
  对捕获目标的前面字符串进行判定称为回顾后发断言,对捕获目标的后面字符串进行判定称为预测先行断言。
  在判断时如果判断条件是一定要符合某种条件则称谓正,在判断时如果判断条件是一定要不符合某种条件则称谓负。
  零宽度正回顾后发断言: ‘a1b2c3’中需要捕获前面有至少有1个字母的数字:
  re.findall("(?<=[a-zA-Z])(\d)","a1b2c3") // 捕获结果为 ['1', '2', '3']
  零宽度正预测先行断言: ‘a1b2c3’中需要捕获后面有至少有1个数字的字母:
  re.findall("([a-zA-Z])(?=\d)","a1b2c3") // 捕获结果为 ['a', 'b', 'c']
  零宽度负回顾后发断言: ‘a1b2c3’中需要捕获前面不为字母a的数字:
  re.findall("(?<!a)(\d)","a1b2c3") // 捕获结果为 ['2', '3']
  零宽度负预测先行断言: ‘a1b2c3’中需要捕获后面不为数字1的字母:
  re.findall("([a-zA-Z])(?!1)","a1b2c3") //捕获结果为 ['b', 'c']
  其实这些名字看起来还是很高大上,可能因为比较绕口,总之记住什么是预测什么是回顾基本就懂了。
  

  3.后向引用(Backreferences )

  正则表达式提供了对匹配成功的某部分进行存储供以后使用这一能力,我们使用\1来访问第一个被捕获的组,\2,\3以此类推。
  后向引用可以用与正则表达式本身成为匹配规则的一部分:
 re.findall(r"[ ]+(\w+)[ ]+\1","This is a block of of text") // 结果为 ['of']
 re.findall(r"<([A-Z][A-Z0-9]*)\b[^>]*>.*?</\1>","<B><I>bold italic</I></B> text") //结果为 ['B']
 后向引用也可以用于字符串替换:
 在一个C++项目中需要在字符串”filePath=/dicom/1”中/dicom变为”/ImageStore/dicom”。为了担心在字符串中重复出现’dicom’这个字符串所以没采用字符串替换方案,但是在一番搜索后发现标准c++标准库中的正则表达式不能使用零宽断言语法(boost库已经支持),最终采用了后向引用。
 采用零宽断言的做法:
 re.sub("(?<=filePath\=)/dicom","/ImageStore/dicom","filePath=/dicom/1")
 采用后向引用的做法:
 re.sub("(filePath\=)/dicom","\g<1>/ImageStore/dicom","filePath=/dicom/1") //用 \g<1>获得第一个捕获组的结果
 上面两种做法其最终结果都为 filePath=/ImageStore/dicom/1

  不同语言对正则表达式的支持能力不同,python对正则特性的支持比c++标准库表现的更好,所以本文是以python为例记录了正则中我较常用的三种特性,正则表达式其实很复杂,其高级运用更不止如此。最终希望有一天C++的标准库能对正则表达式支持的更好。
  

0 0
原创粉丝点击