zoukankan      html  css  js  c++  java
  • pcre和正则表达式的误点

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

    基础正则: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).)*'

    ...

  • 相关阅读:
    SAP 增强篇 Method1 BADI增强的查找方法
    增强篇2 生产订单屏幕增强
    增强篇1 PO保存增强
    ABAP DEMO篇21 选择屏幕显示说明TEXT
    ABAP字符串操作1 检查字段小数位的长度
    采购信息记录批导BAPI
    POPUP_GET_VALUES 金额字段不可编辑
    使用弹窗批量修改数据POPUP_GET_VALUES
    Win10手记-取色器ColorPicker的实现
    ASP.NET Web API实现微信公众平台开发(三)自定义菜单
  • 原文地址:https://www.cnblogs.com/f-ck-need-u/p/7684762.html
Copyright © 2011-2022 走看看