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

nan*_*ini 3 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*$/)
    }
}
Run Code Online (Sandbox Code Playgroud)

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

TLP*_*TLP 6

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

您不需要线路的转换数组,您可以使用while循环.如果你想要存储行,我添加了一个注释行,说明如何做得最好.

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


Gre*_*con 5

内容

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

惯用Perl

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

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

简化你的第一个循环:

while (<FILE>)
{
    chomp;
    push @line, $_;
}
Run Code Online (Sandbox Code Playgroud)

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

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

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

foreach关键字实际上是对一个同义词for的关键字,这样你就可以使用foreach了可读性或for为了简洁.(或者因为Bourne shell比你更熟悉csh,所以写作更自然.)如果省略VAR,$_则设置为每个值.

这样,Perl就可以为您处理簿记.

for (@line)
{
    # $_ is the current element of @line
    ...
}
Run Code Online (Sandbox Code Playgroud)

使错误更容易修复

有时Perl可能过于宽容.在第二个循环中说你做了一个简单的印刷错误:

for (@lines)
Run Code Online (Sandbox Code Playgroud)

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

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

您可以删除s数组名称末尾的杂散但仍有程序不产生输出!例如,您可能有一个未处理的输入组合,无法打开OUT文件句柄.

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

有关常见编程错误的警告

您可以打开一个巨大的警告列表,帮助诊断常见的编程问题.有了我想象中的错误版本的代码,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. 该计划正试图产生输出,但需要更多的管道.

除非变量名一致,否则不要执行

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

如果你访问不是通过声明的变量这将产生一个编译时错误our或者use vars,通过局部的my,或者是不完全合格.

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

my @line;
Run Code Online (Sandbox Code Playgroud)

表达你的意图.然后,由于错误的多元化数组名称的错误,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;
Run Code Online (Sandbox Code Playgroud)

首先是shebang线,就Perl而言是一个普通的评论.这些use行启用了strictpragma和warningspragma.

不想成为一个严格的僵尸,正如Mark Dominus所说,我会指出,use strict;如上所述,没有任何选择使得Perl严格处理三个易出错的区域:

  1. 严格的变量,如上所述;
  2. 严格的参考,不允许使用符号引用; 和
  3. 严格的subs,要求程序员在引用子程序时要更加小心.

这是一个非常有用的默认值.见strict编译的文件的更多细节.

Perl的范围运营商

perlop中的文档描述..,Perl的范围内操作,可以帮助你大大简化你的第二个循环的逻辑:

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

在你的问题中,你写道你想要"在Syn_Name和空行之间的数据",这在Perl中是拼写的

/Syn_Name/ .. /^\s*$/
Run Code Online (Sandbox Code Playgroud)

在你的情况下,你也想在范围的末尾做一些特别的事情,并..提供这种情况,同上.

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

将返回的值..(我通常对名为$insideor 的标量进行分配)分配给$is_inside你,可以检查你是否在最后,例如,

my $is_inside = /Syn_Name/ .. /^\s*$/;
if ($is_inside =~ /E0$/) {
    ...
}
Run Code Online (Sandbox Code Playgroud)

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

工作演示

请参阅下面的代码,您可以复制并粘贴以获取工作程序.出于演示目的,他们从内置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!
Run Code Online (Sandbox Code Playgroud)

其输出是

Syn_Namefoobarbaz

我们总是打印存储的当前行$_.当我们处于范围的末尾时,也就是说,当$is_last它成立时,我们也会打印换行符.如果$is_last为false,则三元运算符的另一个分支中的空列表是结果 - 意味着我们$_只打印,没有换行符.

加入带空格的行

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

#! /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!
Run Code Online (Sandbox Code Playgroud)

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

Syn_Name foo bar baz

还有一个边缘案例

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

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
Run Code Online (Sandbox Code Playgroud)

输出:

Syn_Name foo bar 
Syn_Name quux potrzebie

代替chomp,代码删除行末尾的任何尾随不可见空格.这将确保连接线之间的间距是均匀的,即使输入有点邋..

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