Как добавить строки в начало и конец файла в Perl? - PullRequest
2 голосов
/ 05 августа 2009

Я хочу добавить строку вверху и внизу файла. Я могу сделать это следующим образом.

open (DATA, "</usr/old") || die "cant open old\n"; #file to which line has to be added

my @body=<DATA>;
close(DATA);

open (FILE, ">/usr/new") || die "cant open new\n"; #file after stuff has been added

print FILE "9   431";

print FILE "\n";

my $body=@body;

for (my $i=0; $i<$body;$i++){

    print FILE "$body[$i]";#not using for loop leads to addition of spaces in new file
}

print FILE "(3,((((1,4),(7,6)),(2,8)),5),9)";

Поскольку я запускаю большой набор файлов, этот процесс будет занимать много времени. Есть ли в Perl какие-либо специальные функции, которые используются для добавления строк вверху и внизу файла?

Ответы [ 9 ]

10 голосов
/ 05 августа 2009

От ответа perlfaq5 на Как изменить, удалить или вставить строку в файл или добавить в начало файла?


Как изменить, удалить или вставить строку в файл или добавить в начало файла?

(предоставлено Брайаном Д. Фой)

Основная идея вставки, изменения или удаления строки из текстового файла заключается в чтении и печати файла до точки, в которую вы хотите внести изменения, внесении изменений, затем чтении и печати остальной части файла. Perl не обеспечивает произвольный доступ к строкам (особенно с учетом того, что разделитель ввода записей $ / является изменяемым), хотя такие модули, как Tie :: File, могут имитировать его.

Программа Perl, выполняющая эти задачи, принимает базовую форму открытия файла, печати его строк и затем закрытия файла:

open my $in,  '<',  $file      or die "Can't read old file: $!";
open my $out, '>', "$file.new" or die "Can't write new file: $!";

while( <$in> )
    {
    print $out $_;
    }

close $out;

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

Чтобы добавить строки в начало, напечатайте эти строки перед тем, как войти в цикл, который печатает существующие строки.

open my $in,  '<',  $file      or die "Can't read old file: $!";
open my $out, '>', "$file.new" or die "Can't write new file: $!";

print $out "# Add this line to the top\n"; # <--- HERE'S THE MAGIC

while( <$in> )
    {
    print $out $_;
    }

close $out;

Чтобы изменить существующие строки, вставьте код для изменения строк внутри цикла while. В этом случае код находит все версии "perl" в нижнем и верхнем регистре. Это происходит для каждой строки, поэтому убедитесь, что вы должны делать это на каждой строке!

open my $in,  '<',  $file      or die "Can't read old file: $!";
open my $out, '>', "$file.new" or die "Can't write new file: $!";

print $out "# Add this line to the top\n";

while( <$in> )
    {
    s/\b(perl)\b/Perl/g;
    print $out $_;
    }

close $out;

Чтобы изменить только определенную строку, полезно ввести номер строки ввода, $. Сначала прочитайте и распечатайте строки до той, которую хотите изменить. Затем прочитайте единственную строку, которую вы хотите изменить, измените ее и напечатайте. После этого прочитайте остальные строки и напечатайте:

while( <$in> )   # print the lines before the change
    {
    print $out $_;
    last if $. == 4; # line number before change
    }

my $line = <$in>;
$line =~ s/\b(perl)\b/Perl/g;
print $out $line;

while( <$in> )   # print the rest of the lines
    {
    print $out $_;
    }

Чтобы пропустить строки, используйте циклические элементы управления. Следующий в этом примере пропускает строки комментариев, а последний останавливает всю обработку, когда встречает либо END , либо DATA .

while( <$in> )
    {
    next if /^\s+#/;             # skip comment lines
    last if /^__(END|DATA)__$/;  # stop at end of code marker
    print $out $_;
    }

Сделайте то же самое, чтобы удалить определенную строку, используя next, чтобы пропустить строки, которые вы не хотите показывать в выводе. Этот пример пропускает каждую пятую строку:

while( <$in> )
    {
    next unless $. % 5;
    print $out $_;
    }

Если по какой-то странной причине вы действительно хотите видеть весь файл сразу, а не обрабатывать его построчно, вы можете вставить его (если вы можете поместить все это в память!):

open my $in,  '<',  $file      or die "Can't read old file: $!"
open my $out, '>', "$file.new" or die "Can't write new file: $!";

my @lines = do { local $/; <$in> }; # slurp!

    # do your magic here

print $out @lines;

Модули, такие как File :: Slurp и Tie :: File, также могут помочь с этим. Если вы можете, однако, не читать весь файл сразу. Perl не вернет эту память операционной системе, пока процесс не завершится.

Вы также можете использовать однострочники Perl для изменения файла на месте. Следующее меняет все 'Fred' на 'Barney' в inFile.txt, перезаписывая файл новым содержимым. С ключом -p Perl оборачивает цикл while вокруг кода, который вы указали с помощью -e, а -i включает редактирование на месте. Текущая строка в $ . С помощью -p Perl автоматически печатает значение $ в конце цикла. Смотрите perlrun для более подробной информации.

perl -pi -e 's/Fred/Barney/' inFile.txt

Чтобы сделать резервную копию файла inFile.txt, укажите -i расширение файла для добавления:

perl -pi.bak -e 's/Fred/Barney/' inFile.txt

Чтобы изменить только пятую строку, вы можете добавить тестовую проверку $., Номер строки ввода, затем выполнять операцию только после прохождения теста:

perl -pi -e 's/Fred/Barney/ if $. == 5' inFile.txt

