Perl 中的正则表达式 扩展

在Perl中使用正则表达式处理文本
    $_ = "He's out bowling with Barney tonight.";
    s/Barney/Fred/;  # 将所有Barney替换为Fred
    print "$_/n";
s/文本替换可以在替换的同时引用原字符串
接上例:
    s/with (/w+)/against $1's team/;
    print "$_/n";  # says "He's out bowling against Fred's team tonight."
替换时将with后的单词留在了替换后的字符串中

替换的例子:
    $_ = "green scaly dinosaur";
    s/(/w+) (/w+)/$2, $1/;  # "scaly, green dinosaur"   替换了位置
    s/^/huge, /;            # "huge, scaly, green dinosaur"  使用锚点在开头添加
    s/,.*een//;             # "huge dinosaur"   删除特定模式字符串,注意删除的是,scaly,green是由于贪婪匹配
    s/green/red/;           # "huge dinosaur"   失败的匹配,字符串没有任何修改
    s//w+$/($`!)$&/;        # "huge (huge !)dinosaur"  把结尾的词替换成结尾的词之前的部分加括号和!
    s//s+(!/W+)/$1 /;       # "huge (huge!) dinosaur"  空格后跟!和非字母数字字符的模式去掉了前面的空格,后面加空格
    s/huge/gigantic/;       # "gigantic (huge!) dinosaur" 把huge替换为gigantic,这里只替换了第一个匹配的字串(非全局)

替换表达式的值在替换成功情况下为真,否则为假
    $_ = "fred flintstone";
    if (s/fred/wilma/) {
      print "Successfully replaced fred with wilma!/n";
    }

s///替换仅能替换第一个出现的模式
使用/g modifier
/g means Global Replacements 替换每一次匹配的模式
    $_ = "home, sweet home!";
    s/home/cave/g;
    print "$_/n";  # "cave, sweet cave!"

有用的短程序
    $_ = "Input  data/t may have    extra whitespace.";
    s//s+/ /g;  # Now it says "Input data may have extra whitespace."
使用s//s+/ /g 将所有连续空白都替换为单个空格字符

    s/^/s+//;   # 去除行首所有连续空白
    s//s+$//;    # 去除行末所有连续空白
    s/^/s+|/s+$//g;   # 去除行首和行末的连续空白

替换也允许自定义边界字符如
s#^https://#http://#; #把行首https://替换为http://
当成对使用时括号时应使用两对,对应匹配的模式和替换模式
    s{fred}{barney};
    s[fred](barney);
    s<fred>#barney#;
均为合法的形式

匹配中使用的modifier   /i, /x, 和 /s 同样有效, 使用顺序对效果没有影响
    s#wilma#Wilma#gi;    #把所有WiLmA或WILMA之类的单词替换为Wilma
    s{_ _END_ _.*}{  }s;    #把END标志及其后所有的行都清除掉

=~ 操作符指定替换的变量,在替换中仍然有效
    $file_name =~ s#^.*/##s;  # 把文件名中的路径信息去除掉


/U 用于将/U后的所有内容强制转换为大写
    $_ = "I saw Barney with Fred.";
    s/(fred|barney)//U$1/gi;  # $_ is now "I saw BARNEY with FRED."
/L 用于把/L后的所有内容强制转换为小写

这两个操作符默认影响所有在它们之后的内容,使用/E关闭大小写强制效果
s/(/w+) with (/w+)//U$2/E with $1/i; # $_ is now "I saw FRED with barney."


小写的/u和/l只影响紧接着它们的一个字符
大小写的/U /u等可以混用
例如:
    s/(fred|barney)//u/L$1/ig;  # $_ is now "I saw Fred with Barney."
无论这两个单词原来的大小写形式为何,都修改为大写首字母的形式

注意这些大小写操作符在print中同样有效
    print "Hello, /L/u$name/E, would you like to play a game?/n";
对变量$name进行操作,注意不要忘了用/E 关闭开关


split操作符 用于将字符串按照分隔符划分
除了使用逗号文件如CSV文件,对逗号分隔最好使用Text::CSV包来处理

