Perl正则表达式在已经匹配前一个正则表达式

时间:2016-06-09 20:21:32

标签: regex perl

请帮我弄清楚为什么以下不能像我期望的那样工作。我应该能够跳过所有不匹配的数组项,然后匹配任何通过的数组项。相反,我必须制作for循环变量$ server的副本,并在它与一个正则表达式匹配后匹配它并让它通过。变量$ server仍然包含相同的字符串,我希望能够匹配它到第二个正则表达式:

use strict;
use warnings;
use diagnostics;

@servers = ('server01', 'server02', 'vm13', 'vm02');

for my $server ( @servers) {

if ($server !~ m/server01|vm13|vm02/ig ) {

    next;

} else {
    say $server;  # It will print string that contains 
                  # server01, vm13, or vm02

    if ($server =~ m/server01/ig) {

        say $server # It will not print string that 
                    # contains server01 here
    }

    say $server, " again..."; # The variable still works here
}

这种方式有效:

use strict;
use warnings;
use diagnostics;

@servers = ('server01', 'server02', 'vm13', 'vm02');

for my $server (@servers) {

my $server_copy = $server;

if ($server !~ m/server01|vm13|vm02/ig ) {

    next;

} else {

    say $server;  # It will print the name of the server 
                    # that contains server01, vm13, or vm02

    if ($server_copy =~ m/server01/ig) {
        say $server # It now prints the name of that server
    }

    say $server, " again..."; # The variable still works here
}

任何帮助都将不胜感激。

3 个答案:

答案 0 :(得分:3)

更新模块上的已更正语句:any已添加到Perl 5.20中的List::Util

简而言之,它是正则表达式中导致此行为的全局修饰符/g

来自perlretut全球匹配

  

在标量上下文中,对字符串的连续调用将使// g从匹配跳转到匹配,跟踪字符串中的位置

当它记住它的最后位置时,它只会尝试在下一次将字符串中的匹配下来,如choroba的回答所述。这个reagrd中一个非常有用的工具是use re qw(debug);,您可以通过它详细了解正则表达式正在做什么。

我也改变了一些代码。

use strict;
use warnings;
use feature qw(say);
use diagnostics;

my @servers = ('server01', 'server02', 'vm13', 'vm02');

foreach my $server (@servers) {

    next if not $server =~ m/server01|vm13|vm02/i;

    say $server;  # Prints string with either server01, vm13, or vm02

    if ($server =~ m/server01/i) {
        say "Looking for: $server";
    }   

    say "$server, again..."; # The variable still works here
}   

如果要保留的服务器列表很长,您可以使用核心List::Util模块as of Perl 5.20模块中的any。)请参阅注意。结束以前的版本。

use List::Util qw(any);

if (not any { /$server/ } @keep_servers) {
    say "Skipping $server";
    next;
}

当然还有其他方法来操纵数组。特别是,如Borodin所述,可以很好地使用同一模块none中的if (none { /$server/ } @keep servers) { ... }

如果需要跳过它们,那么您当然可以简单地循环遍历@keep_servers。这样的列表可以构造为例如

my @keep_servers = grep { not /server02/ } @servers;

如果您知道要删除哪个列表并且它们形成的列表比保留的列表短得多,这可能是合适的。

注意使用Perl 5.20之前的版本,allanynonenotall中的函数可以在{{3}中找到虽然简单的实现显示在List::Util docs中。

答案 1 :(得分:2)

由于/g标志而导致的。它会记住上次匹配的位置,并在下次尝试从该位置开始匹配。您可以使用字符串server01_server01对其进行验证 - 它将被打印,因为它与正则表达式匹配两次。

如果您不需要,请移除/g标记。

答案 2 :(得分:1)

更改此行

if ($server_copy =~ m/server01/ig)

到这个

if ($server_copy =~ m/server01/i)