Обновление нескольких файлов

Программное обновление текстового файла чаще всего реализуется как запись нового файла, похожего на старый, но содержащего все необходимые изменения. Как вы увидите, этот прием приводит почти к такому же результату, как обновление самого файла, но имеет ряд побочных положительных эффектов. В следующем примере используются сотни файлов, имеющих похожий формат. Один из них, fred03.dat, содержит строки следующего вида:

Program name: granite
Author: Gilbert Bates
Company: RockSoft
Department: R&D
Phone: +1 503 555-0095
Date: Tues March 9, 2004
Version: 2.1
Size: 21k
Status: Final beta

Требуется обновить файл и включить в него другую информацию. После обновления файл должен выглядеть примерно так:

Program name: granite
Author: Randal L. Schwartz
Company: RockSoft
Department: R&D
Date: June 12, 2008 6:38 pm
Version: 2.1
Size: 21k
Status: Final beta

Коротко говоря, в файл вносятся три изменения. Имя автора (Author) заменяется другим именем; дата (Date) заменяется текущей датой; телефон (Phone) удаляется совсем. Все эти изменения повторяются в сотнях аналогичных файлов.


Perl поддерживает механизм редактирования файлов «на месте» с небольшой дополнительной помощью со стороны оператора <>. Далее приводится программа, выполняющая нужные операции, хотя на первый взгляд непонятно, как она работает. В ней появился только один незнакомый элемент – специальная переменная $^I. Пока не обращайте на нее внимания, мы вернемся к ней позже.

#!/usr/bin/perl -w
use strict;
chomp(my $date = `date`);
$^I = ".bak";
while (<>) {
s/^Author:.*/Author: Randal L. Schwartz/;
s/^Phone:.*\n//;
s/^Date:.*/Date: $date/;
print;
}

Так как нам нужна сегодняшняя дата, программа начинается с выполнения системной команды date. Пожалуй, для получения даты (в слегка ином формате) было бы лучше воспользоваться функцией Perl localtime в скалярном контексте:

my $date = localtime;

В следующей строке задается переменная $^I; пока не обращайте на нее внимания. Список файлов для оператора <> берется из командной строки. Основной цикл читает, обновляет и выводит данные по одной строке. (Если руководствоваться тем, что вам уже известно, все измененное содержимое будет выведено на терминал и моментально промчится мимо ваших глаз, а файлы не изменятся… Но продолжайте читать.) Обратите внимание: второй вызов s/// заменяет всю строку с телефоном пустой строкой, не оставляя даже символа новой строки.


Соответственно в выходных данных эта строка присутствовать не будет, словно она никогда не существовала. В большинстве выходных строк ни один из трех шаблонов не совпадет, и эти строки будут выведены в неизменном виде. Итак, результат близок к тому, что мы хотим получить, но пока мы не показали вам, как обновленная информация вернется на диск. Секрет кроется в переменной $^I. По умолчанию она равна undef, а программа работает обычным образом. Но если задать ей какую-либо строку, оператор <> начинает творить еще большие чудеса.

Вы уже знакомы с волшебством оператора <>: он автоматически открывает и закрывает серию файлов или читает данные из стандартного входного потока, если имена файлов не заданы. Но если $^I содержит строку, эта строка используется в качестве расширения резервной копии файла. Давайте посмотрим, как это работает на практике. Допустим, оператор <> открывает наш файл fred03.dat. Он открывает файл так же, как прежде, но переименовывает его в fred03.dat.bak. Открытым остается тот же файл, но теперь он хранится на диске под другим именем. Затем <> создает новый файл и присваивает ему имя
fred03.dat. Никаких проблем при этом не возникает; это имя файла уже не используется.


Далее <> по умолчанию выбирает новый файл для вывода, так что все выводимые данные попадут в этот файл.

Цикл while читает строку из старого файла, обновляет ее и выводит в новый файл. На среднем компьютере программа способна обновить тысячи файлов за считанные секунды. Неплохо, верно? Что же увидит пользователь, когда программа завершит работу? Пользователь скажет: «Ага, я вижу! Perl изменил мой файл fred03.dat, внес все необходимые изменения и любезно сохранил копию в резервном файле fred03.dat.bak!» Но мы то знаем правду: Perl никакие файлы не обновлял. Он создал измененную копию, сказал «Абракадабра!» и поменял файлы местами, пока мы следили за волшебной палочкой. Хитро. Некоторые программисты задают $^I значение ~ (тильда), потому что emacs создает резервные копии именно с таким расширением.

Другое возможное значение $^I – пустая строка. Оно активизирует режим редактирования «на месте», но исходные данные не сохраняются в резервном файле. Но поскольку даже небольшая опечатка в шаблоне может стереть все старые данные, использовать пустую строку рекомендуется только в том случае, когда вы хотите проверить надежность своих архивов на магнитных лентах. Резервные копии файлов легко удалить после завершения. А если что-то пойдет не так и вам потребуется заменить обновленные файлы резервными копиями, Perl и в этом вам поможет.

Оцените статью: (0 голосов)
0 5 0

Статьи из раздела Perl на эту тему:
m// в списочном контексте
Глобальная замена (/g)
Другие возможности регулярных выражений
Другие ограничители
Замена с использованием оператора s///