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和空行之间打印数据。 我的代码提取了我需要的块。 但是块之间的数据是逐行打印的。我希望每个块的数据都打印在一行上。
答案 0 :(得分:6)
简化您的代码。使用触发器操作器来控制打印。请注意,打印最后一行不会添加换行符(除非该行包含多个换行符)。充其量,它打印空字符串。在最坏的情况下,它打印空白。
您不需要线条的过渡数组,您可以使用while循环。如果你想要存储行,我添加了一条注释行,说明最好的方法。
#chomp(my @line = <FILE>);
while (<FILE>) {
chomp;
if(/Syn_Name/ .. /^\s*$/) {
print OUT;
print "\n" if /^\s*$/;
}
}
答案 1 :(得分:5)
您似乎拥有C系列语言的背景资料。这很好,因为它完成了工作,但你可以让Perl为你处理机器,即
简化你的第一个循环:
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.
立即,你会看到有助于发现的有价值的信息,这些信息可能很难或至少是乏味的:
请注意,即使存在上述潜在问题,Perl也会尝试执行。对于某些类型的问题,例如变量命名不一致,您可能更喜欢Perl 不执行您的程序但停止并让您先修复它。你可以告诉Perl是strict about variables:
如果您访问的变量未通过
our
或use 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严格处理三容易出错的地区:
这是一个非常有用的默认值。有关详细信息,请参阅the strict
pragma's documentation。
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*$/;
}