@fields = split /separator/, $string;

例子
@fields = split /:/, "abc:def:g:h";  # 得到("abc", "def", "g", "h")

如果分隔符出现多次,会出现为值为undef的元素
@fields = split /:/, "abc:def::g:h";  # gives ("abc", "def", "", "g", "h")

在开始处出现的undef会被填进结果,而在末尾的不会,要小心!
@fields = split /:/, ":::a:b:c:::";  # gives ("", "", "", "a", "b", "c")末尾

没有undef的元素

根据空白字符切分的例子
    my $some_input = "This  is a /t        test./n";
    my @args = split //s+/, $some_input;  # ("This", "is", "a", "test.")
能够处理连续的空格字符

split操作符默认处理$_变量,默认根据空格字符切分
 my @fields = split;  # like split //s+/, $_;

当依据//s+/切分时,行首的空白字符并不会造成undef元素,小心!
如果需要把行首字符处理为undef,使用split ' ',$your_string;

与split相对的join操作
注意join的第一个参数是一个string不是正则表达式!!!
my $result = join $glue, @pieces;

例子
   my $x = join ":", 4, 6, 8, 10, 12;  # $x is "4:6:8:10:12"

连接符紧接在数组元素后面,当元素不足两个时,一个连接符都不会出现
    my $y = join "foo", "bar";       # $y的值"bar",没有连接符foo
    my @empty;                       # empty array
    my $empty = join "baz", @empty;  # $empty的值为空undef

可以综合使用split和join修改分隔符,如
    $x = "4:6:8:10:12"
    my @values = split /:/, $x;  # @values is (4, 6, 8, 10, 12)
    my $z = join "-", @values;   # $z is "4-6-8-10-12"

list语境下的match匹配
当match正则表达式用在数组语境下时
匹配成功,返回值是对应的match memeory, 匹配不成功, 则是一个空数组

    $_ = "Hello there, neighbor!";
    my($first, $second, $third) = /(/S+) (/S+), (/S+)/;
    print "$second is my $third/n";

/g modifier和m//也可以一起用,每次发现匹配都会放入数组中
    my $text = "Fred dropped a 5 ton granite block on Mr. Slate";
    my @words = ($text =~ /([a-z]+)/ig);
    print "Result: @words/n";
    # Result: Fred dropped a ton granite block on Mr Slate
text每次获得一个单词,然后放入words数组, 这段代码的功能与split相似,不同在于使

用正则表达式匹配确定内容而非分隔符

当存在多于一个括号时,匹配结果返回多个string
例子:
    my $data = "Barney Rubble Fred Flintstone Wilma Flintstone";
    my %last_name = ($data =~ /(/w+)/s+(/w+)/g);
通过使用多个()括号,last_name

哈希表被初始化为
{
Barney => Rubble,
Fred => Flintstone,
Wilma => Flintstone,
}


贪婪的数量修饰Greedy Quantifiers
+ * {0,5} 等数量修饰符匹配方式都是贪婪的(尽可能匹配最长)
例子: 用/fred.+barney/ 模式 去匹配fred and barney went bowling last night.
正则表达式引擎的工作方式(由于优化的存在,实际过程可能不同)
1. 首先匹配到了fred
2. .+贪婪匹配,一直匹配到了night.
3. barney发现已经在词尾了,无法匹配,.+匹配的位置向前移动寻找可能的匹配,直到成功或失败
这样的匹配方式涉及大量的回退.性能会受影响

非贪婪数量修饰, 在后面加?
+? 匹配一次或多次(非贪婪,匹配仅可能短的字符串)
使用贪婪和非贪婪数量修饰,在测试匹配结果上,没有任何不同

当预期.+分隔的两个模式间距离很近的时候,使用非贪婪模式有可能能够提高正则表达式引擎解析的效率.

但是使用memory时可能会造成非常大的不同!
例如:使用正则模式替换去除<BOLD>标记
s#<BOLD>(.*)</BOLD>#$1#g;
对I'm talking about the cartoon with Fred and <BOLD>Wilma</BOLD>! 能够正确作

