删除线从Perl中的大文件删除线从Perl中的大文件(Deleting a line from a

2019-05-12 08:19发布

我有巨大的文本文件,并第一五行的记载如下:

This is fist line
This is second line
This is third line
This is fourth line
This is fifth line

现在,我想在该文件中的第三行,将新的字符串我写替换该行字符的随机位置写的东西。 我能够做到这一点与下面的代码:

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);

然而,我无法找出与同类的方法,我怎么能删除该文件整个第三行,以便文本读取如下:

This is fist line
This is second line
This is fourth line
This is fifth line

我不希望将整个文件读入一个数组,我也不希望使用领带::文件。 是否有可能使用seek和tell达到我的要求? 一个解决方案将是非常有益的。

Answer 1:

文件是一个字节序列。 我们可以更换 (重写)其中的一部分,但我们会怎么删除 ? 一旦文件被写入其字节不能序列的“拉出”或以任何方式“空白”。 (在该文件的末尾的那些可被驳回,根据需要通过截断文件)。

内容的其余部分必须移动“上升”,从而使接下来要除去覆盖它的文本。 我们不得不重新改写文件的其余部分。 在实践中,往往是简单得多重写整个文件。

作为一个非常简单的例子

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这个话题 。

因为我们真正使用的临时文件,我建议的核心模块文件::温度为。


这可以由更有效,但更为复杂,通过在更新打开'+<'模式,以便覆盖只有文件的一部分。 您迭代,直到与模式,记录(行tell ),它的位置和线路长度,然后将所有剩余的行复制到内存中。 然后, seek回该线的位置减去长度,并转储文件的复制休息,覆盖线和所有它后面。

请注意,现在对于文件的其余部分中的数据被复制两次 ,虽然一个副本在内存中。 去这个麻烦可能是有意义的,如果要删除的行远了非常大的文件。 如果有更多的线路来消除这种混乱得到。


写出一个新的文件,并复制它在原来的改变文件的inode编号。 这可能是一些工具或程序出了问题,如果是,则可以改为通过更新或者原

  • 一旦写入新文件时,打开它读取和开原进行写入。 这则会覆盖原文件。 然后从新文件读取和写入到原来的一个,从而复制内容返回到相同的inode。 完成后删除新的文件。

  • 打开原始文件中读-写模式( '+<' )开始。 一旦写入新文件, seek到原来的(或从中覆盖的地方)的开头和写入新文件的内容。 请记住,还设置了档案结尾,如果新文件更短,

     truncate $fh, tell($fh); 

之后完成了复制。 这需要一定的照顾和第一种方式可能是一般更安全。

如果文件不是巨大的新的“文件”,可以在内存中“写”,作为一个数组或一个字符串。



Answer 2:

使用sed从在Perl Linux命令行命令:

my $return = `sed -i '3d' text.txt`;

其中,“3D”是指删除第3行。



Answer 3:

这要看是有用perlrun ,看看自己如何perl的修改文件“就地”。

鉴于:

$ cat text.txt
This is fist line
This is second line
This is third line
This is fourth line
This is fifth line

你显然已经“修改就地”,sed的一样,通过使用-i-p开关来调用Perl的:

$ perl -i -pe 's/This is third line\s*//' text.txt
$ cat text.txt
This is fist line
This is second line
This is fourth line
This is fifth line

但是,如果你请教Perl的菜谱食谱7.9(或看perlrun ),你会看到这一点:

$ perl -i -pe 's/This is third line\s*//' text.txt

相当于:

while (<>) {
    if ($ARGV ne $oldargv) {           # are we at the next file?
        rename($ARGV, $ARGV . '.bak');
        open(ARGVOUT, ">$ARGV");       # plus error check
        select(ARGVOUT);
        $oldargv = $ARGV;
    }
    s/This is third line\s*//;
}
continue{
    print;
}
select (STDOUT);                      # restore default output


文章来源: Deleting a line from a huge file in Perl