zoukankan      html  css  js  c++  java
  • perlretut

    简单的单词匹配

    最简单的正则就是一个单词,或者更通用一点,一个字符序列.一个仅包含某个单词的正则可以匹配包含那个单词的任意字符串:

    "Hello World" =~ /World/; # 匹配成功

    在这条语句中,World就是一个正则,/World/两边的双斜杠//告诉Perl这是一个正则表达式.运算符=~会把任意的字符串传递给该正则表达式,如果正则匹配成功,则返回真,如果匹配失败,则返回假.在上面的例子中,World刚好匹配了字符串"Hello World"中的第二个单词,所以该表达式返回的值为真.

    类似的这种表达式经常会被用在条件判断语句中:

    print "It matches
    " if "Hello World" =~ /World/;

    还可以使用!~运算符来表示相反的意思,也就是"不匹配":

    print "It doesn't match
    " if "Hello World" !~ /World/;

    正则表达式中包含的字符串直接量还可以使用变量来代替:

    $greeting = "World";
    print "It matches " if "Hello World" =~ /$greeting/;

    如果你要匹配的字符串存放在$_里,则语句中的$_ =~部分可以省略:

    $_ = "Hello World";
    print "It matches " if /World/;

    另外,正则默认的分隔符//还可以换成其他任意配对的符号,不过必须要在前面加上'm':

    "Hello World" =~ m!World!;   # 匹配成功,分隔符为'!'
    "Hello World" =~ m{World}; # 匹配成功,分隔符为'{}'
    "/usr/bin/perl" =~ m"/perl"; # 匹配成功,分隔符为'"',斜杠/不再是分隔符,而是正则中的一个普通字符

    正则必须要严格匹配目标字符串中的每个字符才算匹配成功:

    "Hello World" =~ /world/;  # 不匹配,字符w大小写不同
    "Hello World" =~ /o W/; # 匹配成功,空格' '也是一个普通字符
    "Hello World" =~ /World /; # 不匹配,字符串末尾没有空格' '

    Perl会尽可能早的在字符串最左边发生匹配:

    "Hello World" =~ /o/;       # 匹配了'Hello'中的'o'
    "That hat is red" =~ /hat/; # 匹配了'That'中的'hat'

    在匹配目标字符串的过程中,并不是所有的字符都匹配它们本身.有一些字符,称之为元字符,它们属于正则表达式表示法中的保留字符,拥有特殊意义.元字符一共有下面这些:

    {}[]()^$.|*+?

    想要匹配一个元字符本身,可以使用反斜杠来转义这个元字符:

    "2+2=4" =~ /2+2/;                       # 匹配失败,+是一个元字符
    "2+2=4" =~ /2+2/; # 匹配成功,+可以匹配普通的+号
    'C:WIN32' =~ /C:\WIN/; # 匹配成功
    "/usr/bin/perl" =~ //usr/bin/perl/; # 匹配成功

    在最后一个正则中,斜杠'/'前面也必须使用反斜杠转义,因为斜杠已经用作了正则分隔符.

    在正则中,不可打印的ASCII字符可以使用转义序列来表示.常用的例子有:用t来表示一个制表符,用n来表示一个换行符,用r来标识一个回车符.除此之外,任意的字节都可以使用八进制转义序列,比如033,或者16进制转义序列,比如x1B来表示:

    "1000	2000" =~ m(0	2)      # 匹配成功
    "cat" =~ /143x61x74/ # 匹配成功,使用了ASCII码转义序列

    正则在很多表现上类似于用双引号包围的字符串,比如也可以进行变量内插:

    $foo = 'house';
    'cathouse' =~ /cat$foo/; #匹配成功
    'housecat' =~ /${foo}cat/; #匹配成功

    在上面所有的这些例子中,只要正则匹配了目标字符串中的某一部分,就可以算作是匹配成功.如果想要限制匹配具体发生的位置,我们可以使用锚点元字符 ^$.锚点^匹配的是字符串的开头位置,锚点$匹配的是字符串的结束位置,或者字符串尾部的换行符之前的位置.下面是一些例子:

    "housekeeper" =~ /keeper/;         #匹配成功
    "housekeeper" =~ /^keeper/; #匹配失败
    "housekeeper" =~ /keeper$/; #匹配成功
    "housekeeper " =~ /keeper$/; #匹配成功
    "housekeeper" =~ /^housekeeper$/; #匹配成功

    使用字符类

    一个字符类可以匹配一系列可能匹配的字符,而不仅仅是单个字符.字符类使用一个方括号[...]来表示,一系列可能匹配的字符都放在这个方括号里面.下面是一些例子:

    /cat/;            # 匹配'cat'
    /[bcr]at/; # 匹配'bat','cat',或'rat'
    "abc" =~ /[cab]/; # 匹配'a'

    在上面最后一条语句中,虽然'c'是字符类中的第一个字符,但该正则最先匹配到的是目标字符串最左边的字符'a' .

    /[yY][eE][sS]/; # 忽略大小写匹配'yes',即是'yes','Yes','YES',等.
    /yes/i; # 效果同上

    第二个正则使用了'i'修饰符,该修饰符的作用是让匹配忽略大小写.

    字符类的字符中也有普通字符和特殊字符(元字符)之分,但字符类中的普通字符和特殊字符与字符类外部的不同.字符类中的特殊字符有这些:-]^$,想要匹配它们本身必须使用反斜杠转义:

    /[]c]def/; # 匹配']def'或'cdef'
    $x = 'bcr';
    /[$x]at/; # 匹配'bat,'cat',或'rat'
    /[$x]at/; # 匹配'$at'或'xat'
    /[\$x]at/; # 匹配'at','bat,'cat',或'rat'

    在字符类中,特殊字符'-'作为了范围运算符,利用范围运算符,冗长的[0123456789][abc...xyz]可以精简成[0-9][a-z] :

    /item[0-9]/;    # 匹配'item0'或...或'item9'
    /[0-9a-fA-F]/; # 匹配一个十六进制数字

    但如果'-'字符放在了字符类的开头处或末尾处,该字符就会被当作一个普通字符来看待.

    字符^只有放在字符类的开头处才算是一个特殊字符,它代表的是否定字符类,效果是让该字符类能够匹配那些所有不在该字符类内部的字符.普通字符类[...]和否定字符类[^...]都必须匹配一个有效的字符,不能什么也不匹配.下面是例子:

    /[^a]at/;  # 不匹配'aat'或'at',但匹配其他所有的类似'bat','cat,'0at','%at',等.
    /[^0-9]/; # 匹配一个非数值字符
    /[a^]at/; # 匹配'aat'或'^at',这里'^'是个普通字符

    Perl支持一些常用字符类的简写形式.(下面讲的这些简写形式的意义都得在该正则开启了ASCII安全模式(使用/a修饰符)的前提下才正确,否则这些简写形式也能匹配非ASCII的Unicode字符.查看perlrecharclass文档中的反斜杠序列了解详情.)

    • d 代表一个数字字符,相当于:

      [0-9]
    • s 代表一个空白符,相当于:

      [ 	
      f]
    • w 代表一个单词字符(字母,数字,下划线),相当于:

      [0-9a-zA-Z_]
    • D 是d的否定形式,它代表任何一个非数字字符,相当于:

      [^0-9]
    • S 是s的否定形式,它代表任何一个非空白符字符,相当于:

      [^s]
    • W 是w的否定形式,它代表任何一个非单词字符,相当于:

      [^w]
    • 点号'.'匹配任何一个非换行符" "的字符.相当于:

      [^
      ]

     dswDSW这些简写形式可以使用在字符类外部,也可以使用在字符类内部.下面是一些例子:

    /dd:dd:dd/; # 匹配hh:mm:ss时间格式
    /[ds]/; # 匹配任何一个数字或空白符
    /wWw/; # 匹配一个单词字符,后跟一个非单词字符,再跟一个单词字符
    /..rt/; # 匹配任意两个非换行符的字符,后跟'rt'
    /end./; # 匹配'end.'
    /end[.]/; # 同上,匹配'end.'

    单词锚点b匹配一个单词边界,也就是一个单词字符和非单词字符wWWw中间的那个位置,比如:

    $x = "Housecat catenates house and cat";
    $x =~ /cat/; # 匹配'catenates'中的cat
    $x =~ /cat/; # 匹配'housecat'中的cat
    $x =~ /cat/; # 匹配字符串结尾处的'cat'

    在最后一个例中,字符串结尾处的位置也是一个单词边界.

    匹配这个或那个

    我们可以使用多选元字符'|'来匹配多个可能的字符序列.比如想匹配dogcat,我们可以用正则dog|cat.和前面一样,Perl会从目标字符串的最左边开始进行匹配.在每个字符处,Perl会首先尝试匹配第一个选项dog.如果dog不匹配,再尝试下一个选项cat.如果cat也不匹配,则匹配失败,Perl会开始尝试目标字符串中的下个字符.下面是一些例子:

    "cats and dogs" =~ /cat|dog|bird/;  # 匹配"cat"
    "cats and dogs" =~ /dog|cat|bird/; # 匹配"cat"

    在第二个正则中,即使dog是第一个选项,但cat还是最先匹配了字符串最左边的cats.

    "cats"          =~ /c|ca|cat|cats/; # 匹配"c"
    "cats" =~ /cats|cat|ca|c/; # 匹配"cats"

    在目标字符串中的某个字符位置处,会优先匹配正则多选分支中的第一个选项,如果匹配成功,则继续匹配下面的正则,如果匹配失败,才尝试下个选项.

    分组和层次匹配

    分组元字符()可以让正则中的一部分内容被当作一个单元来对待.比如正则house(cat|keeper)会匹配一个house后跟一个catkeeper.下面有更多的例子:

    /(a|b)b/;                # 匹配'ab'或'bb'
    /(^a|b)c/; # 匹配字符串开头的'ac'或者任意位置的'bc'
    /house(cat|)/; # 匹配'housecat'或'house'
    /house(cat(s|)|)/; # 匹配'housecats'或'housecat'或'house'.分组也可以嵌套.
    "20" =~ /(19|20|)dd/; # 匹配了最后一个空选项'()dd',因为'20dd'不匹配

    提取匹配内容

    我们还可以利用分组元字符()提取出目标字符串中自己感兴趣的那部分内容.每个分组匹配的内容都会被存储到一个特殊变量$1,$2,等中.可以像普通变量一样使用它们:

    # 提取出时,分,秒
    $time =~ /(dd):(dd):(dd)/; # 匹配hh:mm:ss格式
    $hours = $1;
    $minutes = $2;
    $seconds = $3;

    在列表上下文中,正则的匹配操作会返回一个包含了所有分组对应的匹配变量的列表($1,$2,...).所以我们可以把上面的代码改写为:

    ($hours, $minutes, $second) = ($time =~ /(dd):(dd):(dd)/);

    如果正则中存在嵌套分组,则$1对应了最左边的那个左括号所属的分组, $2则对应了第二个左括号所在的分组,等等.也就是说,无论分组有没有嵌套,一个分组的序号是几只看该分组左括号的位置.例如,下面是个比较复杂的正则,分组序号已经标示了出来:

    /(ab(cd|ef)((gi)|j))/;
    1 2 34

    还有一种和匹配变量$1 , $2 , ...相关联的值,称之为反向引用g1 , g2 , ... 反向引用是用在正则内部的匹配变量:

    /(www)sg1/; # 寻找字符串中类似'the the'的字符序列

    需要注意的是:$1 , $2 , ... 应该只在正则外部使用,g1 , g2 , ...应该只在正则内部使用.

    重复匹配

    可以使用量词元字符?, * , + ,和{}来重复正则中的某一部分内容.量词必须紧跟在一个字符,字符类,或者分组的后面.它们各自的意义如下:

    • a? = 匹配'a'1次或0次

    • a* = 匹配'a'0次或更多次,也就是任意次

    • a+ = 匹配'a'1次或更多次,也就是至少一次

    • a{n,m} = 匹配'a'至少n次,最多m次.

    • a{n,} = 匹配'a'至少n

    • a{n} = 匹配'a'n次,不多不少.

    下面是一些例子:

    /[a-z]+s+d*/;                # 匹配一个至少包含一个字符的单词,后跟一个至少包含一个字符的连续空白符,再跟一个任意长度甚至是0位的数字
    /(w+)s+g1/; # 匹配两个任意长度的重复单词
    $year =~ /^d{2,4}$/; # 确保变量year的值是一个2到4位的数字
    $year =~ /^d{4}$|^d{2}$/; # 这个更准确一点,因为年份不需要3位数字

    这些量词总会尽可能多的匹配字符串中的字符.例如:

    $x = 'the cat in the hat';
    $x =~ /^(.*)(at)(.*)$/; # 匹配完成后,
    # $1 = 'the cat in the h'
    # $2 = 'at'
    # $3 = '' (空匹配)

    第一个.*一直匹配到了最后一个at的左边,第二个.*没有字符可以匹配了,所以它匹配了0次.

    更多匹配

    还有一些关于匹配运算符的东西你需要知道.那就是全局修饰符//g可以让匹配运算符在同一个字符串上匹配多次.在标量上下文中,如果该正则拥有//g修饰符,则在第一次匹配成功后,下次匹配会从目标字符串中上次成功匹配的结束索引位置开始匹配.而且你还可以使用pos()函数读取或修改目标字符串中上次成功匹配的索引位置.比如,下面的例子:

    $x = "cat dog house"; # 3个单词
    while ($x =~ /(w+)/g) {
    print "Word is $1, ends at position ", pos $x, " ";
    }

    打印出:

    Word is cat, ends at position 3
    Word is dog
    , ends at position 7
    Word is house
    , ends at position 13

    一次失败的匹配或者改变目标字符串会重置当前索引位置.如果你不想在匹配失败后重置当前位置,可以使用//c修饰符,也就成了/regex/gc .

    在列表上下文中,//g会返回所有的匹配分组,如果正则中没有分组,则返回整个正则的匹配结果组成的列表,比如:

    @words = ($x =~ /(w+)/g);  # matches,
    # $word[0] = 'cat'
    # $word[1] = 'dog'
    # $word[2] = 'house'

    搜索和替换

    在Perl中,字符串的搜索和替换使用s/regex/replacement/modifiers这样的形式.其中replacement是一个双引字符串,目标字符串中所有被regex匹配的子字符串都会被replacement代替.这里用来关联目标字符串和s///的运算符仍然是=~,如果需要匹配的目标字符串存储在$_中,那么$_ =~可以省略掉.如果匹配成功,s///运算符会返回总共替换的次数,否则返回假.下面是一些例子:

    $x = "Time to feed the cat!";
    $x =~ s/cat/hacker/; # $x的值变成了"Time to feed the hacker!"
    $y = "'quoted words'";
    $y =~ s/^'(.*)'$/$1/; # 删除掉单引号,$y的值成了"quoted words"

    使用s///运算符,匹配变量$1,$2,等可以直接用在replacement中.如果正则拥有全局修饰符,s///g会搜索并替换掉源字符串中所有的匹配项:

    $x = "I batted 4 for 4";
    $x =~ s/4/four/; # $x的值成了"I batted four for 4"
    $x = "I batted 4 for 4";
    $x =~ s/4/four/g; # $x的值成了"I batted four for four"

    无损修饰符s///r会让替换的结果作为返回值返回而不会修改原变量中的值:

    $x = "I like dogs.";
    $y = $x =~ s/dogs/cats/r;
    print "$x $y "; # 打印出"I like dogs. I like cats."

    $x = "Cats are great.";
    print $x =~ s/Cats/Dogs/r =~ s/Dogs/Frogs/r =~ s/Frogs/Hedgehogs/r, " ";
    #打印出"Hedgehogs are great."

    @foo = map { s/[a-z]/X/r } qw(a b c 1 2 3);
    # @foo现在的值为qw(X X X 1 2 3)

    求值修饰符s///e会将替换字符串隐式的传入eval{...}来求值,返回的结果才作为最终的替换字符串.下面是一些例子:

    # 将字符串中所有的单词都反向拼写
    $x = "the cat in the hat";
    $x =~ s/(w+)/reverse $1/ge; # $x的值成了"eht tac ni eht tah"

    # 将百分号转换成十进制数

    $x = "A 39% hit rate";
    $x =~ s!(d+)%!$1/100!e; # $x的值成了"A 0.39 hit rate"

    上面的最后一个例子演示了s///运算符还可以使用其他的分隔符,比如s!!!s{}{},甚至s{}//.如果使用了单引号s''',则正则和替换字符串都会被当作单引字符串,也就不能进行变量内插.

    split运算符

    split /regex/, string可以将string分割到一个子字符串列表中,然后返回这个列表.参数中的正则表达式可以指定应该拿什么字符序列来分割源字符串string.例如,想将一个字符串分割成多个单个的单词,使用:

    $x = "Calvin and Hobbes";
    @word = split /s+/, $x; # $word[0] = 'Calvin'
    # $word[1] = 'and'
    # $word[2] = 'Hobbes'

    分割一个由逗号分割的数字列表,使用:

    $x = "1.618,2.718,   3.142";
    @const = split /,s*/, $x; # $const[0] = '1.618'
    # $const[1] = '2.718'
    # $const[2] = '3.142'

    如果分割符使用了空正则//,则源字符串会被分割为一个个的字符.如果正则中包含分组,那么返回的结果列表中还会包含分组中匹配的内容:

    $x = "/usr/bin";
    @parts = split m!(/)!, $x; # $parts[0] = ''
    # $parts[1] = '/'
    # $parts[2] = 'usr'
    # $parts[3] = '/'
    # $parts[4] = 'bin'

    因为$x的第一个字符就匹配了分隔符中指定的正则,所以split返回的列表中的第一个元素是个空字符串.

    BUGS

    无.

    相关链接

    这仅仅是一个快速入门指南,想要一份更深入的正则表达式教程,请参阅perlretut,以及完整的参考perlre.

    作者和版权

    Copyright (c) 2000 Mark Kvale All rights reserved.

    This document may be distributed under the same terms as Perl itself.

    致谢

    笔者在此要感谢Mark-Jason Dominus, Tom Christiansen, Ilya Zakharevich, Brad Hughes, 以及Mike Giroux,感谢他们提出了宝贵的意见.

  • 相关阅读:
    火狐插件火狐黑客插件将Firefox变成黑客工具的七个插件
    memcache安装环境:WINDOWS 7
    PHP正则表达式
    968. 监控二叉树 力扣(困难) dfs 官方说DP
    375. 猜数字大小 II 力扣(中等) 区间动态规划、记忆化搜索
    629. K个逆序对数组 力扣(困难) 区间动态规划
    剑指 Offer 51. 数组中的逆序对 力扣(困难) 巧用归并排序算法
    488. 祖玛游戏 力扣(困难) dfs
    16. 最接近的三数之和 力扣(中等) 双指针
    319. 灯泡开关 力扣(中等) 数论
  • 原文地址:https://www.cnblogs.com/xiaopengren/p/3431870.html
Copyright © 2011-2022 走看看