2

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

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 是否具有用于在文件顶部和底部添加行的特定功能?

4

9 回答 9

13

perlfaq5How do I change, delete, or insert a line in a file, or append to the beginning?


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

(由布赖恩·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,请提供 -ia 文件扩展名以添加:

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
于 2009-08-05T04:00:30.197 回答
2

给出了三个答案,使以下非常糟糕的做法得以延续:

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

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

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

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

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

于 2009-08-05T03:33:58.000 回答
2

使用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
于 2009-08-05T03:44:14.830 回答
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,那么请忽略此更新,因为这是一个老人的胡言乱语 :-)

于 2009-08-05T00:54:06.010 回答
0

你可以这样做

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
于 2009-08-05T00:57:23.183 回答
0

有很多方法可以做到这一点,例如使用@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);
于 2009-08-05T01:05:41.993 回答
0

我对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);
于 2009-08-05T01:39:32.860 回答
-1

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

perl -ple 'print "Top line" if $. == 1; if (eof) { print "$_\nBottom line";  exit; }' yourfile.txt > newfile.txt
于 2009-08-05T01:25:38.440 回答
-1

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

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

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

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

于 2015-12-21T12:30:36.470 回答