2016-10-26 10 views
4

У меня есть огромный текстовый файл и первые пять строк он читает, как показано ниже:Удаление строки из огромного файла в Perl

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 

Я не хочу читать весь файл в массиве, и я не хочу использовать Tie :: File. Можно ли достичь моего требования, используя поиск и рассказ? Решение будет очень полезно.

+0

Почему вы не хотите использовать 'Tie :: File'? Я думаю, что это было бы идеально для этой цели. – Borodin

+1

@Borodin Даже Tie :: File wil читает файл в массив, не будет ли это потребляемой памятью? Может ли в этом случае помочь опция -memory модуля? –

ответ

7

Файл представляет собой последовательность байтов. Мы можем заменить (переписать) некоторые из них, но как бы мы удалить их? Как только файл записывается, его байты не могут быть «вытащены» из последовательности или «заглушены» каким-либо образом. (Те, которые находятся в конце файла, могут быть уволены, обрезая файл по мере необходимости.)

Остальная часть содержимого должна перемещаться «вверх», так что последующее удаляемое текст перезаписывает его. Мы должны переписать остальную часть файла. На практике часто намного проще переписать весь файл.

В очень простом примере

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: $!"; 

Об этом пишет каждую строку входного файла в выходной файл, если строка не соответствует заданному шаблону. Затем этот файл переименовывается, заменяя оригинал (что не связано с копированием данных). См. this topic in perlfaq5.

Поскольку мы действительно используем временный файл, я бы рекомендовал для этого основной модуль File::Temp.


Это может быть более эффективным, но гораздо более сложным путем открытия в режиме обновления '+<' так, чтобы перезаписать только часть файла. Вы повторяете до строки с рисунком, записываете (tell) свою позицию и длину строки, а затем копируете все оставшиеся строки в памяти. Затем seek вернитесь в позицию минус длина этой строки и выгрузите скопированный остаток файла, перезапишив строку и все, что следует за ней.

Обратите внимание, что данные для остальной части файла копируются дважды, хотя одна копия находится в памяти. Переход к этой проблеме может иметь смысл, если удаляемая строка находится далеко от очень большого файла. Если есть больше строк для удаления, это становится беспорядочным.


Выписывая новый файл и скопировать его на оригинал меняет инф.узлов номер файла. Это может быть проблемой для некоторых инструментов или процедур, и если она вместо этого можно обновить оригинал либо

  • После того, как новый файл записывается, открыть его для чтения и открыть оригинал для записи. Это скрепляет исходный файл.Затем прочитайте из нового файла и напишите на оригинал, скопировав содержимое обратно в тот же индекс. Удалите новый файл, когда закончите.

  • Для начала откройте исходный файл в режиме чтения-записи ('+<'). Как только новый файл будет записан, seek в начало оригинала (или на место, из которого можно переписать), и напишите ему содержимое нового файла. Не забудьте также установить оконечные-файла, если новый файл короче, например

    truncate $fh, tell($fh); 
    

    после копирования делается. Это требует некоторой осторожности, и первый способ, вероятно, в целом безопаснее.

Если файл не был огромным, новый «файл» может быть «записан» в памяти, как массив или строка.

+0

Моя точка зрения: мы не можем переписать эту строку ничем, чтобы линия перестала существовать, и следующая строка автоматически появляется? –

+2

'Это третья строка \ n' занимает 19 символов. Вы можете заменить его только на 19 символов. – PerlDuck

+0

@ H.Burns Справа, вот что - нет ничего, это байты, которые есть, поэтому некоторый контент. Единственный способ «удалить» его - переместить остальные. Представьте себе линию маленьких коробок, каждая из которых внутри - внутри каждого должно быть что-то. В файловой системе нет возможности магически вырвать коробку. Единственное, что мы можем сделать, это переместить содержимое следующего окна в одно, которое мы хотим «удалить» и т. Д. Байт в конце может быть отброшен. – zdim

0

Используйте sed команду из командной строки Linux в Perl:

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

Где "3d" означает удаление 3-й ряд.

+0

Почему нижний предел? OP попросил метод удалить строку из огромного файла в perl. Он делает то, что хочет. – papaiatis

+0

Возможно, потому, что это не совсем решение Perl, а просто решение sed. Кроме того, содержимое '$ return' бесполезно. Это всегда пусто. (Я не был противником, кстати.) – PerlDuck

-1

Полезно посмотреть на 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 Cookbook recipe 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 

 Смежные вопросы

  • Нет связанных вопросов^_^