2008-12-05 79 views
3

有人可以給如何從在Perl文件中刪除最後一個n行一些提示?我有一個大約400 MB的非常大的文件,我想從中刪除約125,000條最後一行。如何刪除一個文件的最後N行?

+1

這應該是一個常見問題。等一下,等一下。 *類型類型提交*。現在它在perlfaq5中。 :) – 2009-10-20 07:02:40

回答

13

您可以使用Tie::File處理文件作爲一個數組。

use Tie::File; 
tie (@File, 'Tie::File', $Filename); 
splice (@File, -125000, 125000); 
untie @File; 

另一種方法是在殼使用headwc -l

編輯: grepsedawk讓我們想起了-n選項head的,沒有必要wc

head -n -125000 FILE > NEWFILE
+1

+1我喜歡shell的想法。那將是我最初的做法。特別是如果這是一個一次性的事情。 – 2008-12-05 23:44:12

+0

是的我剛剛使用wc和頭,它似乎工作.. :) – anand 2008-12-05 23:48:18

+0

其實,我認爲在這種情況下,Perl腳本縮放比較好,因爲它不會重新寫入文件。 – Svante 2008-12-05 23:54:34

4

你知道有多少行有,或有關於這個文件的任何其他線索?你是否必須重複這樣做,還是隻是一次?

如果我不得不這樣做一次,我會加載VIM文件,看最後的行號,然後從最後一行我想,直到最後刪除:

:1234567,$d 

的總體規劃辦法是做它在兩個通道:一個確定的行數,再一個擺脫線。

最簡單的方法是打印的行權數量爲一個新文件。這是唯一有效的循環,也許有點磁盤顛簸的條款,但大多數人有很多這樣的。 perlfaq5中的一些內容應該有所幫助。你完成了這項工作,並且繼續生活。

 
while() 
    { 
    print $out; 
    last if $. > $last_line_I_want; 
    } 

如果這是你必須做很多或數據量太大,重寫它的東西,你可以創建線條和字節偏移和truncate()文件到合適大小的指標。當你保持索引時,你只需要發現新的行結尾,因爲你已經知道你離開的地方。一些文件處理模塊可以爲你處理所有這些。

3
  1. 轉到文件的末尾:FSEEK
  2. 計數向後多行
  3. 查出文件位置:FTELL
  4. 截斷該文件到該位置的長度:ftruncate
4

我只是用這個問題的shell腳本:

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

(tac就像貓一樣,但以相反的順序打印行。由Jay Lepreau和David MacKenzie撰寫。部分GNU coreutils)

-1

最有效的方法是查找文件末尾,然後逐漸增加讀取段,同時計算每行中的換行數,然後使用truncate(請參閱perldoc -f truncate)修剪它。 CPAN上還有一個或兩個模塊用於向後讀取文件。

6

由於人們已經建議Tie :: Array,它能很好地完成這項工作,所以如果您想手動完成這項工作,我會列出基本算法。有一些草率的,慢的方法可以很好地處理小文件。以下是對大文件執行此操作的有效方法。

  1. 從結尾找到第N行之前文件中的位置。
  2. 截斷該點後的所有內容(使用truncate())。

1是棘手的部分。我們不知道文件中有多少行或者它們在哪裏。一種方法是統計所有線路,然後返回到第N個。這意味着我們必須每次掃描整個文件。效率更高的是從文件末尾向後讀取。您可以使用read()來做到這一點,但使用File::ReadBackwards可以更容易地逐行倒退(仍然使用高效的緩衝讀取)。

這意味着您只讀取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! $!"; 
0

Schwern擁有:是use Fnctl$rbw->get_handle線在你的腳本有必要嗎?另外,如果它不返回true,我建議報告truncate錯誤。

- 道格拉斯獵人(誰又能對崗位評價,如果他能有)

0

試試這個代碼:

我的$ I = 0;
sed -i'\ $ d'filename while($ i ++ < n);

反引號也將在那裏,但我不能讓他們打印:(

0

試試這個

:|dd of=urfile seek=1 bs=$(($(stat -c%s urfile)-$(tail -1 urfile|wc -c))) 
0

我的建議,使用ed

printf '$-125000,$d\nw\nq\n' | ed -s myHugeFile 
0

這個例子代碼將保留最後10行的索引,因爲它會掃描文件,然後使用最早的索引i在緩衝區中,截斷文件。這當然只會在截斷在你的系統上工作時才起作用。

#! /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; 

這有額外的好處,它僅使用了在過去的十年指標足夠的內存,並且當前行。

相關問題