我有以下三行内容:
rename($file_path, $file_fh.'.bak');
open( my $file_IN_fh, '<' , $file_path.'.bak') || die "die message";
open( my $file_OUT_fh, '>' , $file_path) || die "die message";
效果很好。它使我可以浏览文件while(<$file_IN_fh>)
,使用脚本(s///g
,if()
进行一堆更改,以确定行是否停留,等等),然后写入输出文件。最后,我得到了编辑的文件,并且文件名未更改。
我的问题是我现在不再需要备份文件,因此我想用类似的代码替换不会创建备份文件的代码,并来回评论这三个文件如果我的需求发生变化,这些年来可以排成一行。
我如何不在命令行中进行这种编辑?
答案 0 :(得分:4)
一种基本方法是逐行读取文件并将所需的输出行写入临时文件,然后将其重命名以覆盖原始文件。
use File::Copy qw(move);
open my $fh, '<', $file or die "Can't open $file: $!";
open my $fh_out, '>', $outfile or die "Can't open $outfile: $!";
while (<$fh>) {
next if /line_to_skip/;
s/patt/repl/g;
print $fh_out $_;
}
close $_ for ($fh, $fh_out);
move ($outfile, $file) or die "Can't move $outfile to $file: $!";
这通常由“就地”编辑文件的工具完成(具有额外的安全性,检查和灵活性)。由于$outfile
是临时使用File::Temp。
close-ing文件时添加检查。
请注意,这会更改文件的索引节点号,这对于某些应用程序可能很重要。†
如果文件不大,您可以简化并先阅读
open my $fh, '<', $file or die "Can't open $file: $!";
my @lines = <$fh>;
open $fh, '>', $file or die "Can't open $file for writing: $!";
for (@lines) {
next if /line_to_skip/;
s/patt/repl/g;
print $fh_out $_;
}
close $fh;
由于>
模式会截断现有的inode数据,因此保留了inode编号。
†如果确实存在问题,您仍然可以保留相同的inode。写入临时文件后,将其打开以进行读取,然后打开原始文件以进行写入;截断该索引节点的内容。然后将临时文件复制到原始文件。关闭句柄并删除临时文件。
答案 1 :(得分:2)
如果文件很大,那么我会问为什么您要避免使用临时文件。否则,建议将文件加载到内存中,进行修改,然后再写回。
use File::Slurp qw( read_file write_file );
my $in = read_file($qfn, array_ref => 1);
my @out;
while (defined( $_ = shift(@$in) )) {
s/a/b/g; # For example.
push @out, $_ if /c/; # For example.
}
write_file($qfn, \@out);
我避免通过使用两个数组来使用昂贵的splice
。
请注意,使用Tie :: File可能会节省一行代码,但这会快30倍 [1] ,并且可能使用更少的内存(尽管节省内存是Tie :: File的目标)。 Tie ::文件永远无法解决!
答案 2 :(得分:1)
看看Tie::File
模块。它是一个核心模块,因此不需要安装,其代码非常简单
use Tie::File;
tie my @file, 'Tie::File', $filepath or die $!;
此后,数组@file
将保存文件的内容,每个元素占一行,并且对该数组的任何更改都将反映在文件中。所有数组操作(例如push
,splice
等)都可以正常运行
请注意,文件的第一行位于数组的元素零等中。