用.
对I thought you said Fred and <BOLD>Velma</BOLD>, not <BOLD>Wilma</BOLD>
s#<BOLD>(.*)</BOLD>#$1#g处理的结果是匹配最长的<BOLD>和</BOLD>
留下了I thought you said Fred and Velma</BOLD>, not <BOLD>Wilma
不是预期的结果
改为s#<BOLD>(.*?)</BOLD>#$1#g;就能够正确匹配替换掉最接近的<BOLD>和</BOLD>
标记对

非贪婪模式的数量修饰符还有:
{5,10}?
{8,}?
?? 不太直观,匹配一次或多次,尽可能不匹配^_^
对于{3}这样指定重复次数的,加不加非贪婪符没有任何不同,最好不要使用{3}?,以免降低可读性

多行匹配

经典正则表达式匹配的只是单行字符串
Perl中m//能够匹配到多行的字符串

$_ = "I'm much better/nthan Barney is/nat bowling,/nWilma./n";
 print "Found 'wilma' at start of line/n" if /^wilma/b/im;
锚点^ $作用于整个字串的开头和结尾
/m选项
打开这个选项使得^和$两个锚点作用于字串中每一行的开头和结尾,而不是整个字串的开头和结尾

在s///替换中也可以使用/m选项
    open FILE, $filename
      or die "Can't open '$filename': $!";
    my $lines = join '', <FILE>; #逐行读入文件内容,将整个文件变成一个

字符串,
     #注意join的第一个参数是个空字符,这个文件不能太大
    $lines =~ s/^/$filename: /gm;
打开一个文件,并将文件名加入文件的每一行开始处.
这种做法非常没有效率,不能作为处理文件的常规方法!


较为复杂(也更实用)的例子:
处理多个文件,修改文件中的每一行
    #!/usr/bin/perl -w

    use strict;

    chomp(my $date = `date`);
    $^I = ".bak";

    while (<>) {
      s/^Author:.*/Author: Randal L. Schwartz/;
      s/^Phone:.*/n//;
      s/^Date:.*/Date: $date/;
      print;
    }

my $date = `date`; 获取系统当前日期
另一种方法是
my $date = localtime; 
打印出来的结果是:
The current date is: 02/01/2007 Thu
Enter the new date: (mm-dd-yy)
Thu Feb  1 14:13:21 2007

特殊变量$^I
当特殊变量$^I被设置成某个string时,这个string会被用来作为备份文件的扩展名
在上例中,假如我们传递了参数fred03.dat给perl程序
Perl首先打开文件fred03.dat,将其改名为fred03.dat.bak, 而后<>操作符创建一个新

文件名字叫fred03.dat,并把fred03.dat这个新文件作为默认输出,所有print都进入了

fred03.dat这个新文件.
创建新文件时,新文件的owner等属性都和源文件相同

$^I设置为~ 的方式,与 emacs backup的方式兼容
$^I也可以被设置为空字符串,这样就没有backup file

$^I最好不要被设置为空,这样你的正则表达式没有按预期工作时,仍然有干净的原文件可用.


Perl允许使用更简单的方式,在一行命令行中实现上述的功能:
$ perl -p -i.bak -w -e 's/Randall/Randal/g' fred*.dat

各参数解释意义如下:

-p参数, 生成循环读取每行的框架
    while (<>) {
      print;
    }
如果使用-n参数,则生成的程序没有while循环,仅有print语句

-i.bak
相当于$^I=".bak" 设置$^I变量

-w 打开所有warning

-e 选项后面的内容是Perlcode,使用-p时,这些语句被放入循环内,print前
可以使用-e输入多行perl代码, 注意每个-e后面的语句都必需要写分号,只有最后一个

分号可以被省略.

这行命令被解释为:
    #!/usr/bin/perl -w
    $^I = ".bak";
    while (<>) {
      s/Randall/Randal/g;
      print;
    }
输入参数为fred*.dat @ARGV保持了所有符合这个通配符的文件名 

将所有fred*.dat文件中的Randall修改成Randal

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值