替换文本文件中的多行

问题描述 投票:0回答:2

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

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 text replace
2个回答
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
投票

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

你的替换声明是这样的:

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
© www.soinside.com 2019 - 2024. All rights reserved.