有人可以给出一些关于如何从 Perl 中删除文件的最后 n 行的提示吗?我有一个大约 400 MB 的非常大的文件,我想从中删除大约 125,000 行的最后一行。
11 回答
您可以使用Tie::File将文件作为数组处理。
使用领带::文件; tie (@File, 'Tie::File', $Filename); 拼接 (@File, -125000, 125000); 解开@File;
另一种方法是在 shell中使用head
和。wc -l
编辑: grepsedawk 提醒我们-n
选项 to head
,没有wc
必要:
头 -n -125000 文件 > 新文件
正如人们已经建议的那样,Tie::Array 可以很好地完成工作,如果您想手动完成,我将列出基本算法。有一些草率、缓慢的方法可以很好地处理小文件。这是处理大文件的有效方法。
- 查找文件中从末尾数第 N 行之前的位置。
- 在那之后截断所有内容(使用
truncate()
)。
1是棘手的部分。我们不知道文件中有多少行或它们在哪里。一种方法是将所有行数起来,然后返回第 N 行。这意味着我们每次都必须扫描整个文件。更有效的是从文件末尾向后读取。您可以这样做,但使用File::ReadBackwardsread()
更容易,它可以逐行向后(同时仍然使用有效的缓冲读取)。
这意味着您只阅读了 125,000 行而不是整个文件。 truncate()
应该是 O(1) 并且是原子的,并且无论文件有多大,几乎都不会花费任何成本。它只是重置文件的大小。
#!/usr/bin/perl
use strict;
use warnings;
use File::ReadBackwards;
my $LINES = 10; # Change to 125_000 or whatever
my $File = shift; # file passed in as argument
my $rbw = File::ReadBackwards->new($File) or die $!;
# Count backwards $LINES or the beginning of the file is hit
my $line_count = 0;
until( $rbw->eof || $line_count == $LINES ) {
$rbw->readline;
$line_count++;
}
# Chop off everything from that point on.
truncate($File, $rbw->tell) or die "Could not truncate! $!";
你知道有多少行,或者关于这个文件还有其他线索吗?你必须一遍又一遍地这样做,还是只做一次?
如果我必须这样做一次,我会在 vim 中加载文件,查看最后一行号,然后从我想要的最后一行删除直到最后:
:1234567,$d
一般的编程方式是分两遍进行:一是确定行数,二是去掉行数。
简单的方法是将正确数量的行打印到新文件中。它仅在周期和磁盘抖动方面有效,但大多数人都有很多。perlfaq5中的一些东西应该会有所帮助。你完成了工作,然后继续生活。
尽管( ) { 打印$输出; 最后如果 $。> $last_line_I_want; }
如果这是您必须做的很多事情或者数据太大而无法重写它,您可以创建行和字节偏移的索引并将文件truncate()到正确的大小。当你保留索引时,你只需要发现新的行尾,因为你已经知道你离开的地方。一些文件处理模块可以为您处理所有这些。
对于这个问题,我只会使用一个 shell 脚本:
tac file | sed '1,125000d' | tac
(tac 类似于 cat,但以相反的顺序打印行。作者 Jay Lepreau 和 David MacKenzie。GNU coreutils 的一部分。)
- 转到文件末尾:fseek
- 倒数那么多行
- 找出文件位置: ftell
- 将文件截断到该位置作为长度:ftruncate
Schwern:你脚本中的use Fnctl
和$rbw->get_handle
行是必要的吗?此外,我建议truncate
在它不返回 true 的情况下报告错误。
-- Douglas Hunter(如果可以的话,他会评论那篇文章)
试试这个代码:
我的 $i =0 ;
sed -i '\$d' 文件名 while( $i++ < n ) ;
反引号也将在那里,但我无法打印它们:(
试试这个
:|dd of=urfile seek=1 bs=$(($(stat -c%s urfile)-$(tail -1 urfile|wc -c)))
我的建议,使用ed
:
printf '$-125000,$d\nw\nq\n' | ed -s myHugeFile
此示例代码将在扫描文件时保留最后 10 行的索引。然后它使用缓冲区中最早的索引来截断文件。当然,这只有在 truncate 在您的系统上有效时才有效。
#! /usr/bin/env perl
use strict;
use warnings;
use autodie;
open my $file, '+<', 'test.in'; # rw
my @list;
while(<$file>){
if( @list <= 10 ){
push @list, tell $file;
}else{
(undef,@list) = (@list,tell $file);
}
}
seek $file, 0, 0;
truncate $file, $list[0] if @list;
close $file;
这有一个额外的好处,它只为最后十个索引和当前行占用了足够的内存。
最有效的方法是查找文件末尾,然后增量读取段,同时计算每个段中的换行数,然后使用 truncate(请参阅 perldoc -f truncate)将其修剪掉。CPAN 上还有一两个模块用于向后读取文件。