Perl在将输出打印到新文件时出现问题

4
我想删除文本文件中所有以HPL_开头的行。我已经实现了这一点,并且可以打印到屏幕上,但是当我尝试写入文件时,新文件中只会打印修改后文本的最后一行。请问有什么帮助吗?
open(FILE,"<myfile.txt"); 
@LINES = <FILE>; 
close(FILE); 
open(FILE,">myfile.txt"); 
foreach $LINE (@LINES) { 
@array = split(/\:/,$LINE); 


my $file = "changed";

open OUTFILE, ">$file" or die "unable to open $file $!";

print OUTFILE $LINE unless ($array[0] eq "HPL_");

} 
close(FILE); 
close (OUTFILE);




exit;

2
没有人想要,也不应该被迫去调试没有在顶部加上use strict; use warnings的Perl代码。甚至尝试这样做纯属疯狂。在现代Perl编程环境中,您还需要使用use v5.12或其他版本来标识您正在运行的Perl版本,并且如果您使用的是5.10.1或更高版本,则还需要使用use autodie。否则,这将变得非常困难。 - tchrist
3个回答

8
你只需要删除所有以 HPL_ 开头的行吗?这很简单!
perl -pi -e 's/^HPL_.*//s' myfile.txt

是的,这真的只是一行代码。 :-)


好的回答,我已经点赞了。但是我敢打赌这只是整个问题的一部分,实际问题更加复杂。换句话说,你可能回答了这个问题,但并没有满足真正的需求。并不是在批评你,只是猜测而已。 - MJB
嗯,好的,这比我想象中要容易得多!谢谢 :-) - James_up_North
1
这样做不会在“HPL_”原本所在的位置留下空行吗? 当我尝试时,我必须显式添加 \n 以删除空行。 - TLP

4

如果你不想使用单行代码,可以按照以下方式重写“写入文件”部分:

my $file = "changed";
open( my $outfh, '>', $file ) or die "Could not open file $file: $!\n";
foreach my $LINE (@LINES) { 
  my @array = split(/:/,$LINE);
  next if $array[0] eq 'HPL_';
  print $outfh $LINE;
}
close( $outfh );

请注意,每次循环都在使用open()打开文件。这会导致文件仅包含最后一行,因为使用带有>open()意味着“覆盖文件中的内容”。这是您代码存在的主要问题。 编辑:另外,您需要整理一下代码。使用我展示的词法文件句柄。始终在每个Perl程序的顶部添加tchrist发布的三行。使用三操作符版本的open()。不要将整个文件读入数组,因为如果尝试读取大型文件,则可能会导致计算机内存不足。您的程序可以重写为:
#!perl

use strict;
use autodie; 
use warnings FATAL => "all";

my $infile = "myfile.txt";
my $outfile = "changed.txt";

open( my $infh, '<', $infile );
open( my $outfh, '>', $outfile );
while( my $line = <$infh> ) {
    next if $line =~ /^HPL_/;
    print $outfh $line;
}
close( $outfh );
close( $infh );

注意使用 use autodie 后,您不必在 open() 函数中添加 or die ...,因为 autodie 声明会为您处理这个问题。

相信在print中使用词法文件句柄需要将它们包装在{和}中。 - Rob Raisch
@Rob:不,它们不需要。也许在非常古老的Perl版本中需要,但至少从5.6版本开始就不需要了。只有在像将文件句柄存储在数组中这样的情况下才需要这样做,此时您必须执行类似于print { $fhs[1] } $line的操作。请参见perldoc print - CanSpice
@CanSpice 啊。这说明我已经使用 Perl 很长时间了。 :) - Rob Raisch
@Rob,数据格必须是以下之一:⑴像STDOUTIO::Handle这样的裸词;⑵像$fh$His::fh这样的标量变量;⑶像{ $Handles{$name} }{ get_handle() }{ $ok ? STDOUT : STDERR }这样的括号分隔块。这与某个保存间接句柄的变量是否在词法范围内或是否被自动创建没有任何关系。它总是以这种方式工作。当你们不是指这个时,你们总是错误地说“词法文件句柄”,这会导致混淆。句柄自动创建与词法作用域无关! - tchrist
例如,这些是词法文件句柄但不是自动创建的:my $fh = local *FOOmy $fh = *STDOUT{IO}my $fh = "main::STDOUT"。而这个不是词法文件句柄,但也被自动创建了:open($His::fh=undef, "< /dev/null"); print $His::fh "stuff\n"。而 $His::fh = new IO::Handle::既不是词法也不是自动创建的。你们一直在说词法文件句柄,但实际上你们的意思是自动创建的句柄,但这两个概念完全不同。 - tchrist

4
你的代码问题在于你在行处理循环中打开了输出文件,由于你使用了'>'形式的open,每次都会以写入方式打开文件,覆盖掉之前的内容。
将open()的调用移到文件顶部,在循环上方,它应该可以正常工作。
此外,我不确定你的意图,但在你的示例的第4行,你重新打开了输入文件进行写入(使用'>'),这也会破坏其中包含的任何内容。
顺便说一句,你可以尝试阅读Perl的grep()命令,它专门设计用来做你需要的事情,例如:
#!/usr/bin/perl
use strict;
use warnings;

open(my $in, '<', 'myfile.txt') or die "failed to open input for read: $!";
my @lines = <$in> or die 'no lines to read from input';
close($in);

# collect all lines that do not begin with HPL_ into @result
my @result = grep ! /^HPL_/, @lines; 

open(my $out, '>', 'changed.txt') or die "failed to open output for write: $!";
print { $out } @result;
close($out);

1
没错,每次循环中的open()都会覆盖文件。前两个open是可以的,因为在关闭和重新打开之前,文件已经被加载到数组中了。请注意,要以“追加”模式打开文件并避免覆盖其内容,请使用“>>$file”进行打开(在这个例子中不是一个好主意)。 - Eric Darchis

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接