《精通正则表达式》笔记

正则表达式 (cleregular expression)

    正则表达式是高效,强大,便捷的文本处理工具,本身,再加上如同一门袖珍编程语言的通用模式表示法(general pattern notation),赋予使用者描述和分析文本的能
力。配合上特定语言工具提供的额外支持,正则表达式能够添加,删除,分离,叠加,插入,和休整各种类型的文本和数据。

正则表达式的组成
    
    完整的正则表达式由两种字符构成:特殊字符(special characters,如通配符*),称为“元字符”;其他为“文字”(literal),或称普通文本字符(normal text characters)。



    行起始和结束(位置匹配)

    在检查一行文本时,"^" 代表一行的开始, "$" 代表结束。如,“cat” 寻找的是一行文本中的任意位置的 “cat”,但是 “^cat” 只寻找行首的 cat, "^"用来把匹配文本
(这个表达式的其他部分匹配的字符)“锚定”在这一行的开头。同样,“cat$”之寻找位于行末的 cat,如 scat,以cat结尾的行。


    ^cat 匹配以 cat 开头的行 //错误
    ^cat 匹配以 c 作为一行的第一个字符,紧接着一个 a , 再紧接着 t 的文本。 //正确

    注:要养成按照字符理解正则表达式的习惯。脱字符和美元符号的特别之处在于,他们匹配的是一个“位置”,而不是具体文本。



    匹配若干字符之一(文本匹配)

    如过,我们想匹配单词,如“grey”,同时又不确定它是否写作“gray”,就可以使用正则表达式结构体(construct)"[……]"。它容许使用者列出在某处期望匹配的字符,>通常被称为字符组(characters class)。"e" 匹配 e,"a" 匹配 a, "[ea]" 匹配 e 或 a。

    "gr[ea]y": 先找到 g,跟着是一个r,然后是一个 a 或者 e,最后是一个y。
   
    注:"[Ss]mith" 单词首字母大小写的情况需注意,这个表达式,仍然可以匹配内嵌在单词里的smith或Smith,如blacksmith。
    
    请注意,在字符组以外,普通字符,都有“接下来是(and then)” 的意思,“gr[ae]y"——"首先匹配 g,接下来是 r ……"。这与字符组内部的情况正好相反。字符组的内容
是在同一个位置能够匹配的若干字符,所以它的意思是“或”。
    
    在一个字符组中可以列举多个字符。如[123456],匹配 1 到 6 中的任意一个数字。在字符组内部,字符组元字符(character-class metacharacter)"-"(连字符)表示>一个范围。如[0-9],[a-z]等,表示匹 配0 到 9中的任意一个数字,a 到 z 中的任意一个小写字母。和一一列举表单的意思是一样的。“[0-9a-fA-F],[A-Fa-f0-9]”用来匹配>十六进制数字,顺序无所谓。除此外,可以随心所欲把字符范围与普通字符相结合,如“[0-9A-z_!.?]”,能够匹配一个数字,大写字母,下划线,惊叹号,点号,或者问号。

    注:只有在字符组内部,“-” 连字符才是元字符,否则它只能匹配普通连字符号。实际,即使在字符组内部,若它出现在字符组的开头,它也不是元字符,只是一个普通

