3

Perl でファイルから最後の n 行を削除する方法のヒントを誰か教えてもらえますか? 約 400 MB の非常に大きなファイルがあり、そこから最後の 125,000 行を削除したいと考えています。

4

11 に答える 11

13

Tie::Fileを使用して、ファイルを配列として扱うことができます。

Tie::File を使用します。
tie (@File, 'Tie::File', $Filename);
スプライス (@ファイル、-125000、125000);
@File を解きます。

別の方法は、シェルでheadandを使用することです。wc -l

編集: grepsedawk は、不要な-nオプションを思い出させます:headwc

head -n -125000 ファイル > NEWFILE
于 2008-12-05T23:38:06.927 に答える
6

人々がすでにTie::Arrayを提案しているので、これはうまく機能します。手動で実行したい場合は、基本的なアルゴリズムをレイアウトします。小さなファイルにうまく機能する、ずさんな、遅い方法があります。大きなファイルに対してこれを行う効率的な方法は次のとおりです。

  1. ファイル内の最後からN行目の直前の位置を見つけます。
  2. それ以降はすべて切り捨てます(を使用truncate())。

1はトリッキーな部分です。ファイルに何行あるのか、どこにあるのかわかりません。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! $!";
于 2008-12-07T06:17:38.303 に答える
4

何行あるかわかりますか、それともこのファイルに関する手がかりは他にありますか? これを何度も繰り返す必要がありますか、それとも一度だけですか?

これを一度行う必要がある場合は、ファイルをvimにロードし、最後の行番号を見て、必要な最後の行から最後まで削除します。

:1234567,$d

一般的なプログラミング方法は、2 つのパスで行うことです。1 つは行数を決定するためのもので、もう 1 つは行を削除するためのものです。

簡単な方法は、正しい行数を新しいファイルに出力することです。サイクルとディスクのスラッシングに関してのみ効率的ですが、ほとんどの人はそれらをたくさん持っています。perlfaq5の一部が役立つはずです。あなたは仕事をやり遂げ、人生を歩み始めます。

その間( )
   {
   $out; を印刷します。
   $の場合は最後。> $last_line_I_want;
   }

これが頻繁に行う必要がある場合、またはデータ サイズが大きすぎて書き直すことができない場合は、行とバイト オフセットのインデックスを作成し、ファイルを適切なサイズにtruncate()することができます。インデックスを保持しているため、中断した場所が既にわかっているため、新しい行末を見つけるだけで済みます。一部のファイル処理モジュールは、そのすべてを処理できます。

于 2008-12-05T23:47:06.160 に答える
4

この問題にはシェルスクリプトを使用します。

tac file | sed '1,125000d' | tac

(tac は cat に似ていますが、行を逆順に出力します。Jay Lepreau と David MacKenzie による。GNU coreutils の一部です。)

于 2008-12-06T01:32:39.863 に答える
3
  1. ファイルの最後に移動: fseek
  2. その行数を逆算する
  3. ファイルの位置を調べる: ftell
  4. 長さとしてその位置までファイルを切り捨てます: ftruncate
于 2008-12-06T00:19:39.370 に答える
0

これを試して

:|dd of=urfile seek=1 bs=$(($(stat -c%s urfile)-$(tail -1 urfile|wc -c)))
于 2009-10-19T08:50:37.067 に答える
0

このコードを試してください:

私の$i= 0;
sed -i'\ $ d'ファイル名while($ i ++ <n);

バッククォートもありますが、印刷することはできません:(

于 2009-06-20T06:46:58.467 に答える
0

を使用した私の提案ed

printf '$-125000,$d\nw\nq\n' | ed -s myHugeFile
于 2009-10-19T09:01:16.047 に答える
0

このコード例は、ファイルをスキャンするときに、最後の 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;

これには、最後の 10 個のインデックスと現在の行に十分なメモリしか使用しないという追加の利点があります。

于 2009-10-20T06:29:51.423 に答える
0

Schwern: スクリプトの行use Fnctl$rbw->get_handle行は必要ですか? truncateまた、 true が返されない場合はエラーを報告することをお勧めします。

-- Douglas Hunter (できればその投稿にコメントしただろう)

于 2008-12-08T01:06:38.480 に答える
-1

最も効率的な方法は、ファイルの最後までシークし、各セグメントの改行数をカウントしながらセグメントを段階的に読み取り、切り捨て(perldoc -f truncateを参照)を使用して切り詰めることです。CPANには、ファイルを逆方向​​に読み取るためのモジュールも1つまたは2つあります。

于 2008-12-06T15:48:14.613 に答える