【问题标题】:How do I replace lines in the middle of a file with Perl?如何用 Perl 替换文件中间的行?
【发布时间】:2011-01-17 17:21:36
【问题描述】:

我正在以追加模式打开文件。我需要替换文件中的第2,3和4行,稍后我需要在文件末尾添加新数据。

【问题讨论】:

  • append模式下打开文件与替换已经写入的第2,3,4行似乎不一致。
  • 如何使用所需数据将数据从一个文件转储到另一个文件并在末尾添加新数据
  • 你需要打开两个文件,一个是你读的,一个是你写的。然后遍历第一个文件,并将其中的适当数据添加到第二个文件中。
  • 我也有同样的问题。但是,请注意,在我的情况下,文件必须在线更改。这意味着,既不能像FAQ的方法那样打开一个新文件来编写,也不能在bash中运行'perl -pi'。不要认为答案有帮助。

标签: perl file text replace append


【解决方案1】:

如果您以追加模式打开文件,则无法替换打开文件时已存在的行。附加文件意味着您的程序只能读取文件并添加到文件的末尾。

您可以创建一个新文件,向其中添加内容(可能基于来自不同文件的内容),然后将新文件复制到不同的文件。

我想我不太明白这个作业。

【讨论】:

  • @hobbs,请参阅 perl 文档,perldoc.perl.org/perlopentut.html 它说模式 +>>(读取和追加)“...将允许您在文件中的任何位置读取,但所有写入都将总是走到最后。”所以要小心,一个系统的实现并不意味着它是正确的。
【解决方案2】:

您可以使用Tie::File 执行此操作。 (请注意,它必须重写整个文件才能替换初始行,但 Tie::File 会对您隐藏详细信息。)