字符“—”,而不是表示一个范围。同样的道理,问号,和点号通常也被当做元字符,但是在字符粗内,则不是。

   排除型字符组(文本匹配)

    "[^……]",这个字符组会匹配任何未列出的字符。如"[^1-6]",匹配除1到6外的任何字符。字符组开头的"^",表示排除,这里列出不希望匹配的字符。

    注:排除型字符组,表示“匹配一个未列出的字符”,而不是 “不要匹配列出的字符”。两种说法看似一样,但略有差异。有一种简单的理解排除型字符组的办法,就是把>他们看做普通字符组,里面包含的是除了“排除型字符组中所有字符”以外的字符。

    egrep 'q[^u]' word.list
    "q[^u]" 可以匹配到,Iraqi,qoph,miqra等,无法匹配到 Iraq,Qantas 。原因,正则要求的是 小写 q,而 Qantas是 大写Q 所以无法匹配,若用Q[^u]则可以。无法匹>配到Iraq,原因是, 正则要求q之后紧跟一个u以外的字符,这就排除了q在行尾的情况。通常,行尾会存在一个换行符,但是egrep会在检查正则表达式之前,去掉换行符,>所以行尾的q之后,没有有除u之外的其他字符。故无法匹配。

    注:一个字符组,即使是排除型字符组,也需要匹配一个字符。(Iraq 无法匹配的原因,q后没有字符,和'q[^u]',先匹配 q, 再匹配一个非u的字符,不符。)


    用点号匹配任意字符(文本匹配)

    元字符 "." (dot 或 point) 是用来匹配任意字符的字符组的简便写法。如,‘03[-./]19[-./]76’ 匹配 '03/19/76,03-19-76,03.19.76', 也可以简单尝试,'03.19.76'。

    注:"." 在字符组中 "[-./]" 表示普通字符。


    多选结构


    匹配任意子表达式(子表达式文本匹配)

    "|" 是一个非常简捷的元字符,它的意思是“或”。依靠它把不同的子表达式组合成一个总的表达式,而这个总的表达式,又能够匹配任意的子表达式。在这样的组合中,
子表达式称为“多选分支”。如:"Bob" 和 "Robert" 是两个表达式, "Bob|Robert" 就是能头同时匹配其中任意一个正则表达式。如:"gr[ae]y" 可以写成 "gr(a|e)y"。其>中括号是必须的。否则"gra|ey",则是匹配 "gra" 或者 "ey"。多选结构可以包含多字符,但是不能跨越括号的界限。如"(fir|1)st.[Ss]treet"。

    注:一个字符组只能匹配目标文本中的单个字符,而每个多选结构自身都可能是完整的正则表达式,都可以匹配任意长度的文本。

    在一个包含多选结构的表达式中使用脱字符和美元符的时候要小心。例如 "^From|Subject|Date:." 和 "^(From|Subject|Date):.",前者由3个多选分支构成,所以能匹>配"^From"或"Subject"或"Date:.",后者匹配一行起始位置,然后匹配"From","Subject"或"Date"中的任意一个,然后匹配":."。

    即可匹配

        起始行,然后F,r,o,m然后:.,
        起始行,然后S,b,j,e,c,t然后:.,
        起始行,然后D,a,t,e然后:.,

    忽略大小写

    忽略大小写,这并非是正则表达式语言的一部分,而是许多工具语言提供的有用的特性。如egrep 的 "-i" 参数。



    单词分界符(位置匹配)

    如果 egrep 支持“元字符序列”,如"\<","\>" 就可以用他们来匹配单词分界的位置。它相当于单词行锚点。用来匹配单词的开头和结束位置。如"\<cat\>" 匹配单词开>头位置,然后是c,a,t三个字母,然后是单词结束的位置。

    注:"<"和">" 本身不是元字符,只有当它们和斜线结合的时候,整个序列才具有特殊意义。这就是称为“元字符序列”的原因。重要的是它的特殊意义,而不是字符个数>。


    元字符小结

    .      点               单个任意字符
    []     字符组           列出的任意字符
    [^]    排除型字符组     为列出的任意字符
    ^      脱字符           行的起始位置
    $      美元符           行的结束位置
    \<     反斜线-小于      单词的起始位置
    \>     反斜线-大于      单词的结束位置

    |      竖线             匹配分隔两边的任意一个子表达式
    ()     括号             限制竖线的作用范围,等



    可选项元素(文本匹配,结构匹配)

    "?" 代表可选项。把它加在一个字符的后面,就表示此处容许出现这个字符,不过它的出现并非匹配成功的必要条件。它只作用与之前紧邻的元素。"colou?r" 匹配 c,o,l,o,然后u?,最后r,无论u是否出现,匹配都是成功的。
    "4th|4" == "4(th)?" "?"作用于整个括号。


    其他量词,重复出现(文本匹配,结构匹配)

    "+"和"*" 的作用与问号类似,"+"表示之前紧邻的元素出现一次或多次。"*" 表示之前紧邻的元素出现任意多次,或者不出现。换句话说,"*" 表示匹配尽可能多的次数
