Файл представляет собой последовательность байтов. Мы можем заменить (переписать) некоторые из них, но как бы мы удалить их? Как только файл записывается, его байты не могут быть «вытащены» из последовательности или «заглушены» каким-либо образом. (Те, которые находятся в конце файла, могут быть уволены, обрезая файл по мере необходимости.)
Остальная часть содержимого должна перемещаться «вверх», так что последующее удаляемое текст перезаписывает его. Мы должны переписать остальную часть файла. На практике часто намного проще переписать весь файл.
В очень простом примере
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);
после копирования делается. Это требует некоторой осторожности, и первый способ, вероятно, в целом безопаснее.
Если файл не был огромным, новый «файл» может быть «записан» в памяти, как массив или строка.
Почему вы не хотите использовать 'Tie :: File'? Я думаю, что это было бы идеально для этой цели. – Borodin
@Borodin Даже Tie :: File wil читает файл в массив, не будет ли это потребляемой памятью? Может ли в этом случае помочь опция -memory модуля? –