替换文本文件中的多行

时间:2017-12-20 17:22:50

标签: perl text replace

我的文本文件包含以下文字(以及其他文字)

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 =”后跟相同的数字集,因此我需要搜索并替换显示的特定行。文本文件的大小不是很大。

2 个答案:

答案 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,在较新版本中使用editedit_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 postthis post以及this post

第一种方式和最后一种方式更改文件的inode编号。如果这是一个问题,请参见this post

答案 1 :(得分:1)

这是一个有趣的错误,你已经做了,我可以看到是什么导致你做到了。但我认为我从未见过其他人犯过同样的错误: - )

您的替换声明是:

s:\Q${s_orig}\E:${s_new}:/igs

所以你决定使用:作为替换运算符的分隔符。但是您想要使用选项igs以及您看到人们谈论替代运算符选项的所有地方,他们谈论使用/来介绍选项。因此,您已将/igs添加到替换运算符中。

但是你错过了(我完全理解为什么)是选项之前的/实际上是替换运算符的标准s/.../.../版本的结束分隔符。如果你更改了分隔符(正如你所做的那样),那么你需要改变的结束分隔符。

在您的情况下,Perl不会期望/,因为它已经看到了结束分隔符。因此,它决定/是一个除法运算符,并尝试将替换结果除以igs。它将igs解释为零,您就会收到错误。

修复方法是删除/所以:

s:\Q${s_orig}\E:${s_new}:/igs

变为:

s:\Q${s_orig}\E:${s_new}:igs