只打印第一个单词

时间:2013-07-25 07:40:50

标签: perl

我需要一些关于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

但我只想获得每一行的第一个字(例如ADMADM_TMSADM_1)。

请注意,该文件在此处打印的内容上方和下方有许多其他行。我只想对GROUPSSERVERS之间的行进行此操作。

3 个答案:

答案 0 :(得分: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;

答案 1 :(得分:1)

我建议您在代码中进行2次更改

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

I:在push

之前提取第一个单词

更改此

push (@matchingLines, $_);

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

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

请注意,/^(\S+)/$_ =~ /^(\S+)/的简写。如果您正在使用7stud's answer中的显式循环变量,则不能使用此简写,而是使用显式语法,例如$line =~ /^(\S+)/或循环变量。

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

II:改变print

的方式

更改此

print map { "$_" } @matchingLines;

进入

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

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

或者,根据TLP's suggestion

$\ = $/; 
print for @lines;

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

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

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

  • 请参阅perldoc perlvar(只需使用CTRL + F在该页面中找到它们)
  • 或者您只需在控制台上使用perldoc -v '$/'等即可获取这些信息。

关于可读性的说明

我不认为隐式正则表达式匹配,即/pattern/本身就不好。

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

答案 2 :(得分:0)

您可以使用flip-flop operator (range)选择输入的一部分。这个运算符的想法是它返回false,直到它的LHS(左侧)返回true,然后它返回true直到它的RHS返回false,之后它被重置。这有点像保留一个国家。

请注意,边线也包含在匹配中,因此我们需要删除它们。之后,使用doubleDown's想法并将/^(\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