【问题标题】:Search and replace multiple lines from a file从文件中搜索和替换多行
【发布时间】:2021-10-19 08:03:43
【问题描述】:

我正在尝试删除 a.txt 文件的一部分并替换为 b.txt 文件的内容,同时还使用 Perl 程序对 a.txt 中的其他行进行修改。

文件 a.txt

line1
line2 
replace from below line
replace from this line
bla bla...
bla bla...
to this line
line3
line4

文件 b.txt

replacement1
replacement2
replacementn 

下面是我的代码,它不起作用。

#!/apps/perl/5.8.3/bin/perl -w
   open (INPUT, "a.txt")              or die $!;
   open (REPLACE, "b.txt")            or die $!;
   open (OUTPUT, ">c.txt")          or die $!;

my $replace_text;
{
    local $/;
    $replace_text = <REPLACE>;
}
close(REPLACE);

while (<INPUT>) {
   s/line1/modified_line1/;
   s/line2/modified_line2/;
   
   if($_ =~ /replace from below line/){
       while(<INPUT>){
             {
                local undef $/;
                s/replace from this line.*to this line/$replace_text/smg;
             }
       s/line3/modified_line3/;
       s/line4/modified_line4/;
       print OUTPUT;
   }
}
}
close(INPUT);
close(OUTPUT);

预期的输出文件 c.txt

modified_line1
modified_line2
replacement1
replacement2
replacementn
modified_line3
modified_line4

谁能帮我理解我哪里出错了?

【问题讨论】:

  • open (INPUT, "filename") 多年来一直是不好的做法。不要使用原始文件句柄名称。首选open my $f, mode, name

标签: regex perl


【解决方案1】:

我认为您不需要嵌套的 while 循环来读取您的输入文件。

一种方法是使用变量来控制何时打印到输出文件:

use warnings;
use strict;

open (INPUT, "a.txt")   or die $!;
open (REPLACE, "b.txt") or die $!;
open (OUTPUT, ">c.txt") or die $!;

my $replace_text;
{
    local $/;
    $replace_text = <REPLACE>;
}
close(REPLACE);

my $print = 1;
while (<INPUT>) {
    s/line(\d)/modified_line$1/;
    $print = 0 if /replace from below line/;
    if (/to this line/) {
        $print = 1;
        $_ = $replace_text;
    }
    print OUTPUT if $print;
}
close(INPUT);
close(OUTPUT);

输出:

modified_line1
modified_line2 
replacement1
replacement2
replacementn
modified_line3
modified_line4

我还使用 \d 将您的 4 个 line 替换合并为 1 个。

【讨论】:

    【解决方案2】:

    虽然我很喜欢 perl,但这里真的没必要:

    sed -e 's/line1/modified_line1/' \
        -e 's/line2/modified_line2/' \
        -e 's/line3/modified_line3/' \
        -e 's/line4/modified_line4/' \
        -e '/replace from below/rb.txt' \
        -e '/replace from below/,/to this line/d' a.txt
    modified_line1
    modified_line2
    replacement1
    replacement2
    replacementn
    modified_line3
    modified_line4
    

    如果您确实想使用 perl,我会这样做:

    #!/usr/bin/env perl
    use strict;
    use warnings;
    open my $ah, '<', "a.txt" or die "a.txt: $!\n";
    
    while(<$ah>) {
            s/line1/modified_line1/;
            s/line2/modified_line2/;
            s/line3/modified_line3/;
            s/line4/modified_line4/;
            if( /replace from below/ ){
                    system "cat b.txt" and exit 1;
            }
            next if( /replace from below/ .. /to this line/);
            print;
    }
    

    【讨论】:

      【解决方案3】:

      问题描述没有说明a.txt文件可以有多大。发布的代码使用带有修饰符 /smg 的正则表达式,这表明 OP 尝试处理多行文本。

      假设输入文件足够小,可以在内存中读取和处理。

      对于代码可管理性替代放置在__DATA__ 块中,该块读取%substitute 哈希。

      基于keys %substitute 构建正则表达式$re 以用于替换模式。

      多行替换基于原始OP的代码(不适用于逐行读取输入数据)。

      定义了两个子例程,用于将文件内容读入变量并将变量数据存储到文件中——只是为了使代码更易于阅读和理解。

      use strict;
      use warnings;
      use feature 'say';
      
      my($fname_in,$fname_repl,$fname_out) = qw/a.txt b.txt c.txt/;
      
      my %substitute = split(/[,\s]/, do{ local $/; <DATA>} );
      my $re = '\b(' . join('|',keys %substitute) . ')\b';
      
      my $data = read_file($fname_in);
      my $replace_with = read_file($fname_repl);
      
      $data =~ s/$re/$substitute{$1}/g;
      $data =~ s/replace from below line.*?to this line/$replace_with/gsm;
      
      save_file($fname_out,$data);
      say $data;
      
      exit 0;
      
      sub read_file {
          my $fname = shift;
          my $data;
          
          open my $fh, '<', $fname
              or die "Couldn't open $fname";
          $data = do { local $/; <$fh> };
          close $fh;
          
          return $data;
      }
      
      sub save_file {
          my $fname = shift;
          my $data  = shift;
          
          open my $fh, '>', $fname
              or die "Couldn't open $fname";
          say $fh $data;
          close $fh;
      }
      
      __DATA__
      line1,modified_line1
      line2,modified_line2
      line3,modified_line3
      line4,modified_line4
      

      输出

      modified_line1
      modified_line2
      replacement1
      replacement2
      replacementn
      modified_line3
      modified_line4
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-10-18
        • 2021-01-31
        • 1970-01-01
        • 2022-11-28
        • 2015-02-20
        • 1970-01-01
        相关资源
        最近更新 更多