Нужно открыть файл и заменить несколько строк - PullRequest
0 голосов
/ 14 мая 2019

У меня действительно большой XML-файл.Внутри него есть определенные увеличивающиеся числа, которые я хотел бы заменить другим увеличивающимся числом.Я посмотрел и вот что кто-то предложил здесь раньше.К сожалению, я не могу заставить его работать: (

В приведенном ниже коде все экземпляры 40960 должны быть заменены на 41984, все экземпляры 40961 на 41985 и т. Д. Ничего не происходит. Что я делаю не так?

use strict;
use warnings;

my $old = 40960;
my $new = 41984;
my $string;

my $file = 'file.txt';

rename($file, $file.'.bak');
open(IN, '<'.$file.'.bak') or die $!;
open(OUT, '>'.$file) or die $!;

$old++;
$new++;

for (my $i = 0; $i < 42; $i++) {
    while(<IN>) {
        $_ =~ s/$old/$new/g;
        print OUT $_;
    }
}

close(IN);
close(OUT);

Ответы [ 3 ]

1 голос
/ 14 мая 2019

Вот пример, который работает построчно, поэтому размер файла не имеет значения.В примере предполагается, что вы хотите заменить такие вещи, как «45678», но не «fred45678».В примере также предполагается, что существует диапазон чисел, и вы хотите, чтобы они были заменены новым смещением диапазона на константу.

#!/usr/bin/perl

use strict;
use warnings;

use constant MIN => 40000;
use constant MAX => 90000;
use constant DIFF => +1024;

sub repl { $_[0] >= MIN && $_[0] <= MAX ? $_[0] + DIFF : $_[0] }

while (<>) {
    s/\b(\d+)\b/repl($1)/eg;
    print;
}
exit(0);

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

foo bar 123
40000 50000 60000 99999
fred60000
fred 60000 fred

... он производит этот вывод.

foo bar 123
41024 51024 61024 99999
fred60000
fred 61024 fred

Здесь есть пара классических перлизмов, но пример не должен быть сложнымследовать, если вы правильно RTFM.

1 голос
/ 14 мая 2019

Другие ответы помогут вам лучше решить вашу проблему. Мой концентрируется на объяснении, почему ваш код не работает.

Суть вашего кода здесь:

$old++;
$new++;

for (my $i = 0; $i < 42; $i++) {
    while(<IN>) {
        $_ =~ s/$old/$new/g;
        print OUT $_;
    }
}

Вы увеличиваете значения $old и $new вне своих циклов. И вы никогда не измените эти значения снова. Таким образом, вы делаете одну и ту же замену (меняя 40961 на 41985) 42 раза. Вы никогда не пытаетесь изменить какие-либо другие числа.

Также посмотрите на цикл while, который читает из IN. На первой итерации (когда $i равен 0) вы читаете все данные из IN, а указатель файла остается в конце файла. Поэтому, когда вы снова входите в цикл while на второй итерации (и всех последующих итерациях), вы вообще не читаете данные из файла. Вам необходимо сбросить указатель файла на начало файла в конце каждой итерации.

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

Соединяя эти предложения, вам нужно что-то вроде этого:

my $old    = 40960;
my $change = 1024;

while (<IN>) {
    # Easier way to write your loop
    for my $i ( 1 .. 42 ) {
        my $new = $old + $change;
        # Use \b to mark word boundaries
        s/\b$old\b/$new/g;
        $old++;
    }
    # Print each output line only once
    print OUT $_;
}
0 голосов
/ 14 мая 2019

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

use strict;
use warnings;

{
my $old = 40960;
my $new = 41984;

my ($regexp) = map { qr/$_/ } join '|', map { $old + $_ } 0..41;

my $file = 'file.txt';
rename($file, $file.'.bak');
open(IN, '<'.$file.'.bak') or die $!;
my $str = do {local $/; <IN>};
close IN;
$str =~ s/($regexp)/do_subst($1, $old, $new)/ge;

open(OUT, '>'.$file) or die $!;
print OUT $str;
close OUT;

}

sub do_subst {
    my ( $old, $old_base, $new_base ) = @_;
    my $i = $old - $old_base;
    my $new = $new_base + $i;
    return $new;
}

Примечание. Вероятно, можно сделать его более эффективным с помощью Regexp::Assemble

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