我的文本文件包含以下文字(以及其他文字)
DIFF_COEFF= 1.000e+07,1.000e+07,1.000e+07,1.000e+07,
1.000e+07,1.000e+07,1.000e+07,1.000e+07,1.000e+07,1.000e+07,1.000e+07,
1.000e+07,1.000e+07,1.000e+07,1.000e+07,1.000e+07,1.000e+07,1.000e+07,
1.000e+07,1.000e+07,1.000e+07,1.000e+07,1.000e+07,1.000e+07,1.000e+07,
1.000e+07,1.000e+07,1.000e+07,1.000e+07,1.000e+07,1.000e+07,1.000e+07,
1.000e+07,1.000e+07,1.000e+07,1.000e+07,1.000e+07,4.000e+05,
我需要用以下文字替换它:
DIFF_COEFF= 2.000e+07,2.000e+07,2.000e+07,2.000e+07,
2.000e+07,2.000e+07,2.000e+07,2.000e+07,2.000e+07,2.000e+07,2.000e+07,
2.000e+07,2.000e+07,2.000e+07,2.000e+07,2.000e+07,2.000e+07,2.000e+07,
2.000e+07,2.000e+07,2.000e+07,2.000e+07,2.000e+07,2.000e+07,2.000e+07,
2.000e+07,2.000e+07,2.000e+07,2.000e+07,2.000e+07,2.000e+07,2.000e+07,
2.000e+07,2.000e+07,2.000e+07,2.000e+07,2.000e+07,8.000e+05,
上面的每一行对应于文本文件中的新行。
经过一些谷歌搜索后,我认为在下面使用Perl可能会有效,但事实并非如此。我收到了错误消息
在-e第1行非法除零,<>块1
s_orig='DIFF_COEFF=*4.000e+05,'
s_new='DIFF_COEFF= 2.000e+07,2.000e+07,2.000e+07,2.000e+07,\n2.000e+07,2.000e+07,2.000e+07,2.000e+07,2.000e+07,2.000e+07,2.000e+07,\n2.000e+07,2.000e+07,2.000e+07,2.000e+07,2.000e+07,2.000e+07,2.000e+07,\n2.000e+07,2.000e+07,2.000e+07,2.000e+07,2.000e+07,2.000e+07,2.000e+07,\n2.000e+07,2.000e+07,2.000e+07,2.000e+07,2.000e+07,2.000e+07,2.000e+07,\n2.000e+07,2.000e+07,2.000e+07,2.000e+07,2.000e+07,8.000e+05,'
perl -0 -i -pe "s:\Q${s_orig}\E:${s_new}:/igs" file.txt
有没有人知道这样做的正确方法?
编辑 - 更多细节:此块之后的文本是“DIFF_COEFF_Q =”后跟相同的数字集,因此我需要搜索并替换显示的特定行。文本文件的大小不是很大。
答案 0 :(得分:1)
将文件复制到新文件,但在这些标记之间的文本范围内删除替换文本。然后移动该文件以替换原始文件,因为可能需要根据问题中的尝试perl -0 -i
来判断。
请注意,在更改文件时,我们必须构建新内容,然后替换该文件。有几种方法可以做到这一点以及使模块变得更容易的模块,如下所示。
下面的代码使用range operator以及它为范围内的行返回计数器的事实,第一个为1
,最后为E0
。因此,当我们在最后一行写下替换文本(和post-region-end标记)时,我们不会复制该区域内的行。
根据问题编辑,我认为感兴趣的区域在DIFF_COEFF_Q=
行之前结束。
use warnings;
use strict;
use feature 'say';
use File::Copy 'move';
my $replacement = "replacement text";
my $file = 'input.txt';
my $out_file = 'new_' . $file;
open my $fh_out, '>', $out_file or die "Can't open $out_file: $!";
open my $fh, '<', $file or die "Can't open $file: $!";
while (<$fh>)
{
if (my $range_cnt = /^\s*DIFF_COEFF\s*=/ .. /^\s*DIFF_COEFF_Q\s*=/) #/
{
if ($range_cnt =~ /E0$/)
{
print $fh_out $replacement; # may need a newline
print $fh_out $_;
}
}
else {
print $fh_out $_;
}
}
close $fh or die "Can't close $file: $!"; # don't overwrite original
close $fh_out or die "Can't close $out_file: $!"; # if there are problems
#move $out_file, $file or die "Can't move $file to $out_file: $!";
如果您想要替换原始文件,则在对实际文件进行了足够好的测试后,取消注释move
行。在$replacement
之后,您可能需要或不需要换行符,具体取决于它。
另一种方法是使用标志进入/离开该范围。但这不会更清晰,因为有两个不同的动作,在进入范围时停止复制并在离开时写入替换。因此需要设置和检查多个标志,最终会变得更加混乱。
如果文件不能很大,则在内存中读取和处理文件会更简单。然后打开相同的文件进行写入并转储新内容
my $text = do { # slurp file into a scalar
local $/;
open my $fh, '<', $file or die "Can't open $file: $!";
<$fh>
};
$text =~ s/^\s*DIFF_COEFF\s*=.*?(\n\s*DIFF_COEFF_Q)/$replacement$1/ms;
# Change $out_file to $file to overwrite
open my $fh_out, '>', $out_file or die "Can't open $out_file: $!";
print $fh_out $text;
此处/m
修饰符用于多行模式,其中我们可以使用^
作为行的开头(而不是整个字符串),这里有用。 /s
也会使.
与换行符匹配。另请注意,我们可以像Path::Tiny
my $text = path($file)->slurp;
来覆盖文件
另一种选择是使用Path::Tiny,在较新版本中使用edit和edit_lines方法
use Path::Tiny;
# NOTE: edits $file in place (changes it)
path($file)->edit(
sub { s/DIFF_COEFF=.*?(\n\s*DIFF_COEFF_Q)/$replacement$1/s }
);
有关详情,请参阅this post和this post以及this post。
第一种方式和最后一种方式更改文件的inode编号。如果这是一个问题,请参见this post。
答案 1 :(得分:1)
这是一个有趣的错误,你已经做了,我可以看到是什么导致你做到了。但我认为我从未见过其他人犯过同样的错误: - )
您的替换声明是:
s:\Q${s_orig}\E:${s_new}:/igs
所以你决定使用:
作为替换运算符的分隔符。但是您想要使用选项i
,g
和s
以及您看到人们谈论替代运算符选项的所有地方,他们谈论使用/
来介绍选项。因此,您已将/igs
添加到替换运算符中。
但是你错过了(我完全理解为什么)是选项之前的/
实际上是替换运算符的标准s/.../.../
版本的结束分隔符。如果你更改了分隔符(正如你所做的那样),那么你需要改变的结束分隔符。
在您的情况下,Perl不会期望/
,因为它已经看到了结束分隔符。因此,它决定/
是一个除法运算符,并尝试将替换结果除以igs
。它将igs
解释为零,您就会收到错误。
修复方法是删除/
所以:
s:\Q${s_orig}\E:${s_new}:/igs
变为:
s:\Q${s_orig}\E:${s_new}:igs