【问题标题】:How do I add lines to the top and bottom of a file in Perl?如何在 Perl 文件的顶部和底部添加行?
【发布时间】:2010-11-16 20:52:23
【问题描述】:

我想在文件的顶部和底部添加一行。我可以按照以下方式进行。

open (DATA, "</usr/old") || die "cant open old\n"; #file to which line has to be added

my @body=<DATA>;
close(DATA);

open (FILE, ">/usr/new") || die "cant open new\n"; #file after stuff has been added

print FILE "9   431";

print FILE "\n";

my $body=@body;

for (my $i=0; $i<$body;$i++){

    print FILE "$body[$i]";#not using for loop leads to addition of spaces in new file
}

print FILE "(3,((((1,4),(7,6)),(2,8)),5),9)";

由于我为大量文件运行此过程将非常耗时。 Perl 是否具有用于在文件顶部和底部添加行的特定功能?

【问题讨论】:

  • 没有在顶部添加行之类的东西,您需要某种缓冲区来保存您要在底部写入的内容,它可以是字符串,但我更喜欢临时文件。所以你从上到下写/追加。没有其他办法。
  • @user2387149 - 不正确。您可以通过简单的测试在文件顶部添加一行:perl -pi -e 'print "This will appear before the original first line\n" if $. == 1' inFile.txt From stackoverflow.com/a/4388309/48082
  • @Cheeso,您的示例创建一个新文件,将换行符写入其中,然后写入原始文件的内容。新文件以与原始文件同名的链接结束这一事实并没有改变它是不同文件的事实。
  • 啊,我明白了。好点子。 @WilliamPursell

标签: perl filehandle


【解决方案1】:

Perl 不能在文件的开头插入,因为很少有操作系统允许这样做。你需要一个你在这里的类型的重写操作。

您在使用该代码时可能遇到的一个问题是,您的地址空间无法容纳真正的大文件。

通过读取整个文件然后将其写出,您可能会遇到内存问题。我会做的是:

  • 重命名当前文件
  • 使用您想要在开头插入的内容重新创建它,
  • 将重命名的文件以大块(不一定是行)的形式复制到新文件的末尾,
  • 在末尾添加新位。

这将是快速且节省内存的。

当然,如果您的文件足够小以适合内存,请坚持使用现有文件。已经足够了。

更新:

似乎有足够多的人误解我在提倡一个我认为我会直截了当的 shell 脚本。您可以在本机 Perl 中完成上述所有操作。

但您可能需要考虑是否有必要使用 Perl。像这样的 shell 命令:

( echo '9   431';cat /usr/old;echo '(3,((((1,4),(7,6)),(2,8)),5),9)' ) >/usr/new

同样会成功(而且可能同样快)。

当然,如果您需要 Perl,那么请忽略此更新,因为这是一个老人的胡言乱语:-)

【讨论】:

  • 正如一个人曾经说过的那样,“如果我们都假设被接受为真实的东西确实是真实的,那么前进的希望就很小了”。请参阅下面的答案:stackoverflow.com/questions/1230654/…
  • @zakovyrya,你应该看看 Tie::File 下的源代码,看看它在做什么 :-) 然后让我知道从性能 POV 来看这有多好。
  • @Pax,有时为了程序员的方便而牺牲性能是可以的。事实上,看看所谓的计算机科学的整个历史。
【解决方案2】:

你可以这样做

open(FILE,">", $file) or die "cannot open $file: $!";
print FILE "add line to top\n";
while (<FILE>) { print $_ ."\n";}
close(FILE);
print FILE "add line to bottom\n";

在命令行上

perl myscript.pl > newfile

