简体   繁体   English

替换文本文件中的多行

[英]Replace multiple lines in text file

I have text files containing the text below (amongst other text) 我有包含以下文本的文本文件(以及其他文本)

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,

and I need to replace it with the following text: 我需要将其替换为以下文本:

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,

Each line above corresponds to a new line in the text file. 上面的每一行对应于文本文件中的新行。

After some googling, I thought making use of Perl in the following might work, but it did not. 经过一番谷歌搜索后,我认为在下面的示例中使用Perl可能有效,但没有成功。 I got the error message 我收到错误消息

Illegal division by zero at -e line 1, <> chunk 1 在-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

Does anyone here know the right way to do this? 这里有人知道正确的方法吗?

Edit - some more details: the text after this block is "DIFF_COEFF_Q=" followed by the same set of numbers, so I need to search for and replace the specific lines shown. 编辑-更多详细信息:该块之后的文本为“ DIFF_COEFF_Q =”,后跟相同的数字集,因此我需要搜索并替换显示的特定行。 The text files are not very large in size. 文本文件的大小不是很大。

Copy the file over to a new one, except that within the range of text between these markers drop the replacement text instead. 将文件复制到一个新文件,除了在这些标记之间的文本范围内放置替换文本。 Then move that file to replace the original, as it may be needed judging by the attempted perl -0 -i in the question. 然后移动该文件以替换原始文件,因为根据问题中尝试的perl -0 -i判断可能需要该文件。

Note that when changing a file we have to build new content and then replace the file. 请注意,在更改文件时,我们必须构建新内容,然后替换文件。 There are a few ways to do this and modules that make it easier, shown further below. 有几种方法可以做到这一点,并且使它更容易实现的模块如下所示。

The code below uses the range operator and the fact that it returns the counter for lines within the range, 1 for the first and the number ending with E0 for the last. 下面的代码使用范围运算符,并且它返回范围内行的计数器,第一个返回1 ,最后一个返回以E0结尾的数字。 So we don't copy lines inside that region while we write the replacement text (and the post-region-end marker) on the last line. 因此,当我们在最后一行上写入替换文本(以及后区域结束标记)时,我们不会在该区域内复制行。

I consider the region of interest to end right before DIFF_COEFF_Q= line, per the question edit. 根据问题编辑,我认为感兴趣的区域DIFF_COEFF_Q=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: $!";

Uncomment the move line once this has been tested well enough on your actual files, if you want to replace the original. 如果要替换原始文件,请在实际文件中对move行进行足够好的测试后,取消注释。 You may or may not need a newline after $replacement , depending on it. $replacement ,您可能需要也可能不需要换行符,具体取决于它。

An alternative is to use flags for entering/leaving that range. 一种替代方法是使用标志来输入/离开该范围。 But this won't be cleaner since there are two distinct actions, to stop copying when entering the range and write replacement when leaving. 但这不会更干净,因为有两个不同的动作:进入范围时停止复制并在离开时写入替换。 Thus multiple flags need be set and checked, what may end up messier. 因此,需要设置和检查多个标志,最终可能会变得更加混乱。

If the files can't ever be huge it is simpler to read and process the file in memory. 如果文件不可能太大,则读取和处理内存中的文件会更简单。 Then open the same file for writing and dump the new content 然后打开相同的文件进行写入并转储新内容

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;

Here /m modifier is for multiline mode in which we can use ^ for the beginning of a line (not the whole string), what is helpful here. /m修饰符用于多行模式,在该模式下 ,我们可以将^用作行的开头(而不是整个字符串),这很有用。 The /s makes . /s使. match a newline, too. 也匹配换行符。 Also note that we can slurp a file with Path::Tiny as simply as: my $text = path($file)->slurp; 还要注意,我们可以使用Path::Tiny来抓取文件,就像这样: my $text = path($file)->slurp;

Another option is to use Path::Tiny , which in newer versions has edit and edit_lines methods 另一个选择是使用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 } 
);

For more on this see, for example, this post and this post and this post . 有关此内容的更多信息,例如,请参阅本帖子本帖子本帖子

The first and last way change the inode number of the file. 第一种和最后一种方法更改文件的索引节点号。 See this post if that is a problem. 如果这是一个问题,请参阅这篇文章

It's an interesting error that you've made and I can see what has led you to make it. 您犯了一个有趣的错误,我知道导致您犯错的原因。 But I don't think I've ever seen anyone else make the same mistake :-) 但我认为我从未见过其他人犯同样的错误:-)

Your substitution statement is this: 您的替代声明是这样的:

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

So you've decided to use : as the delimiter of the substitution operator. 因此,您决定使用:作为替换运算符的定界符。 But you want to use the options i , g and s and everywhere you've seen people talk about options on a substitution operator, they talk about using / to introduce the options. 但是您要使用选项igs并且在您看到人们谈论替代运算符上的选项的任何地方,他们都谈论使用/引入选项。 So you've added /igs to your substitution operator. 因此,您已经将/igs添加到了替换运算符。

But what you've missed (and I completely understand why) is that the / that comes before the options is actually the closing delimiter of the standard, s/.../.../ , version of the substitution operator. 但是,您错过了(我完全理解了为什么)的是,选项之前的/实际上是替代运算符s/.../.../标准的结束定界符。 If you change the delimiter (as you have done) then your altered closing delimiter is all you need. 如果您更改定界符(如已完成),则只需更改后的结束定界符即可。

In your case, Perl doesn't expect the / as it has already seen the closing delimiter. 在您的情况下,Perl不希望/因为它已经看到了结束定界符。 It, therefore, decides that the / is a division operator and tries to divide the result of your substitution by igs . 因此,它确定/是除法运算符,并尝试将替换结果除以igs It interprets igs as zero and you get your error. 它将igs解释为零,您会得到错误。

The fix is to remove that / so: 解决方法是删除该/所以:

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

becomes: 变成:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM