文件是一个字节序列。我们可以替换(覆盖)其中一些,但我们将如何删除它们?一旦文件被写入,它的字节就不能以任何方式从序列中“拉出”或“空白”。(可以通过根据需要截断文件来消除文件末尾的那些。)
其余内容必须“向上”移动,以便要删除的文本后面的内容会覆盖它。我们必须重写文件的其余部分。实际上,重写整个文件通常要简单得多。
作为一个非常基本的例子
use warnings 'all';
use strict;
use File::Copy qw(move);
my $file_in = '...';
my $file_out = '...'; # best use `File::Temp`
open my $fh_in, '<', $file_in or die "Can't open $file_in: $!";
open my $fh_out, '>', $file_out or die "Can't open $file_out: $!";
# Remove a line with $pattern
my $pattern = qr/this line goes/;
while (<$fh_in>)
{
print $fh_out $_ unless /$pattern/;
}
close $fh_in;
close $fh_out;
# Rename the new fie into the original one, thus replacing it
move ($file_out, $file_in) or die "Can't move $file_out to $file_in: $!";
这会将输入文件的每一行写入输出文件,除非一行匹配给定的模式。然后重命名该文件,替换原始文件(不涉及数据复制)。请参阅perlfaq5 中的此主题。
由于我们确实使用了临时文件,因此我建议使用核心模块File::Temp。
'+<'
通过以更新模式打开以仅覆盖文件的一部分,这可能会更有效,但也更复杂。您迭代直到带有模式的行,记录 ( tell
) 其位置和行长,然后复制内存中所有剩余的行。然后seek
回到减去该行长度的位置,并转储复制的文件的其余部分,覆盖该行及其后面的所有内容。
请注意,现在文件其余部分的数据被复制了两次,尽管一份副本在内存中。如果要删除的行在一个非常大的文件中很远,那么解决这个问题可能是有意义的。如果有更多行要删除,这会变得更加混乱。
写出一个新文件并将其复制到原始文件上会更改文件的inode编号。对于某些工具或程序来说,这可能是个问题,如果是,您可以通过以下任一方式更新原始文件
复制完成后。这需要一些小心,第一种方法通常可能更安全。
如果文件不是很大,则可以将新的“文件”作为数组或字符串“写入”内存中。