【讨论】:

    【解决方案3】:

    有很多方法可以做到这一点,例如使用@Pax 提到的简单 shell 脚本。你也可以用 join() 替换你的数组和循环:

    open(DATA, "</usr/old") || die "cant open old\n"; #file to which line has to be added
    my $body=join("", <DATA>);
    open (FILE, ">/usr/new") || die "cant open new\n"; #file after stuff has been added
    print FILE "9   431\n";
    print(FILE $body);
    print FILE "(3,((((1,4),(7,6)),(2,8)),5),9)";
    close(FILE);
    

    【讨论】:

    • 我实际上并不提倡使用 shell 脚本,你可以使用原生 Perl 完成我提到的所有事情。但是,当然,如果 Perl 不是 pre-req,你可以 这样做: ( echo "start line" ; cat file ; echo "end line" ) > file2 ; mv file2 文件
    【解决方案4】:

    正如 Pax 所说,没有内置的方法可以做到这一点。但是,如果您想在 shell 中使用单行 perl 命令来执行此操作,则可以使用:

    perl -ple 'print "Top line" if $. == 1; if (eof) { print "$_\nBottom line";  exit; }' yourfile.txt > newfile.txt
    

    【讨论】:

      【解决方案5】:

      我对ghostdog74的修改是文件句柄应该在打印语句中,并且文件应该在第二个打印语句之后关闭。

          open(FILE, ">", "file") or die "cannot open $file: $!"; 
          print FILE "add line to top";
          while (<FILE>) { print $_;}
          print FILE "add line to bottom";
          close(FILE);
      

      【讨论】:

        【解决方案6】:

        已经给出了三个使非常糟糕的做法永久存在的答案:

        open(FILE,"<file") or die "cannot open";
        

        不仅如此,由于您打开文件不是为了写入而是为了读取,因此代码被破坏了。

        当打开失败时,您可以告诉用户为什么它失败了。请养成包含$的习惯!在错误消息中。另外,使用open 的三个参数形式将模式与名称分开:

        my $path="file";
        open my($fh), '>', $path or die "$path: $!";
        

        (这并不能回答您的问题,但我将其作为答案而不是评论以增加重点,以便我可以查看它,因为它是一个相当长的吐槽。)

        【讨论】:

          【解决方案7】:

          使用Tie::File,它使您可以通过 Perl 数组访问磁盘文件的行。它带有标准分发。

          文档中的示例:
          use Tie::File;
          
          tie @array, 'Tie::File', filename or die ...;
          $array[13] = 'blah';     # line 13 of the file is now 'blah'
          print $array[42];        # display line 42 of the file
          
          $n_recs = @array;        # how many records are in the file?
          $#array -= 2;            # chop two records off the end
          
          for (@array) {
              s/PERL/Perl/g;         # Replace PERL with Perl everywhere in the file
          }
          
          # These are just like regular push, pop, unshift, shift, and splice
          # Except that they modify the file in the way you would expect
          push @array, new recs...;
          my $r1 = pop @array;
          unshift @array, new recs...;
          my $r2 = shift @array;
          @old_recs = splice @array, 3, 7, new recs...;
          
          untie @array;            # all finished
          

          【讨论】:

            【解决方案8】:

            来自perlfaq5How do I change, delete, or insert a line in a file, or append to the beginning of a file?的回复


            如何在文件中更改、删除或插入一行,或追加到文件的开头?

            (由布赖恩·d·福伊提供)

            在文本文件中插入、更改或删除一行的基本思想包括读取并打印文件到要进行更改的位置,进行更改,然后读取并打印文件的其余部分。 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;
            

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

            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 $_;
                }
            

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

            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::Slurp 和 Tie::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
            

            【讨论】:

              【解决方案9】:

              我不会说 Perl,但也许这适用于某些情况:

              perl -0777 -pi -e 's/^/MY TEXT TO PREPEND/' myfile.txt
              

              也就是说,以段落模式(一行)打开文件,并用新文本替换该行的开头,进行就地重写。

              对于许多大文件可能效率不高。

              【讨论】:

                猜你喜欢
                • 2013-02-04
                • 2019-01-20
                • 2014-07-05
                • 2012-07-15
                • 1970-01-01
                • 1970-01-01
                • 2020-04-27
                • 1970-01-01
                • 2019-04-25
                相关资源
                最近更新 更多