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,感谢他们提出了宝贵的意见.

  • 相关阅读:
    day2
    day1
    ORM基本增删改查
    ORM
    url路由
    模板语言、url
    views视图
    django基本过程
    django基础
    Web框架
  • 原文地址:https://www.cnblogs.com/xiaopengren/p/3431870.html
Copyright © 2011-2022 走看看