正则表达式

October 26, 2013 @ 10:06 AM

正则表达式比此处所介绍的主题要大得多,确信你理解了这些概念。对于教程,可以参阅 perlrequickperlretut。而权威的文档,则能够参阅perlre

匹配和替换返回数量

m//s/// 操作符分别返回匹配或替换的数目。你既可以直接使用该数目,也可以检查其真值。

  1. if ( $str =~ /Diggle|Shelley/ ) {
  2. print "We found Pete or Steve!\n";
  3. }
  4. if ( my $n = ($str =~ s/this/that/g) ) {
  5. print qq{Replaced $n occurrence(s) of "this"\n};
  6. }

不要在未检查匹配成功的情况下使用捕获变量

除非匹配成功,捕获变量 $1 等是无效的,并且它们不会被清理。

  1. # BAD: Not checked, but at least it "works".
  2. my $str = 'Perl 101 rocks.';
  3. $str =~ /(\d+)/;
  4. print "Number: $1"; # Prints "Number: 101";
  5. # WORSE: Not checked, and the result is not what you'd expect
  6. $str =~ /(Python|Ruby)/;
  7. print "Language: $1"; # Prints "Language: 101";

你必须检查匹配的返回值:

  1. # GOOD: Check the results
  2. my $str = 'Perl 101 rocks.';
  3. if ( $str =~ /(\d+)/ ) {
  4. print "Number: $1"; # Prints "Number: 101";
  5. }
  6. if ( $str =~ /(Python|Ruby)/ ) {
  7. print "Language: $1"; # Never gets here
  8. }

常用匹配选项

/i:不区分大小写

/g:匹配多次

  1. $var = "match match match";
  2. while ($var =~ /match/g) { $a++; }
  3. print "$a\n"; # prints 3
  4. $a = 0;
  5. $a++ foreach ($var =~ /match/g);
  6. print "$a\n"; # prints 3

/m:更改 ^ 和 $ 的意义

正常情况下,^ 意为字符串的开头,而 $ 为字符串的结尾。/m使它们分别意为行首和行尾。

  1. $str = "one\ntwo\nthree";
  2. @a = $str =~ /^\w+/g; # @a = ("one");
  3. @b = $str =~ /^\w+/gm; # @b = ("one","two","three")

不管是否有 /m,使用 \A\z 来匹配字符串的开头和结尾。\Z除了会忽略最后的换行之外,与 \z 相同,

/s:使 . 也匹配换行

  1. $str = "one\ntwo\nthree\n";
  2. $str =~ /^(.{8})/s;
  3. print $1; # prints "one\ntwo\n"

捕获变量 $1 及之友

捕获括号对的内容被存储到数字变量中。括号从左到右分配:

  1. my $str = "abc";
  2. $str =~ /(((a)(b))(c))/;
  3. print "1: $1 2: $2 3: $3 4: $4 5: $5\n";
  4. # prints: 1: abc 2: ab 3: a 4: b 5: c

捕获括号及变量的数目没有上限。

利用 ?: 避免捕获

如果括号后紧接着 ?:,那么该分组不会被捕获。在你不想保存匹配的内容时会有用:

  1. my $str = "abc";
  2. $str =~ /(?:a(b)c)/;
  3. print "$1\n"; # prints "b"

利用 /x 选项使正则表达式更易读

如果你在使用正则表达式时玩了些花样,那么为它写注释。你可以使用 /x选项达到目的。

丑陋的庞然大物:

  1. my ($num) = $ARGV[0] =~ m/^\+?((?:(?<!\+)-)?(?:\d*.)?\d+)$/x;

使用 /x 允许的空白和注释更可读:

  1. my ($num) =
  2. $ARGV[0] =~ m/^ \+? # An optional plus sign, to be discarded
  3. ( # Capture...
  4. (?:(?<!\+)-)? # a negative sign, if there's no plus behind it,
  5. (?:\d*.)? # an optional number, followed by a point if a decimal,
  6. \d+ # then any number of numbers.
  7. )$/x;

除非被转义,空白和注释将被去除。

利用 \Q 和 \E 自动引起正则表达式

这会自动转义正则表达式的元字符。不会转义 $ 符号。

  1. my $num = '3.1415';
  2. print "ok 1\n" if $num =~ /\Q3.14\E/;
  3. $num = '3X1415';
  4. print "ok 2\n" if $num =~ /\Q3.14\E/;
  5. print "ok 3\n" if $num =~ /3.14/;

输出:

  1. ok 1
  2. ok 3

对 s/// 使用 /e 选项来执行代码

这将允许任意代码替换正则表达式中的字符串。

  1. my $str = "AbCdE\n";
  2. $str =~ s/(\w)/lc $1/eg;
  3. print $str; # prints "abcde"

必要时使用 $1 及之友。

了解何时使用 study

study 在多数情况下都无用。它所做的是创建一个每个单字节字符首次出现在字符串中的位置的表。这意味着如果你有 1,000 个字符长的字符串,你要寻找由一个常量字符开头的各种字符串,匹配器可以立即跳转到正确的位置。例如:

  1. "This is a very long [... 900 characters skipped...] string that I have here,
  2. ending at position 1000"

现在,如果你要匹配正则表达式 /Icky/,匹配器将试图寻找第一个匹配的字母 I。在找到它之前得扫描前面的 900+ 个字符。但 study 创建了一个 256 个字节第一次出现的地方的表。所以扫描器能够立即跳转到那个位置来开始匹配。

译注:这里没有考虑到多字节字符。

使用 re => debug 调试正则表达式

  1. -Mre=debug