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