需要打开一个文件并替换多个字符串

时间:2019-05-14 13:43:55

标签: string file perl replace

我有一个很大的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 个答案:

答案 0 :(得分:1)

这是一个逐行工作的示例,因此文件的大小无关紧要。该示例假定您要替换“ 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);

将要转换的文件作为参数调用,它将在stdout上生成更改的输出。输入以下内容...

foo bar 123
40000 50000 60000 99999
fred60000
fred 60000 fred

...它将产生此输出。

foo bar 123
41024 51024 61024 99999
fred60000
fred 61024 fred

这里有几个经典的Perlisms,但是如果您适当地进行RTFM,则不难理解该示例。

答案 1 :(得分:1)

其他答案可以为您提供更好的解决方案。我的专心于解释为什么您的代码无法正常工作。

您的代码的核心在这里:

$old++;
$new++;

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

您可以在循环之外递增$old$new的值。而且您再也不会更改这些值。因此,您只进行了42次相同的替换(将40961更改为41985)。您永远不会尝试更改任何其他数字。

还要查看从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 $_;
}

答案 2 :(得分:0)

这是将输入文件读入字符串并立即执行所有替换的另一种方式:

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

可以提高效率