pcre和正则表达式的误点

摘要:
自读取第二个字符进行匹配以来,已匹配正则“1a*”$echo'111aaaaA'|perl lne prints/a*/b/rg'b1b1b1bBbAbAbAbAb#每个非a字符都替换为$echo'1111aaaaAAA'|perl lne“/1a*/;print$'”1#表示匹配的内容。11aaAAA#表示匹配后的剩余内容,这可能不太清楚;

本文只是关于正则一些容易出错的地方,关于正则的学习,可参考如下两篇文章:

基础正则:https://www.cnblogs.com/f-ck-need-u/p/9621130.html

Perl正则:https://www.cnblogs.com/f-ck-need-u/p/9648439.html

1.正则中所有的匹配模式,都应该理解为"匹配了某字符或字符串后,紧跟着再匹配"。这个概念很重要。

2.中括号首部使用脱字符时,表示的是紧跟着匹配不含给定字符的字符,而不是允许不匹配给定的字符。
它们大多数时候是等价的,但在匹配行尾时,意义不同,例如:Aa[^bcd]$ 所匹配的行允许是Aaa$或Aax$,但不允许仅是Aa$。
这就是正则中"紧跟着匹配"的意思。

3.(.[0-9]+)? 可匹配小数点部分,不能写成 (.?[0-9]*) ,后者即使不能匹配小数点,也能匹配原本处于小数点后的数值

4.星号*匹配问题0或多个字符,如果写成"a*",将可以匹配任何单个字符,只不过对于非a字符,匹配到的结果为空。

例如字符串"111aaaAAA",正则"1a*"其实从读取第2个字符进行匹配的时候就已经匹配完成了,匹配的结果不是"1aaa",而是"1"加上1前面的一个空,因为是从行首这个锚定位开始匹配的。使用sed或perl进行替换操作就很容易理解。

$ echo '111aaaAAA' | perl -lne 'print s/a*/b/rg'
b1b1b1bbAbAbAb        # 每一个非a字符前都被替换了

$ echo '111aaaAAA' | perl -lne "/1a*/;print $&;print $'"
1                   # 表示匹配到的内容
11aaaAAA       # 表示匹配后剩下的内容

这可能不是很好理解。但其实想想".*"的作用,其实他们是同一类的写法,正如".*"并不是先用点去匹配一个字符后再通过星号去重复这个字符,而是直接表示匹配可能任意多个的任一字符。不同之处在于,".*"匹配任何单个字符,而"a*"匹配任何单个字符,包括空位置。

另外,有些语言在处理正则的方式上有些差异,特别是在测试"a*"的时候,grep/sed/perl均有所不同,不过python/perl/ruby之类的语言在处理这个问题上,结果都一样。

