Perl редактирование файла - PullRequest
       1

Perl редактирование файла

1 голос
/ 03 февраля 2011

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

Старый тест

Hello World

Старый

Данные

Begin_search_here

Новые данные

Старые данные

Новые данные

Я хочу открыть файл, начать поиск с "Begin_search_here" и затем заменить следующийэкземпляр слова «старый» с «новым».Мой код показан ниже, и я правильно нахожу строку, но по какой-то причине я пишу не в том месте.

open(FILE, "+<$filename") || die "problem opening file";

my search = 0;
while(my $line = <FILE>)
{
if($line =~ m/Begin_search_here/)
{
$search = 1;
}

if($search == 1 && $line =~m/Old/)
{
$line = s/Old/New/;
print FILE $line
}

close FILE;

Ответы [ 6 ]

4 голосов
/ 03 февраля 2011

Вот, пожалуйста,

local $^I = '.bak';
local @ARGV = ($filename);
local $_;
my $replaced = 0;

while (<>) {
    if (!$replaced && /Begin_search_here/ .. $replaced) {
        $replaced = s/Old/New/;
    }
    print;
}

Объяснение:

Установка переменной $^I позволяет редактировать на месте, как если бы вы запускали perl с флагом -i.Исходный файл будет сохранен с тем же именем, что и исходный файл, но с расширением «.bak»;замените ".bak" на "", если вы не хотите, чтобы резервная копия была сделана.

@ARGV установлен в список файлов для редактирования на месте;здесь только ваш единственный файл, названный в переменной $filename.

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

Оператор триггера .. используется, чтобы выяснить, в какой части файла выполнять подстановки. Он будет ложным до тех пор, пока не встретится первая строка, соответствующая шаблону Begin_search_here, а затем останется истинным до первого разапроисходит замена (как записано в переменной $replaced), когда она выключается.

1 голос
/ 03 февраля 2011

Я сделал несколько правок, подобных этой, и предложил общую (но урезанную) стратегию:

use strict;
use warnings;

use English qw<$INPLACE_EDIT>;
use Params::Util qw<_CODE>;

local $INPLACE_EDIT = '.bak';
local @ARGV = '/path/to/file';

my @line_actions 
    = ( qr/^Begin_search_here/
      , qr/^Old Data/ => sub { s/^Old/New/ }
      ); 
my $match = shift @line_actions;
while ( <> ) { 
    if ( $match and /$match/ ) { 
        if ( _CODE( $line_actions[0] )) { 
            shift( @line_actions )->( $_ );
        }
        $match = shift @line_actions;
    }
    print;
}
1 голос
/ 03 февраля 2011

Возможно, вам лучше всего открыть входной файл в режиме чтения (open( my $fh, '<', $file ) or die ...;) и записать измененный текст во временный выходной файл, а затем скопировать временный файл поверх входного файла, когда вы закончите.ваша обработка.

1 голос
/ 03 февраля 2011

Вы неправильно используете режим файла с произвольным доступом.К тому времени, когда вы обновите $line и скажете print FILE $line, «курсор» вашего дескриптора файла уже будет расположен в начале строки next .Таким образом, исходная строка не изменяется, а следующая строка перезаписывается вместо перезаписи исходной строки.

Редактирование на месте (см. perlrun) выглядит так, как если бы оно подходилодля этой проблемы.

В противном случае вам нужно прочитать о функции tell, чтобы сохранить позицию в файле перед прочтением строки и seek назад.в эту позицию, прежде чем переписать строку.Да, и данные, которые вы пишете, должны быть точно такого же размера, что и данные, которые вы перезаписываете, иначе вы полностью удалите файл - см. этот вопрос .

0 голосов
/ 03 февраля 2011

Будьте очень осторожны при редактировании файла на месте. Если данные, которые вы заменяете, имеют другую длину, вы разрушаете файл. Кроме того, если ваша программа терпит неудачу в середине, вы в конечном итоге получите уничтоженный файл.

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

Я использую Perl начиная с версии 3.x, и я не могу вспомнить ни разу, когда я изменил файл вместо .

use strict;
use warnings;

open (INPUT, "$oldfile") or die qq(Can't open file "$oldFile" for reading);
open (OUTPUT, "$oldfile.$$") or die qq(Can't open file "$oldfile.$$" for writing);

my $startFlag = 0;
while (my $line = <INPUT>) {
    if ($line ~= /Begin_search_here/) {
        $startFlag = 1;
    }
    if ($startFlag) {
        $line =~ s/New/Old/;
    }
    print OUTPUT "$line";
}

# 
# Only implement these two steps once you've tested your program
#
unlink $oldfile;
rename $oldfile.$$", $oldfile;
0 голосов
/ 03 февраля 2011

Это работает. Как вы указали, он заменяет только одно вхождение.

#! /usr/bin/perl -pi.bak

if (not $match_state) {
  if (/Begin_search_here/) {
    $match_state = "accepting";
  }
}
elsif ($match_state eq "accepting") {
  if (s/Old/New/) {
    $match_state = "done";
  }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...