美文网首页数据分析Perl 语言入门 Learning Perl
Learning Perl 学习笔记 Ch9 正则表达式(三)用

Learning Perl 学习笔记 Ch9 正则表达式(三)用

作者: sakam0to | 来源:发表于2019-05-29 16:39 被阅读0次
    1. 重新理解正则表达式,Perl中的正则表达式其实不是独立的功能,只是内嵌于某些功能中的子语言, m//就像是文本编辑器中的搜索功能,Perl同样提供了替换功能:s/正则表达式的模式/替换的内容/,如果没有用=~绑定对象,则默认替换的是预置变量$_
      demo9-1:
    #!/usr/bin/perl
    $_="This is a test.\n";
    if($result = s/is/was/){
      print $_;
    }
    print "result:$result";
    

    作为表达式,s///返回匹配结果的布尔值:

    ./demo9-1
    Thwas is a test.
    result:1
    

    • s///默认只会替换第一个匹配的位置,如果要实现全部替换,需要修饰符/g
      demo9-2:
    #!/usr/bin/perl
    $_="This is a test.\n";
    s/is/was/g;
    print $_;
    
     ./demo9-2
    Thwas was a test.
    

    m//中的修饰符在s///中同样可以使用,包括:
    /i:忽略大小写
    /s:让.匹配包括换行符在内的任意字符
    /x:允许在模式中插入任意空白和换行


    • m//一样,在s///中也可以使用捕获变量
      demo9-3:
    #!/usr/bin/perl
    $_="This is a test.\n";
    s/(\w+) (\w+) (\w+) (\w+)/$4 $3 $2 $1/;
    print $_;
    

    把捕获的变量逆序输出

    ./demo9-3
    test a is This.
    

    我们也可以把反向引用加进去:
    demo9-4:

    #!/usr/bin/perl
    $_="This is a test.\n";
    s/(is) \1/were/;
    print $_;
    

    匹配到is is

     ./demo9-4
    Thwere a test.
    

    命名捕获变量和自动捕获变量同样可以使用:
    demo9-5:

    #!/usr/bin/perl
    $_="This is a test.\n";
    s/(?<be>is)/_$+{be}_/;
    print $`."\n";
    print $&."\n";
    print $'."\n";
    print $_;
    
    ./demo9-5
    Th
    is
     is a test.
    
    Th_is_ is a test.
    

    • s///的定界符可以替换,如果是非成对定界符(比如#)只需要重复三次来分割圈引模式和圈引替换模式,但如果是成对定界符(比如{}[])就需要成对使用两次来区分圈引模式和圈引替换模式:
      s#abc#def#
      s{abc}[def]
      虽然 #也被用来表示注释, 但如果Perl解析器在期待一个定界符,就不会把 #视作注释的开头)

    • s///的圈引替换模式中,有一组特殊的转义字符可以实现大小写转换:
      \U:之后的字符全部转换成大写
      \L:之后的字符全部转换为小写
      \E:结束\U\L的作用范围
      \u:之后的第一个字符大写
      \l:之后的第一个字符小写
      可以将\u\L或者\l\U连用实现首字母大写或首字母小写的功能:
      demo9-6
    #!/usr/bin/perl
    $_="This is a test.\n";
    print $_;
    s/(?<be>test)/\u\L$+{be}/;
    print $_;
    
    ./demo9-6
    This is a test.
    This is a Test.
    

    大小写转义字符的规则并不是s///专属的,任何双引号内的字符串都适用此规则
    demo9-7

    #!/usr/bin/perl
    $_="This is a \u\Ltest.\n";
    print $_;
    s/(?<be>test)/\l\Utest/i;
    print $_;
    
    ./demo9-7
    This is a Test.
    This is a tEST.
    


    1. 另一个使用正则表达式的是@result = split //,$string,操作符split会根据分隔符(写成模式)将一个字符串截断成子字符串列表。
      demo9-8:
    #!/usr/bin/perl
    $input = "Zhao:Qian:Sun:LI:Zhou:Wu:Zheng:Wang";
    foreach (split /:/, $input){
      print $_."\n";
    }
    

    分隔符自己不会出现在列表中

    ./demo9-8
    Zhao
    Qian
    Sun
    LI
    Zhou
    Wu
    Zheng
    Wang
    

    • 如果字符串中两个分隔符连着就会产生空字段,但是Perl会忽略末尾的空字段:
      demo9-9:
    #!/usr/bin/perl
    $input = "::Zhao::Qian::Sun::LI::Zhou::Wu::Zheng::Wang::";
    foreach (split /:/, $input){
      print $_."\n";
    }
    

    在原来的每个字段之间和前后都加了额外的分隔符,但是结尾的空字段被舍弃了

    ./demo9-9
    
    
    Zhao
    
    Qian
    
    Sun
    
    LI
    
    Zhou
    
    Wu
    
    Zheng
    
    Wang
    

    • split的默认行为是用空白\s+分割存放于$_内的字符串:
      demo9-10:
    #!/usr/bin/perl
    $_ = " Zhao Qian Sun LI Zhou Wu Zheng Wang ";
    foreach (split){
      print $_."\n";
    }
    

    稍有不同之处在于所有空字段都被舍弃(因为连续的空白字符本身就匹配了\s+模式,所以不存在字符串内部的空字段):

    ./demo9-10
    Zhao
    Qian
    Sun
    LI
    Zhou
    Wu
    Zheng
    Wang
    

    标准的写法可能是 split /\s+/, $_;
    demo9-11:

    #!/usr/bin/perl
    $_ = " Zhao Qian Sun LI Zhou Wu Zheng Wang ";
    foreach (split /\s+/, $_){
      print $_."\n";
    }
    

    这样则可以保留字符串前面的空字段,别忘了连续的空白字符本身就匹配了\s+模式,所以不存在字符串内部的空字段

    ./demo9-11
    
    Zhao
    Qian
    Sun
    LI
    Zhou
    Wu
    Zheng
    Wang
    

    • split操作符功能相反的是join函数(没错,split是操作符,但是join是函数),不过join并没有使用模式:
      $result = join $glue, @pieces;
      join函数的功能是把@pieces中的字符串片段$glue胶水粘合成一个字符串$result, $glue保存着充当胶水的字符串而不是模式
      join可以和split结合实现替换功能,效果类似s///g:
      demo9-12:
    #!/usr/bin/perl
    $_ = " Zhao Qian Sun LI Zhou Wu Zheng Wang ";
    @pieces = split;
    print join "&", @pieces;
    print "\n";
    

    &替换了空格

    ./demo9-12
    Zhao&Qian&Sun&LI&Zhou&Wu&Zheng&Wang
    


    1. 列表上下文中使用m//会得到有趣的新特性:如果模式匹配成功,会返回所有捕获变量的列表
      demo9-13:
    #!/usr/bin/perl
    $_ = "Zhang san   Li si  Wang wu  ";
    @pieces = /([A-Z][a-z]+)/g;
    foreach (@pieces){
      print $_."\n";
    }
    

    捕获了([A-Z][a-z]+)首字母大写其余字母小写的单词:

    Zhang
    Li
    Wang
    

    如果有多个捕获模式就会返回多个捕获串:
    demo9-14:

    #!/usr/bin/perl
    $_ = "Zhang san   Li si  Wang wu  ";
    @pieces = /([A-Z][a-z]+)\s+(\w+)/g;
    foreach (@pieces){
      print $_."\n";
    }
    

    返回的列表里包含了所有捕获串

    ./demo9-14
    Zhang
    san
    Li
    si
    Wang
    wu
    

    特别地,如果捕获的模式恰好是两个,则可以用哈希结构存储m//返回的结果,这种情况下,第一个捕获的变量是哈希的键,第二个捕获的是哈希的值:
    demo9-15:

    #!/usr/bin/perl
    $input = "Zhang san   Li si  Wang wu  ";
    %hash = ($input =~ /([A-Z][a-z]+)\s+(\w+)/g);
    foreach (keys %hash){
      print $_."=>".$hash{$_}."\n";
    }
    
    ./demo9-15
    Wang=>wu
    Li=>si
    Zhang=>san
    


    1. 正则表达式中的量词:*, +, ?, {M,N}默认都是贪婪量词,即会尽可能多的匹配,总会匹配到最大的字符串,而如果想要使用非贪婪量词,只需要在原来的量词后面加上?*?, +?, ??, {M,N}?,这种情况下会尽可能少的匹配:
      demo9-16:
    #!/usr/bin/perl
    $_ = "TAG a b c d ef ghi jk TAG lmn opq TAG rst uvw xyz TAG";
    if(m/TAG (.+) TAG/){
         print "Greedy:".$&."\n";
    }
    if(m/TAG (.+?) TAG/){
         print "Non-greedy:".$&."\n";
    }
    
     ./demo9-16
    Greedy:TAG a b c d ef ghi jk TAG lmn opq TAG rst uvw xyz TAG
    Non-greedy:TAG a b c d ef ghi jk TAG
    

    1. 使用钻石操作符的新特性,结合正则表达式,使用Perl来修改文件:
      $^I变量中是字符串时,将赋予钻石操作符<>新的特性:首先将@ARGV中字符串对应的文件改名,加上后缀,后缀内容即是$^I内存储的值,修改输入指向这个文件,然后打开一个新文件,命名为原来的文件名,修改输出到这个新文件。
      demo9-17:
    #!/usr/bin/perl -w 
    use strict;
    $^I=".bak";
    chomp(my $date = `date`);
    while(<>){
        s/Date:.*/Date:$date/;
        print;
    }
    

    另外创建一个文件写上“Date:”, 则每次运行程序,都会把Date后的时间更新为当前时间

    echo "Date: " > test
    ./demo9-17 test
    

    这时目录下生成一个test.bak文件,存储着修改前的内容。 while循环里每次都会从test.bak里读取一行放到$_里,然后用s///修改后,打印到test文件里(输出已经被<>重定向到新文件里了)
    如果$^I为空,则修改就会直接作用在源文件上


    因为这个程序太普遍,所以有了它的简化版本:
    perl -p -i.bak -w -e 'chomp(my $date=`date`);' -e 's/Date:.*/Date:$date/' tes*
    可以直接在命令行执行,作用和 demo9-17相同
    -p选项让perl自动生成一段类似下面这样的小程序:

    while(<>){
    print;
    }
    

    -e后面跟着程序代码,会插入到while循环之中
    -i.bak$^I=".bak"的作用相同,如果只有-i那就像是把$^I变量置为空,则不会有备份动作发生。
    最后一个参数表示设定@ARGV的值,这里使用了通配符

    相关文章

      网友评论

        本文标题:Learning Perl 学习笔记 Ch9 正则表达式(三)用

        本文链接:https://www.haomeiwen.com/subject/jttszqtx.html