如何在一行上打印某些起始行和停止行之间的所有内容?

时间:2011-12-02 20:03:50

标签: perl

while(<FILE>)
{
    chomp $_;
    $line[$i]=$_;
    ++$i;
}

for($j=0;$j<$i;++$j)
{
    if($line[$j]=~/Syn_Name/)
    {
        do
        {
            print OUT $line[$j],"\n";
            ++$j;
        }
        until($line[$j]=~/^\s*$/)
    }
}

这是我的代码我试图在Syn_Name和空行之间打印数据。 我的代码提取了我需要的块。 但是块之间的数据是逐行打印的。我希望每个块的数据都打印在一行上。

3 个答案:

答案 0 :(得分:6)

简化您的代码。使用触发器操作器来控制打印。请注意,打印最后一行不会添加换行符(除非该行包含多个换行符)。充其量,它打印空字符串。在最坏的情况下,它打印空白。

您不需要线条的过渡数组,您可以使用while循环。如果你想要存储行,我添加了一条注释行,说明最好的方法。

#chomp(my @line = <FILE>);
while (<FILE>) {
    chomp;
    if(/Syn_Name/ .. /^\s*$/) {
        print OUT;
        print "\n" if /^\s*$/;
    }
}

答案 1 :(得分:5)

内容

  • Idiomatic Perl
  • 使错误更容易修复
    • 有关常见编程错误的警告
    • 除非变量名一致,否则不要执行
    • 养成这种习惯会为你节省大量时间
  • Perl的范围运算符
  • 工作演示
    • 立即打印限制线
    • 用空格加入行
    • 又一个边缘案例

Idiomatic Perl

您似乎拥有C系列语言的背景资料。这很好,因为它完成了工作,但你可以让Perl为你处理机器,即

  • chomp默认为$_(对于许多其他Perl运算符也是如此)
  • push将一个元素添加到数组的末尾

简化你的第一个循环:

while (<FILE>)
{
    chomp;
    push @line, $_;
}

现在您没有更新$i来跟踪您已添加到阵列中的行数。

在第二个循环中,不使用C风格的for循环,而是使用foreach循环:

  

foreach循环遍历正常列表值并依次将变量 VAR 设置为列表的每个元素...

     

foreach关键字实际上是for关键字的同义词,因此您可以使用foreach表示可读性,或for表示简洁。 (或者因为Bourne shell比csh更熟悉,所以写作更自然。)如果省略 VAR ,则$_设置为每个值。< / p>

这样,Perl会为您处理簿记。

for (@line)
{
    # $_ is the current element of @line
    ...
}

使错误更易于修复

有时Perl可能适应。在第二个循环中说你做了一个简单的打字错误:

for (@lines)

现在运行程序根本不会产生任何输出,即使输入包含Syn_Name块。

人类可以查看代码并看到您可能打算处理刚刚创建的数组并错误地复制了数组的名称。渴望提供帮助的Perl创建了一个新的空@lines数组,这使得foreach循环无所事事。

您可以删除数组名称末尾的虚假s但仍有程序不产生输出!例如,您可能有一个未处理的输入组合,它们不会打开OUT文件句柄。

Perl有几种简单的方法可以免除这些(以及更多!)处理无声失败的挫败感。

有关常见编程错误的警告

您可以打开有助于诊断常见编程问题的enormous list of warnings。凭借我想象中的错误版本的代码,Perl可以告诉你

Name "main::lines" used only once: possible typo at ./synname line 16.

并修复数组名称中的拼写错误

print() on unopened filehandle OUT at ./synname line 20, <FILE> line 8.
print() on unopened filehandle OUT at ./synname line 20, <FILE> line 8.
print() on unopened filehandle OUT at ./synname line 20, <FILE> line 8.
print() on unopened filehandle OUT at ./synname line 20, <FILE> line 8.
print() on unopened filehandle OUT at ./synname line 20, <FILE> line 8.