,如果实在无法匹配,也不要紧。"+"的意思,匹配尽可能多的次数,但如果连一次匹配都无法完成,就报告失败。"?,+,*" 3个元字符,统称为量词,因为它们限定了所作用
元素的匹配次数。

    注:一个字符组是一个“元素”,所以它可以直接加量词"+,*,?"等,而不需要括号。
    注:每个量词都规定了匹配成功至少需要的次数下限,以及尝试匹配的次数上限。对于某些量词来说,下限是0,对于某些量词来说,上限是无穷大。

  量词小结

    ?      下限无        上限1      可以不出现,也可以只出现一次(单次可选)
    *       下限无        上限无     可以出现无数次,也可以不出现(任意次数均可)
    +       下限1         上限无     可以出现无数次,但至少要出现一次(至少一次)



    规定重复次数的范围:区间(结构匹配,文本匹配)

    使用元字符序列来自定义重复次数的区间"{min,max}",称为区间量词。如 "{3,12}" 能够容许的重复次数在3到12之间。



    括号的反向引用(文本匹配,结构匹配)

    括号的两种用途:限制多选项的范围;将若干字符组合为一个单元,受问号或星号之类量词的作用。
    此外在许多流派的正则中,括号能够“记住” 它们包含的自表达式匹配的文本。“反向引用” 正则表达式特性之一,他允许我们匹配与表达式先前部分匹配的同样的文本。
如:"\<([A-Za-z]+).+\1\>",括号能够“记住”其中子表达式匹配的文本,不论文本内容是什么,元字符序列"\1"都能够记住它们。


    "([a-z])([0-9])\1\2","\1" 代表"[a-z]"匹配的内容,"\2"代表"[0-9]"匹配的内容。




    神奇的转义(文本匹配)

    "\." 转义的点号,这个元序列代表普通点号。这样的方法适用与所有元字符,不过在字符组内部无效。"\" 这样的反斜线称为“转义符”,它的作用是使被转义的元字符>失去特殊含义,成了普通字符。如:"\([a-zA-Z]+\)" 来匹配一个括号内的单词。若"\"后跟的不是元字符,根据语言工具版本而定。如"\<","\>","\1"被当做元字符序列对>待。



    括号的分组捕获与分组不捕获(文本匹配,结构匹配)

    "(……)",用来分组和捕获(括号的反向引用) 而 "(?:……)" 表示只分组不捕获

    if($input =~ m/^([+-]?[0-9]+(?:\.[0-9*]?))[.*]?([CF])$/) "[CF]",两端的括号排在第三位,但它匹配的文本会保存在$2中。"(?:……)" 括号匹配的文本将不会被计>数,不会被捕获保存。这样做避免了不必要的捕获,提高了匹配效率。


    \s 匹配所有“空白”(文本匹配)

    "\s" 表示所有表示“空白字符”的字符组,其中包括,空格符,制表符,换行符,和回车符。
    $input =~ m/^([+-]?[0-9]+(\.[0-9]*)?)\s*([CF])$/


    "/i" 称作“模式修饰符”,其作用是告诉perl的进行不区分大小写的匹配。修饰符其实不是正则表达式的一部分,而是"m/……/"结构的一部分,这个结构告诉perl使用者的
意图,以及采用的正则表达式。


    "/g" 称作"全局替换"修饰符,在第一次替换完成后,继续搜索更多的匹配文本,进行更多的替换。如果需要检查的字符串包含多行需要替换的文本,每条替换规则都要>对所有行生效,就必须使用"/g";

    简记法

    \t          制表符
    \n          换行符
    \r          回车符
    \s          任何“空白”字符(空格,制表,回车等)
    \S          除 “\s空白” 之外的任何字符
    \w          [a-zA-Z0-9] 大小写字母和数字,(\w+ 可以用来匹配一个单词)
    \W          除"\w 大小写字母和数字" 之外的任何字符([^a-zA-Z0-9])
    \d          [0-9],即数字
    \D          除"\d数字"之外的任何字符。([^0-9])
    \b          单词边界符



    perl 的查找和替换

    $var =~ m/regex/ 尝试用正则表达式匹配保存在变量中的文本,并返回能否匹配成功的布尔值。

    $var =~ s/regex/replacement/,若正则表达式能够匹配 $var 变量中的某段文本,则将这段文本替换为 replacement.



    公函生成

    正则搜索简单标记,找到之后用指定的文本替换他。用于替换的文本是 perl中的字符串,所以他们能够引用变量,如程序所示。


     $letter="Dear =FIRST=,
        You have been chosen to win a brand new =TRINKET=! Free!
        Could you use another =TRINKET= in the =FAMILY= household?
        Yes =SUCKER=,
        I bet you could! Just respond by ……";

        $given = "Tom";
        $family = "Cruise";
        $wunderprize = "100% genuine faux diamond";

        $letter =~ s/=FIRST=/$given/g;
        $letter =~ s/=FAMILY=/$family/g;
        $letter =~ s/=SUCKER=/$given $family/g;
        $letter =~ s/=TRINKET=/fabulous $wunderprize/g;

        print $letter;

    股票价值

    股票价值,通常保留小数点后两位,如果第三位不为零,也要保留,去掉其他数字。
    $price =~ s/(\.\d\d[0-9]?)\d*/$1/,"\.",开始匹配小数点,接下来"\d\d"匹配开头两位小数,"[0-9]?"匹配最后一位非零数字,这是我们希望保留的,捕获的$1中保>存,然后将$1放入replacement字符串中。这样做的意义在于,$1在括号外的还能够匹配,但是它没有在replacement字符串中,所以会被删除。也就是说被删除的文本是其他