5.perl正则括号分组时,使用(?:替代左括号(,可以表示只分组不捕获。所谓的捕获表示的是可以反向引用或保存到正则外部的变量中
([-+]?[0-9]+(.[0-9]+)?) *(cm|mm) :(cm|mm)将保存为$3
([-+]?[0-9]+(?:.[0-9]+)?) *(cm|mm) : (cm|mm)将保存为$2

6.特殊锚定符,锚定所匹配的是位置,而非字符,行首^和行尾$同样如此。
注意某些程序对单词的理解和边界定义不一样。且有些程序并不完全支持下列所有的特殊元字符。一般来说,单词是由字母、数字和下划线组成的,即[a-zA-Z0-9_]。
例如gnu grep 2.6版本就不支持s和d,而gnu grep 2.20支持s但不支持d
'':匹配单词边界处的空字符Match the empty string at the edge of a word.
'B':匹配非单词边界处的空字符Match the empty string provided it's not at the edge of a word.
'<':匹配单词开头处的空字符Match the empty string at the beginning of word.
'>':匹配单词结尾处的空字符Match the empty string at the end of word.
'w':匹配单词构成部分Match word constituent, it is a synonym for `[_[:alnum:]]'.
'W':匹配非单词构成部分Match non-word constituent, it is a synonym for `[^_[:alnum:]]'.
's':匹配空白字符Match whitespace, it is a synonym for `[[:space:]]'.
'S':匹配非空白字符Match non-whitespace, it is a synonym for `[^[:space:]]'.
'd':匹配数字it is a synonym for `[0-9]'.
'D':匹配非数字it is a synonym for `[^0-9]'.

For example, 'rat' matches the separate word 'rat', 'BratB' matches 'crate' but not 'furry rat'.

注意,''不仅仅只是简单的表示"匹配单词的边界空字符",它还要求它的左边或右边(且只能是一边)有单词字符。比如`.`能匹配"abc/"中的c,但不能匹配最后那根斜线,因为""左边是非单词字符,右边是字符串结尾,也是非单词字符。但"."能匹配"abc/x"中的"c"和"/"和"x"。

7.字符类,注意某些程序并不完全支持下列所有的字符类
'[:alnum:]' :same as '[0-9A-Za-z]'.
'[:alpha:]' :'[:lower:]' and '[:upper:]', same as '[A-Za-z]'.
'[:lower:]' :
'[:upper:]' :
'[:digit:]' :'0 1 2 3 4 5 6 7 8 9'.
'[:xdigit:]' :Hex digits: `0 1 2 3 4 5 6 7 8 9 A B C D E F a b c d e f'.

'[:blank:]' :space and tab.
'[:space:]' :tab, newline, vertical tab, form feed, carriage return, and space.
'[:punct:]' :Punctuation characters; this is '! " # $ % & ' ( ) * + , - . / : ; < = > ? @ [ ] ^ _ ` { | } ~'.
'[:print:]' :'[:alnum:]', '[:punct:]', and space.
'[:graph:]' :Graphical characters: '[:alnum:]' and '[:punct:]'.

'[:cntrl:]' :Control characters. octal codes 000 through 037, and 177 (`DEL').

8.同一个表达式中,被匹配过的字符无法被第二次匹配。因为正则的宗旨是:匹配了某字符或字符串后,紧跟着再匹配。
例如字符串"#c#",正则表达式"(#.)(.#)"无法匹配。
再例如字符串"#cc#",正则表达式"(#.)(.*)(.#)"能匹配成功,只不过第二个分组只能匹配空。

9."环视"锚定,即lookaround anchor(也称为"零宽断言",表示匹配的是位置,不是字符)。
以 (?= 替代左括号表示从左向右的顺序环视,例如(?=d)表示当前字符的右边是一个数字时就满足条件
以 (?<= 替代左括号表示从右向左的逆序环视,例如(?<=d)表示当前字符的左边是一个数字时就满足条件

  • 正向环视:(?=...)和(?!...),感叹号表否定,即无法匹配感叹号右边的字符时才捕获。
  • 逆向环视:(?<=...)和(?<!...)

逆向环视的表达式必须只能表示固定长度的字符串,例如(?<=word)或(?<=word|word)可以,但(?<=word?)不可以,因为?匹配0或1长度,长度不定。
在PCRE中,可重写为(?<=word|words),但perl中不允许,因为perl严格要求长度必须固定。


10.关于"环视"锚定,最需要注意的一点是匹配的结果不占用任何字符,它仅仅只是锚定位置。
例如:your name is longshuai MA 和 your name is longfei MA
使用(?=longshuai)将能锚定第一个句子中单词"longshuai"前面的空字符,但它的匹配结果是"longshuai"前的空白字符,
所以(?=longshuai)long才能代表"long"这几个字符串
所以仅对于此处的两个句子,long(?=shuai)和(?=longshuai)long是等价的

11.贪婪匹配、惰性匹配和占有优先匹配
默认情况下,对于重复次数的表达式都是贪婪匹配,表示尽可能多的匹配。
有些高级正则引擎支持惰性匹配,表示尽可能少的匹配,只要能满足条件就立即停止。

  • *、    +、    ?、     {M,N} :都是贪婪匹配(greedy)
  • *?、  +?、  ??、   {M,N}? :都是惰性匹配(lazy,Reluctant)
  • *+、  ++、  ?+、   {M,N}+ :都是占有优先匹配(possessive)

占有优先和固化分组是相同的,只要占有了就不再交换,不允许进行回溯。示例见下面的(?>...)固化分组方式

12.匹配模式

  • (?i):不区分大小写,可使用(?-i)取消该模式。例如"(?i)abc(?-i)cdB"只对中间的abc进行不区分大小写的匹配
    • 由于(?i)遇到闭括号就失效,可以将需要不区分大小写匹配的部分写入分组括号中,例如"((?i)abc)cdB",(?:(?i)abc)cdB=(?i:abc)cdB
  • (?x):extend模式,将忽略多个连续空格和注释符到行尾的字符
  • (?m):(multiline)多行模式,改变^和$的匹配模式。默认模式下,它们匹配字符串首部和尾部。此模式下:
    • ^将匹配字符串首部和换行符。若要仅匹配字符串首部,使用A。
    • $将匹配字符串尾部、换行符和换行符前的空字符。若要仅匹配字符串尾部和行尾,使用,若要仅匹配字符串尾部,使用z
  • (?s):(singleline或dotall)单行模式,改变"."的匹配模式,默认模式下,点"."无法匹配换行符,dotall模式下可以
  • (?U):lazy匹配模式。默认是greedy匹配。

13.强制字面解释:Q...E。该序列将其中间的所有字符强制解释为字面符号,强制性极强。
但perl和pcre有所不同。perl中,该序列中间可引用变量进行变量替换,而pcre中变量符号也被当作普通字符。

14.普通分组和捕获

  • (),$1,$2,$3,$4...有些地方使用1,2,3,4,sed中使用&表示所有匹配,perl中则使用$&
  • g1,g2,g3或g{1},g{2},g{3}。

其中$1,$2, ...用于正则外面,而"g1", "g2", ... 用于正则内部

15.命名分组和捕获

  • (?:...):非命名捕获,仅用于分组,不可用于引用,也称为非捕获型括号。例如"(1|one)(?:2|two)(3|three)",$1=(1|one),$2=(3|three)
  • (?<NAME>...):命名捕获,分组捕获后还命名,就像变量赋值一样。可以使用k<NAME>或k'NAME'或g{NAME}的方法来引用
  • (?>...):固化分组。一匹配成功就永不交回内容(用回溯的想法理解很容易)。

例如"hello world"可以被"hel.* world"进行匹配,但不能被"hel(?>.*) world"匹配。
因为正常情况下,".*"匹配到所有内容,然后回溯释放已匹配内容直到空格" "字符。而固化分组后,已匹配的内容绝不交回,也就无法回溯。

16. 重置匹配:K 用于重置匹配的位置。
比如,footKbar 匹配”footbar”,但是得到的匹配结果是 ”bar”。但是, K 的使用不会干预到子组内的内容, 比如 (foot)Kbar 匹配 ”footbar”,第一个子组内的结果仍然会是 ”foo”。

$ echo abc123abcfoo | grep -P -o '(abc)123Kg1foo' 
abcfoo

17.要想对一个字符串匹配后取反。可以通过正向环视锚定取反来间接实现。
例如,"-a -3 ac c 3 b"中取出负数、正数和空格很简单,"-?[0-9]+|s"即可,但想要借此取反得到"-a ac c b",目前正则表达式只能通过(?!)的环视取反实现:"((?!-?[0-9]+|s).)*",外层括号表示右边不是正数、负数或空格的字符都匹配并进行分组,然后重复量词*,将连续的内容连接起来。
例如:

echo "-a -3 ac c 3 b" | grep -P '((?!-?[0-9]+|s).)*'

...

免责声明:文章转载自《pcre和正则表达式的误点》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇算法分析思维分析,以循环赛日程表为例Window Classes in Win32下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

.NET 数据类型之字符串(String)

  字符串      表示文本,即一系列 Unicode 字符。字符串是 Unicode 字符的有序集合,用于表示文本。 String 对象是 System.Char 对象的有序集合,用于表示字符串。 String 对象的值是该有序集合的内容,并且该值是不可变的(即,为只读)。String 对象的最大大小内存中的为 2 GB 或大约 10 亿个字符。关键...

第一个极小的机器学习的应用

  现在给出一个Web统计信息,他们存储着每小时的访问次数。每一行包含连续的小时和信息,以及该小时Web的访问次数。现在要解决的问题是,估计在何时访问量达到基础设施的极限。极限数据是每小时100000次访问。 1.读取数据: # 获取数据 filepath = r'C:UsersTDDesktopdataMachine Learning1400OS_01_...

pandas --合并操作concat函数和append

concat concat函数是在pandas底下的方法,可以将数据根据不同的轴作简单的融合 pd.concat(objs, axis=0, join='outer', join_axes=None, ignore_index=False, keys=None, levels=None, names=None, verify_integri...

Kube-DNS搭建(1.4版本)

目录贴:Kubernetes学习系列 1、介绍   之前介绍过DNS的搭建(基于Kubernetes集群部署skyDNS服务),但那个版本的DNS是随着Kubernetes1.2发布出来的,有点原始。本文主要讲解Kubernetes1.4版本中的DNS插件的安装。与1.2版本相比,1.4中的DNS增加了解析Pod(HostName)对应的域名的新功能,且...

【深度学习系列】用Tensorflow实现经典CNN网络Vgg

  上周我们讲了经典CNN网络AlexNet对图像分类的效果,2014年,在AlexNet出来的两年后,牛津大学提出了Vgg网络,并在ILSVRC 2014中的classification项目的比赛中取得了第2名的成绩(第一名是GoogLeNet,也是同年提出的)。在论文《Very Deep Convolutional Networks for Large...

数据可视化基础专题(44):NUMPY基础(9)数组操作(1)修改数组形状/翻转数组

1 修改数组形状 函数 描述 reshape 不改变数据的条件下修改形状 flat 数组元素迭代器 flatten 返回一份数组拷贝,对拷贝所做的修改不会影响原始数组 ravel 返回展开数组 numpy.reshape numpy.reshape 函数可以在不改变数据的条件下修改形状,格式如下: numpy.reshape(a...