立即,你会看到有助于发现的有价值的信息,这些信息可能很难或至少是乏味的:

  1. 变量名称不一致,
  2. 该程序试图产生输出,但需要更多的管道。
  3. 除非变量名一致,否则不要执行

    请注意,即使存在上述潜在问题,Perl也会尝试执行。对于某些类型的问题,例如变量命名不一致,您可能更喜欢Perl 执行您的程序但停止并让您先修复它。你可以告诉Perl是strict about variables

      

    如果您访问的变量未通过ouruse vars声明,通过my进行本地化,或者未完全展开,则会生成编译时错误合格。

    权衡是你必须明确你打算成为你的程序的一部分,而不是让它们在第一次使用时方便地生活。在第一个循环之前,您将声明

    my @line;
    

    表达你的意图。然后,由于错误的多元化数组名称的错误,Perl失败了

    Global symbol "@lines" requires explicit package name at ./synname line 16.
    Execution of ./synname aborted due to compilation errors.

    你确切知道哪一行包含错误。

    养成这种习惯会为你节省大量时间

    我开始使用

    编写的几乎所有非平凡的Perl程序
    #! /usr/bin/env perl
    
    use strict;
    use warnings;
    

    首先是shebang线,就Perl而言是一个普通的评论。 use行启用strict编译指示和warnings编译指示。

    不想成为strict-zombie,正如马克·多米努斯所指责的那样,我会指出use strict;如上所述,没有选项会使Perl严格处理容易出错的地区:

    1. 严格的变量,如上所述;
    2. 严格参考,不允许使用符号引用;和
    3. strict subs,要求程序员在引用子程序时要更加小心。
    4. 这是一个非常有用的默认值。有关详细信息,请参阅the strict pragma's documentation

      Perl的范围运算符

      perlop documentation描述了.., Perl's range operator,它可以帮助您大大简化第二个循环中的逻辑:

        

      在标量上下文中,..返回一个布尔值。运算符是双稳态的,如触发器,并模拟 sed awk 和各种编辑器的行范围(逗号)运算符。每个..运算符都维护自己的布尔状态,甚至在调用包含它的子例程时也是如此。只要其左操作数为假,它就是假的。一旦左操作数为真,范围运算符将保持为真,直到右操作数为真, AFTER ,范围运算符再次变为假。在下次评估范围运算符之前,它不会变为假。

      在你的问题中,你写道你想要“Syn_Name和一个空白行之间的数据”,这在Perl中是拼写的

      /Syn_Name/ .. /^\s*$/
      

      在您的情况下,您还希望在范围的末尾执行一些特殊操作,并且..也提供了该案例,同上。

        

      范围中的最终序列号附加了字符串"E0",它不会影响其数值,但如果要排除端点,则可以搜索一些内容。

      ..(我通常使用的标记为$inside$is_inside)返回的值分配给您,可以检查您是否在最后,例如

      my $is_inside = /Syn_Name/ .. /^\s*$/;
      if ($is_inside =~ /E0$/) {
          ...
      }
      

      以这种方式编写也可以避免重复终止条件的代码(..的右侧操作数)。这样,如果您需要更改逻辑,只需在一个位置更改它。当你必须记住时,你有时会忘记并制造错误。

      工作演示

      请参阅下面的代码,您可以复制并粘贴以获取工作程序。出于演示目的,他们从内置DATA文件句柄读取输入并将输出写入STDOUT。以这种方式编写意味着您可以在很少或没有修改的情况下将我的代码转移到您的代码中。

      立即打印限制线

      正如你的问题中所定义的那样,不需要一个循环来收集临时数组中的行,然后再需要另一个循环来处理数组。请考虑以下代码

      #! /usr/bin/env perl
      
      use strict;
      use warnings;
      
      # for demo only
      *FILE = *DATA;
      *OUT = *STDOUT;
      
      while (<FILE>)
      {
          chomp;
          if (my $is_inside = /Syn_Name/ .. /^\s*$/) {
              my $is_last = $is_inside =~ /E0$/;
              print OUT $_, $is_last ? "\n" : ();
          }
      }
      
      __DATA__
      ERROR IF PRESENT IN OUTPUT!
      
      Syn_Name
      foo
      bar
      baz
      
      ERROR IF PRESENT IN OUTPUT!
      

      ,其输出为

      Syn_Namefoobarbaz

      我们始终打印存储在$_中的当前行。当我们在范围的末尾时,也就是当$is_last为真时,我们也会打印换行符。当$is_last为false时,三元运算符的另一个分支中的空列表就是结果 - 意味着我们只打印$_,没有换行符。

      加入带空格的行

      你没有向我们展示一个示例输入,所以我想知道你是否真的想要将这些行组合在一起而不是joining用空格。如果你想要后一种行为,那么程序就变成了

      #! /usr/bin/env perl
      
      use strict;
      use warnings;
      
      # for demo only
      *FILE = *DATA;
      *OUT = *STDOUT;
      
      my @lines;
      while (<FILE>)
      {
          chomp;
          if (my $is_inside = /Syn_Name/ .. /^\s*$/) {
              push @lines, $_;
              if ($is_inside =~ /E0$/) {
                  print OUT join(" ", @lines), "\n";
                  @lines = ();
              }
          }
      }
      
      __DATA__
      ERROR IF PRESENT IN OUTPUT!
      
      Syn_Name
      foo
      bar
      baz
      
      ERROR IF PRESENT IN OUTPUT!
      

      此代码仅在@lines中累积一个Syn_Name块中的行,打印块,并在看到终结符时清除@lines。输出现在是

      Syn_Name foo bar baz

      又一个边缘案例

      最后,如果我们在文件末尾看到Syn_Name但没有终止空行会发生什么?对于您的数据,这可能是不可能的,但如果您需要处理它,您将要使用Perl的eof operator

        

      eof FILEHANDLE
        的 EOF

           

      如果 FILEHANDLE 上的下一次读取将返回文件结尾或 FILEHANDLE 未打开,则返回1 ...没有参数的eof使用最后一个文件读取。

      因此我们终止 空白行或文件结尾。

      #! /usr/bin/env perl
      
      use strict;
      use warnings;
      
      # for demo only
      *FILE = *DATA;
      *OUT = *STDOUT;
      
      my @lines;
      while (<FILE>)
      {
          s/\s+$//;
          #if (my $is_inside = /Syn_Name/ .. /^\s*$/) {
          if (my $is_inside = /Syn_Name/ .. /^\s*$/ || eof) {
              push @lines, $_;
              if ($is_inside =~ /E0$/) {
                  print OUT join(" ", @lines), "\n";
                  @lines = ();
              }
          }
      }
      
      __DATA__
      ERROR IF PRESENT IN OUTPUT!
      Syn_Name
      foo
      bar
      
      YOU CANT SEE ME!
      Syn_Name
      quux
      potrzebie
      

      输出:

      Syn_Name foo bar 
      Syn_Name quux potrzebie

      此处代码除了chomp之外,还删除了行末尾的任何尾随不可见空格。这将确保连接线之间的间距是均匀的,即使输入有点草率。

      如果没有eof检查,程序就不会打印后一行,您可以通过注释掉有效条件并取消注释另一行来查看。

答案 2 :(得分:0)

另一个简化版本:

foreach (grep {chomp; /Syn_Name/ .. /^\s*$/ } <FILE>) {
    print OUT;
    print OUT "\n" if /^\s*$/;
}