0

我需要一些有关以下 perl 代码的帮助。

#!perl -w
use strict;
use warnings;

open my $file, '<', 'ubb' or die $1;

my $spool = 0;
my @matchingLines;

while (<$file>) {
    if (/GROUPS/i) {
        $spool = 1;
        next;
    }
    elsif (/SERVERS/i) {
        $spool = 0;
        print map { "$_" } @matchingLines;
        @matchingLines = ();
    }
    if ($spool) {
        push (@matchingLines, $_);
    }
}
close ($file);

其输出如下所示。

ADM                     LMID=GW_S4_1_PM,GW_S4_2_BM
                        GRPNO=1

ADM_TMS                 LMID=GW_S4_1_PM,GW_S4_2_BM
                        GRPNO=2
                        TMSNAME=TMS

ADM_1                   LMID=GW_S4_1_PM
                        GRPNO=11

ADM_2                   LMID=GW_S4_2_BM
                        GRPNO=12

DMWSG_Gateway_1         LMID=GW_S4_1_PM
                        GRPNO=101
                        ENVFILE="../GW_S4.Gateway.envfile"

DMWSG_Gateway_2         LMID=GW_S4_2_BM
                        GRPNO=201
                        ENVFILE="../GW_S4.Gateway.envfile"

DMWSG_1                 LMID=GW_S4_1_PM
                        GRPNO=106

DMWSG_2                 LMID=GW_S4_2_BM
                        GRPNO=206

但我只想得到每行的第一个单词(例如ADM,,,ADM_TMSADM_1

请注意,该文件在此处打印的内容上方和下方有很多其他行。我只想对介于GROUPS和之间的行执行此操作SERVERS

4

3 回答 3

1
use strict;
use warnings;
use 5.014;    #say()

my $fname = 'data.txt';
open my $INFILE, '<', $fname 
    or die "Couldn't open $fname: $!";  #-->Not $1"

my $recording_on = 0;
my @matching_lines;

for my $line (<$INFILE>) {

    if ($line =~ /groups/i) {
        $recording_on = 1;
        next;
    }
    elsif ($line =~ /servers/i) {
        say for @matching_lines;  #say() is the same as print(), but it adds a newline at the end
        @matching_lines = ();
        $recording_on = 0;
    }

    if ($recording_on) {
        my ($first_word, $trash)  = split " ", $line, 2;
        push @matching_lines, $first_word;
    }
}

close $INFILE;
于 2013-07-25T08:06:15.343 回答
1

我建议对您的代码进行 2 处更改

注意:在您的问题中使用您的示例数据(以及其他内容)对这些进行了测试。

I:提取之前的第一个单词push

改变这个

push (@matchingLines, $_);

push (@matchingLines, /^(\S+)/);

这会将每行的第一个单词推入数组,而不是整行。

请注意,这/^(\S+)/$_ =~ /^(\S+)/. 如果您使用像7stud's answer中的显式循环变量,则不能使用此速记,而是使用显式语法,例如$line =~ /^(\S+)/循环变量是什么。

当然,您也可以使用7stud's answersplit中建议的功能。

二:改变你的方式print

改变这个

print map { "$_" } @matchingLines;

进入

local $" = "\n";
print "@matchingLines \n";

$"print指定使用双引号或say在双引号内打印数组时用于列表元素的分隔符。

或者,根据TLP 的建议

$\ = $/; 
print for @lines;

或者

print join("\n", @lines), "\n"

注意$/是输入记录分隔符(默认为换行符),$\是输出记录分隔符(默认未定义)。$\附加在每个print命令之后。

有关$/$\和的更多信息$"

  • 查看perldoc perlvar(只需使用 CTRL+F 在该页面中找到它们)
  • 或者您可以简单地perldoc -v '$/'在控制台上使用 etc 来获取这些信息。

可读性说明

我不认为隐式正则表达式匹配 ie/pattern/本身是不好的。

但是与变量匹配,即$variable =~ /pattern/更具可读性(因为您可以立即看到正在进行正则表达式匹配)并且对初学者更友好,但以简洁为代价。

于 2013-07-25T08:45:12.850 回答
0

您可以使用触发器运算符(范围)来选择输入的一部分。这个操作符的想法是它返回 false 直到它的 LHS(左侧)返回 true,然后它返回 true 直到它的 RHS 返回 false,之后它被重置。这有点像保存一个状态。

请注意,边缘线也包含在匹配中,因此我们需要删除它们。之后,使用doubleDown 的想法并推入/^(\S+)/一个数组。使用push它的好处是,如果捕获正则表达式失败,它会返回一个空列表,当正则表达式不匹配时,这会给我们一个无警告的失败。

use strict;
use warnings;

my @matches;
while (<>) {
    if (/GROUPS/i .. /SERVERS/i) {    # flip-flop remembers the matches
        next if (/GROUPS/i or /SERVERS/i);
        push @matches, /^(\S+)/;
    }
}

# @matches should now contain the first words of those lines
于 2013-07-25T10:22:22.430 回答