【讨论】:

    【解决方案3】:

    一个好的 Perl 习惯用法是读写标准输入和输出。您可以根据需要进行管道传输和重定向。通过操作符<>(对 readline 的换行),Perl 将打开您在命令行中作为参数传递的文件。

    为了回答你的问题,几行代码(尽可能简洁):

    #!/usr/bin/env perl
    use strict; use warnings;
    
    while (<>) {
      if ($. == 2) {
        print "new data", "\n";
        next;
      }
      if ($. == 3) {
        print "very new data", "\n";
        next;
      }
      print;
    }
    
    print "more data", "\n"; 
    

    然后这样调用它:perl yourscript.pl inputfile > outputfile

    如果你想自己打开文件(这里我们只是跳过不需要的行):

    my open $in_fh, '<', $inputfile
      or die "Can't open file $inputfile: $!";
    
    my open $out_fh, '>', $outputfile
      or die "Can't open file $output: $!";
    
    while (my $line = <$in_fh>) {
      next if ( $. == 2 or $. == 3 or $. == 4 );
      print $out_fh $line;
    }
    print $out_fh "...whatever\n";
    

    【讨论】:

      【解决方案4】:

      我认为这是我在 Stackoverflow 上转发最多的常见问题解答。 perlfaq5How do I change, delete, or insert a line in a file, or append to the beginning of a file? 的答案。

      忘记附加模式的东西。那只会让你的生活更加艰难。


      在文本文件中插入、更改或删除一行的基本思想包括读取并打印文件到要进行更改的位置,进行更改,然后读取并打印文件的其余部分。 Perl 不提供对行的随机访问(特别是因为记录输入分隔符 $/ 是可变的),尽管 Tie::File 等模块可以伪造它。

      执行这些任务的 Perl 程序采用打开文件、打印其行、然后关闭文件的基本形式:

      open my $in,  '<',  $file      or die "Can't read old file: $!";
      open my $out, '>', "$file.new" or die "Can't write new file: $!";
      
      while( <$in> )
          {
          print $out $_;
          }
      
      close $out;
      

      在该基本表单中,添加您需要插入、更改或删除行的部分。

      要将行添加到开头,请在进入打印现有行的循环之前打印这些行。

      open my $in,  '<',  $file      or die "Can't read old file: $!";
      open my $out, '>', "$file.new" or die "Can't write new file: $!";
      
      print $out "# Add this line to the top\n"; # <--- HERE'S THE MAGIC
      
      while( <$in> )
          {
          print $out $_;
          }
      
      close $out;
      

      要更改现有行,请插入代码以修改 while 循环内的行。在这种情况下,代码会找到“perl”的所有小写版本并将它们大写。每一行都会发生这种情况,因此请确保您应该在每一行上都这样做!

      open my $in,  '<',  $file      or die "Can't read old file: $!";
      open my $out, '>', "$file.new" or die "Can't write new file: $!";
      
      print $out "# Add this line to the top\n";
      
      while( <$in> )
          {
          s/\b(perl)\b/Perl/g;
          print $out $_;
          }
      
      close $out;
      

      仅更改特定行the input line number, $. 很有用。首先阅读并打印要更改的行。接下来,读取要更改的单行,更改并打印。之后,阅读其余的行并打印出来:

      while( <$in> )   # print the lines before the change
          {
          print $out $_;
          last if $. == 4; # line number before change
          }
      
      my $line = <$in>;
      $line =~ s/\b(perl)\b/Perl/g;
      print $out $line;
      
      while( <$in> )   # print the rest of the lines
          {
          print $out $_;
          }
      

      要跳过行,请使用循环控件。本示例中的下一个跳过注释行,最后一个在遇到__END____DATA__ 时停止所有处理。

      while( <$in> )
          {
          next if /^\s+#/;             # skip comment lines
          last if /^__(END|DATA)__$/;  # stop at end of code marker
          print $out $_;
          }
      

      执行相同的操作来删除特定行,方法是使用 next 跳过您不想在输出中显示的行。此示例每隔五行跳过一次:

      while( <$in> )
          {
          next unless $. % 5;
          print $out $_;
          }
      

      如果出于某种奇怪的原因,您真的想立即查看整个文件而不是逐行处理,则可以将其吞入其中(只要您可以将整个文件放入内存中!):

      open my $in,  '<',  $file      or die "Can't read old file: $!"
      open my $out, '>', "$file.new" or die "Can't write new file: $!";
      
      my @lines = do { local $/; <$in> }; # slurp!
      
          # do your magic here
      
      print $out @lines;
      

      File::SlurpTie::File 等模块也可以提供帮助。但是,如果可以,请避免一次读取整个文件。在进程完成之前,Perl 不会将该内存归还给操作系统。

      您还可以使用 Perl 单行代码就地修改文件。以下将 inFile.txt 中的所有 'Fred' 更改为 'Barney',用新内容覆盖文件。使用-p 开关,Perl 围绕您使用-e 指定的代码包装一个while 循环,-i 打开就地编辑。当前行位于$_。使用-p,Perl 在循环结束时自动打印$_ 的值。详情请见perlrun

      perl -pi -e 's/Fred/Barney/' inFile.txt
      

      要备份 inFile.txt,请给 -i 一个要添加的文件扩展名:

      perl -pi.bak -e 's/Fred/Barney/' inFile.txt
      

      如果只更改第五行,可以添加一个测试检查$.,输入行号,然后只在测试通过时执行操作:

      perl -pi -e 's/Fred/Barney/ if $. == 5' inFile.txt
      

      要在某行之前添加行,您可以在 Perl 打印 $_ 之前添加一行(或多行!):

      perl -pi -e 'print "Put before third line\n" if $. == 3' inFile.txt
      

      您甚至可以在文件的开头添加一行,因为当前行打印在循环的末尾:

      perl -pi -e 'print "Put before first line\n" if $. == 1' inFile.txt
      

      要在文件中已有的一行之后插入一行,请使用-n 开关。就像-p 一样,只是它不会在循环结束时打印$_,所以你必须自己做。在这种情况下,请先打印$_,然后再打印您要添加的行。

      perl -ni -e 'print; print "Put after fifth line\n" if $. == 5' inFile.txt
      

      要删除行,只打印您想要的行。

      perl -ni -e 'print unless /d/' inFile.txt
      
          ... or ...
      
      perl -pi -e 'next unless /d/' inFile.txt
      

      【讨论】:

      • FAQ 链接已损坏,正确链接:learn.perl.org/faq/…。我试图编辑它,但编辑器不让我保存,抛出关于代码缩进的消息。
      猜你喜欢
      • 1970-01-01
      • 2016-08-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-08-03
      • 2014-06-01
      相关资源
      最近更新 更多