多余的数字,也就是正则末尾"\d*"匹配的字符。这是一种正则思想。



    perl -p -i -e 's/sysread/read/g' file

    -e 表示整个程序接在命令后面
    -p 表示对目标文件的每一行进行查找替换
    -i 表示把替换的结果写回到文件
    -w 用来打开 perl 的额外警告功能


    perl -w mkreply king.in > king.out



    用环视功能为数值添加逗号

    环视结构不匹配任何字符,只匹配文本中的特定位置。
    “顺序环视”,作为表达式的一部分,顺序环顺序(从左到右)查看文本,尝试匹配子表达式,如果能够匹配,就返回匹配成功的信息。肯定型顺序环视用特殊序列  "(?=……)"来表示,如 "(?=\d)",表示如果当前位置右边的字符是数字则匹配成功。另一种环视称为“逆序环视”,它逆序(从右至左)查看文本。它用特殊序列"(?<=……)">表示,例如"(?<=\d)",如果当前位置左边有一位数字,则匹配成功。(也就是说,紧跟在数字后面的位置)

    环视不会“占用”字符。

    检查子表达式能否匹配的过程中,他们本身不会“占用”任何文本。

    by Jeffrey Fridl "(?=Jeffrey)", 匹配的位置为 by &Jeffrey Fridl。"&"所在位置。顺序环视会检查子表达式能否匹配,但是它只寻找能够匹配的位置,而不会真正"占用"这些字符。

    顺序环视和真正匹配字符的部分结合,如"Jeff" 我们就能比单纯"Jeff"匹配更精确的结果。"(?=Jeffery)Jeff" 只能匹配 "Jeffery" 这个单词中的"Jeff"。

    "by Jeffrey Friedl" //可以匹配  Jeff
    "by Thomas Jefferson" //不可匹配

    "(?=Jeffrey)" == "Jeff(?=rey)"

    s/\bJeffs\b/Jeff's/g
    s/\b(Jeff)(s)\b/$1'$2/g
    s/\bJeff(?=s\b)/Jeff'/g
    s/(?<=\bJeff)(?=s\b)/'/g
    s/(?=s\b)(?<=\bJeff)/'/g

    $pop = s/(?<=\d)(?=(\d\d\d)+$)/,/g



    四中环视

    "(?<=……)"       肯定逆序环视        子表达式能够匹配左侧文本
    "(?<!……)"       否定逆序环视        子表达式不能匹配左侧文本
    "(?=……)"        肯定顺序环视        子表达式能够匹配右侧文本
    "(?!……)"        否定顺序环视        子表达式不能匹配右侧文本


    "\b" == "(?<!\w)(?=\w)|(?<=\w)(?!\w)"


    perl "<>" 操作符,在应用变量 $variable 时, "$variable=<>",这个有趣的结构能每次读入一行数据。输入的数据来自命令行中perl脚本之后的列出的文件名

    while($line = <>) {


    }
           





©️2020 CSDN 皮肤主题: 大白 设计师: CSDN官方博客 返回首页
实付0元
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值