2012-02-17 3 views
0

У меня есть несколько команд, печатающих текст в файл с помощью perl. Во время этих команд print у меня есть оператор if, который должен удалить последние 5 строк файла, который я сейчас пишу, если утверждение истинно. Количество линий удаляемых всегда будет 5.Как удалить последние 5 строк файла

if ($exists == 0) { 
    print(OUTPUT ???) # this should remove the last 5 lines 
} 
+0

проверить этот вопрос: http://stackoverflow.com/questions/345513/how-can-i -delete-the-last-n-lines-of-a-file – asf107

+1

Из переполнения стека и официального Perl FAQ: [Как изменить, удалить или вставить строку в файл или добавить в начало файла в Perl?] (http://stackoverflow.com/questions/2322140/how-do-i-change-delete-or-insert-a-line-in-a-file-or-append-to-the-beginning) – daxim

ответ

4

Вы можете использовать Tie::File:

use Tie::File; 
tie my @array, 'Tie::File', filename or die $!; 

if ($exists == 0) { 
    $#array -= 5; 
} 

Вы можете использовать один и тот же массив при печати, но использовать push вместо:

push @array, "line of text"; 
+0

Как только я прочитал название, я подумал: «Tie :: File FTW!» Ты подтолкнул меня на это. +1 –

1

Только очевидные способы, которыми я могу думать:

  1. Файл блокировки, scanbackwards, чтобы найти должность и использовать truncate.
  2. Не печатайте непосредственно в файл, пройдите через буфер длиной не менее 5 строк и обрезайте буфер.
  3. Распечатать маркер, который означает «игнорировать последние пять строк». процесс всех ваших файлов, прежде чем читать их с буфером, как в # 2

Все довольно неудобное, но это природа плоских файлов, я боюсь.

НТН

+0

Не то, чтобы эти «плохие» предложения, но Tie :: File имеет встроенную магию. –

+0

@ Джоэль Бергер, не так ли. Tie :: File читает файл с самого начала, поэтому он будет значительно медленнее, чем (1) для больших файлов. Не говоря уже о всей памяти, которую он использовал бы от создания индекса каждой строки в файле. – ikegami

+0

@ikegami, вы правы в # 1 быстрее (для больших файлов).Все, что я на самом деле имел в виду, это то, что Tie :: File будет выполнять большую часть работы, а не то, что было бы более эффективно. –

1

В качестве альтернативы, распечатать весь файл, за исключением последних 5 строк:

open($fh, "<", $filename) or die "can't open $filename for reading: $!"; 
open($fh_new, ">", "$filename.new") or die "can't open $filename.new: $!"; 
my $index = 0; # So we can loop over the buffer 
my @buffer; 
my $counter = 0; 
while (<$fh>) { 
    if ($counter++ >= 5) { 
     print $fh_new $buffer[$index]; 
    } 
    $buffer[$index++] = $_; 
    $index = 0 if 5 == $index; 
} 
close $fh; 
close $fh_new; 
use File::Copy; 
move("$filename.new", $filename) or die "Can not copy $filename.new to $filename: $!"; 
+0

Это №2 от ответа Ричарда Хукстона – DVK

+0

'$ index = 0, если 5 == $ index' также может быть написанный '$ index% = 5', предполагая приращения единицы. – TLP

+1

Это очень странные открытые звонки. 'open' возвращает неопределенное истинное значение для успеха и нуль при отказе - ни один из них не полезен в качестве дескрипторов файлов. '$ file' - это файл дескриптора файла, но он не объявлен, и вы используете одну и ту же переменную дважды. Также '$ filename.new' требует двойных кавычек вокруг него или не будет компилироваться. – Borodin

3
$ tac file | perl -ne 'print unless 1 .. 5' | tac > file.tailchopped 
+1

Мне нравится эта альтернатива, никогда не знала о 'tac' до ... – Zaid

+0

' head --lines = -5 file' – TLP

+1

@TLP: 'head: неизвестный параметр -' и 'использование: head [-count | -n count] [файл ...] '. Это не совместимо с POSIX. – tchrist

1

File::ReadBackwards + truncate является самым быстрым для больших файлов, и, вероятно, так же быстро, как и все остальное для короткие файлы.

use File::ReadBackwards qw(); 

my $bfh = File::ReadBackwards->new($qfn) 
    or die("Can't read \"$qfn\": $!\n"); 

$bfh->readline() or last for 1..5; 

my $fh = $bfh->get_handle(); 
truncate($qfn, tell($fh)) 
    or die $!; 

Tie :: Файл является самым медленным и использует большой объем памяти. Избегайте этого решения.

+0

Протестировано и исправлено. – ikegami

0

вы можете попробовать что-то вроде этого:

open FILE, "<", 'filename'; 
if ($exists == 0){ 
@lines = <FILE>; 
$newLastLine = $#lines - 5; 
@print = @lines[0 .. $newLastLine]; 
print "@print"; 
} 

или даже укоротить:

open FILE, "<", 'filename'; 
@lines = <FILE>; 
if ($exists == 0){ 
print "@lines[0 .. $#lines-5]"; 
} 
Смежные вопросы