我有包含以下文本的文本文件(以及其他文本)
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 =”后跟同一组数字,因此我需要搜索并替换显示的特定行。文本文件的大小不是很大。
将文件复制到新文件,但在这些标记之间的文本范围内删除替换文本。然后移动该文件以替换原始文件,因为可能需要通过问题中尝试的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。
这是一个有趣的错误,你已经做了,我可以看到是什么导致你做到这一点。但我不认为我曾经见过其他人犯过同样的错误:-)
你的替换声明是这样的:
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