Чтобы добавить строки перед определенной строкой, вы можете добавить строку (или строки!) До того, как Perl напечатает $ _:

perl -pi -e 'print "Put before third line\n" if $. == 3' inFile.txt

Вы даже можете добавить строку в начало файла, поскольку текущая строка печатается в конце цикла:

perl -pi -e 'print "Put before first line\n" if $. == 1' inFile.txt

Чтобы вставить строку после той, которая уже есть в файле, используйте ключ -n. Это похоже на -p, за исключением того, что он не печатает $ _ в конце цикла, поэтому вы должны сделать это самостоятельно. В этом случае сначала напечатайте $ _, затем напечатайте строку, которую вы хотите добавить.

perl -ni -e 'print; print "Put after fifth line\n" if $. == 5' inFile.txt

Чтобы удалить строки, напечатайте только те, которые вы хотите.

perl -ni -e 'print unless /d/' inFile.txt

    ... or ...

perl -pi -e 'next unless /d/' inFile.txt
2 голосов
/ 05 августа 2009

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

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

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

  • переименовать текущий файл
  • воссоздайте его с тем материалом, который вы хотите вставить в начале,
  • скопировать переименованный файл большими кусками (не обязательно строками) в конец нового файла,
  • добавить новый бит в конце.

Это будет быстро и эффективно для памяти.

Конечно, если ваши файлы достаточно малы, чтобы поместиться в памяти, придерживайтесь того, что у вас есть. Это достаточно хорошо.

Обновление:

Достаточно людей, кажется, находятся в заблуждении, что я защищаю сценарий оболочки, который, как я думал, я исправлю. Вы можете делать все вышеперечисленное изнутри родного Perl.

Но вы можете подумать, нужно ли использовать Perl. Команда оболочки, например:

( echo '9   431';cat /usr/old;echo '(3,((((1,4),(7,6)),(2,8)),5),9)' ) >/usr/new

сделает то же самое (и, вероятно, так же быстро).

Конечно, если вам нужен Perl, то просто проигнорируйте это обновление, как бред старого человека: -)

2 голосов
/ 05 августа 2009

Используйте Tie :: File , который дает вам доступ к строкам файла диска через массив Perl. Поставляется со стандартным дистрибутивом.

Пример из документации:
use Tie::File;

tie @array, 'Tie::File', filename or die ...;
$array[13] = 'blah';     # line 13 of the file is now 'blah'
print $array[42];        # display line 42 of the file

$n_recs = @array;        # how many records are in the file?
$#array -= 2;            # chop two records off the end

for (@array) {
    s/PERL/Perl/g;         # Replace PERL with Perl everywhere in the file
}

# These are just like regular push, pop, unshift, shift, and splice
# Except that they modify the file in the way you would expect
push @array, new recs...;
my $r1 = pop @array;
unshift @array, new recs...;
my $r2 = shift @array;
@old_recs = splice @array, 3, 7, new recs...;

untie @array;            # all finished
1 голос
/ 05 августа 2009

Было дано три ответа, которые увековечивают очень плохую практику:

open(FILE,"<file") or die "cannot open";

Мало того, код поврежден, так как вы открываете файл не для записи, а для чтения.

Когда не удается открыть, вы можете сказать пользователю , почему не удалось. Пожалуйста, получите привычку включать $! в сообщении об ошибке. Также используйте форму с тремя аргументами open, чтобы отделить режим от имени:

my $path="file";
open my($fh), '>', $path or die "$path: $!";

(Это не отвечает на ваш вопрос, но я делаю это ответом, а не комментарием для дополнительного акцента, и поэтому я могу просмотреть его, так как это довольно длительное изложение.)

0 голосов
/ 21 декабря 2015

Я не говорю на Perl, но, возможно, это работает в некоторых ситуациях:

perl -0777 -pi -e 's/^/MY TEXT TO PREPEND/' myfile.txt

То есть откройте файл в режиме абзаца (одна строка) и замените начало этой строки новым текстом, выполнив перезапись на месте.

Вероятно, неэффективно для многих больших файлов.

0 голосов
/ 05 августа 2009

Моя модификация ghostdog74 заключается в том, что дескриптор файла должен быть в операторах печати, а файл должен быть закрыт после второго оператора печати. ​​

    open(FILE, ">", "file") or die "cannot open $file: $!"; 
    print FILE "add line to top";
    while (<FILE>) { print $_;}
    print FILE "add line to bottom";
    close(FILE);
0 голосов
/ 05 августа 2009

Как говорит Пакс, нет встроенного способа сделать это. Но если вы хотите сделать это с помощью однострочной команды perl из вашей оболочки, вы можете использовать:

perl -ple 'print "Top line" if $. == 1; if (eof) { print "$_\nBottom line";  exit; }' yourfile.txt > newfile.txt
0 голосов
/ 05 августа 2009

Есть много способов сделать это, например, с помощью простого сценария оболочки, как упоминалось в @Pax. Вы также можете заменить свой массив и цикл на метод join ():

open(DATA, "</usr/old") || die "cant open old\n"; #file to which line has to be added
my $body=join("", <DATA>);
open (FILE, ">/usr/new") || die "cant open new\n"; #file after stuff has been added
print FILE "9   431\n";
print(FILE $body);
print FILE "(3,((((1,4),(7,6)),(2,8)),5),9)";
close(FILE);
0 голосов
/ 05 августа 2009

вы можете сделать это

open(FILE,">", $file) or die "cannot open $file: $!";
print FILE "add line to top\n";
while (<FILE>) { print $_ ."\n";}
close(FILE);
print FILE "add line to bottom\n";

в командной строке

perl myscript.pl > newfile
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...