从Perl中的大文件中删除一行

H.B*_*rns 4 perl file seek tell

我有一个巨大的文本文件,前五行如下:

This is fist line
This is second line
This is third line
This is fourth line
This is fifth line
Run Code Online (Sandbox Code Playgroud)

现在,我想在该文件的第三行的随机位置写一些东西,它将用我正在编写的新字符串替换该行中的字符.我能用以下代码实现这一点:

use strict;
use warnings;

my @pos = (0);
open my $fh, "+<", "text.txt";

while(<$fh) {
    push @pos, tell($fh);
}

seek $fh , $pos[2]+1, 0;
print $fh "HELLO";

close($fh);
Run Code Online (Sandbox Code Playgroud)

但是,我无法用同样的方法弄清楚如何从该文件中删除整个第三行,以便文本如下所示:

This is fist line
This is second line
This is fourth line
This is fifth line
Run Code Online (Sandbox Code Playgroud)

我不想将整个文件读入数组,也不想使用Tie :: File.是否有可能使用搜索和告诉来实现我的要求?解决方案将非常有用.

zdi*_*dim 7

文件是一个字节序列.我们可以替换(覆盖)其中的一些,但我们如何删除它们?一旦文件被写入,其字节就不能被"拉出"序列或以任何方式"消隐".(根据需要截断文件,可以解除文件末尾的文件.)

其余的内容必须向上移动,以便删除的文本后面的内容将覆盖它.我们必须重写文件的其余部分.在实践中,重写整个文件通常要简单得多.

作为一个非常基本的例子

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: $!";
Run Code Online (Sandbox Code Playgroud)

这会将输入文件的每一行写入输出文件,除非一行与给定的模式匹配.然后重命名该文件,替换原始文件(不涉及数据副本).请参阅perlfaq5中的此主题.

因为我们真的使用临时文件,所以我建议使用核心模块File :: Temp.


通过在更新'+<'模式下打开以便仅覆盖文件的一部分,可以使这更有效,但更复杂.迭代直到带有模式的行,记录(tell)它的位置和行长度,然后复制内存中的所有剩余行.然后seek返回到该行的减去长度的位置,并转储复制的文件的其余部分,覆盖该行及其后面的所有内容.

请注意,现在文件其余部分的数据被复制两次,尽管一个副本在内存中.如果要删除的行远远超过非常大的文件,那么解决这个问题可能有意义.如果有更多行要删除,这会变得更加混乱.


写出新文件并将其复制到原始文件会更改文件的inode编号.对于某些工具或程序而言,这可能是一个问题,如果是,您可以通过其中任何一个更新原始文件

  • 写完新文件后,将其打开以便阅读并打开原件进行写入.这破坏了原始文件.然后从新文件中读取并写入原始文件,从而将内容复制回相同的inode.完成后删除新文件.

  • 以读写模式('+<')打开原始文件以开始.写入新文件后,seek将其写入原始文件的开头(或覆盖的地方),并将新文件的内容写入其中.如果新文件较短,请记住还要设置文件结尾,

    truncate $fh, tell($fh); 
    
    Run Code Online (Sandbox Code Playgroud)

复制完成后.这需要一些小心,第一种方式通常可能更安全.

如果文件不是很大,则新的"文件"可以作为数组或字符串"写入"内存中.

  • `这是第三行\n`占19个字符.您只能用其他19个字符替换它. (2认同)
  • @ H.Burns,将文件视为网格纸的一部分.除了可以从页面中删除方块之外,您无法删除文件的一部分.如果要从文件/页面中删除某些内容,则需要将每个后续字节/平方复制到前面的字节/方块中.例外情况是您可以通过播放文件大小来从文件末尾删除. (2认同)