我是Perl的新手,正在解决将某些文件中的字符串替换为另一个文件的问题,我知道的唯一方法如下:
#!/usr/bin/perl
$file = "default.properties";
open (IN, $file) || die "Cannot open file ".$file." for read";
@lines=<IN>;
close IN;
open (OUT, ">", $file) || die "Cannot open file ".$file." for write";
foreach $line (@lines)
{
$line =~ s/hello/hello hello hello/ig;
print OUT $line;
}
close OUT;
这会平等地处理每个文件并逐个扫描每个文件的行,如果包含不包含我想要替换的字符串,则会浪费很多时间。我想知道是否有一种方法(比如哈希)来确定文件是否包含特定的字符串?
P.S。是否有更快的方法在文件中替换字符串而不是按顺序扫描它,以找到行匹配然后替换?
答案 0 :(得分:4)
我是Perl的新手
这与您的直接问题无关,但您应该在Modern Perl上找到一本好书。
多年来,Perl发生了很大的变化,你在Perl中的写作方式发生了变化。既然你刚刚开始,也可以做得对。看看你的代码,看起来你正在从旧的Perl版本中获取编码风格。现在回答你的问题:
这会平等地处理每个文件并逐个扫描每个文件的行,如果包含不包含我想要替换的字符串,则会浪费很多时间。我想知道是否有一种方法(比如哈希)来确定文件是否包含特定的字符串?
最后,您必须阅读整个文件。没有简单的方法。是的,您可以使代码更短,但读取操作会逐位读取文件,并且替换文件会逐位替换文件。较短的代码并不一定意味着它的效率更高。
这是你的程序用更现代的风格写的。
#! /usr/bin/env perl
use strict;
use warnings;
use autodie; # Automatically kills your program on file errors
use feature qw(say); # Automatically adds the \n on the end.
use File::Copy; # Gives me the "move" command
my $file = "default.properties";
open my $in_fh, "<", $file;
open my $out_fh, ">", "$file.temp"; #Can't open a file for reading and writing at the same time!
while ( my $line = < $fh > ) {
chomp $line; # I always recommend that you chomp when you read.
$line =~ s/hello/hello hello hello/;
say {$out_fh} $line;
}
close $in_fh;
close $out_fh;
move "$file.temp", $file;
如您所见,这仍然是一次处理一条线。
以上是上面的一些内容:
use strict;
- 要求您在使用use warnings;
- 打印各种警告,例如未定义的变量use autodie;
- 文件操作失败时自动终止程序。如果你忘记检查是否有效,这可以为你节省很多的悲伤。use feature qw(say);
- 执行&#34;说&#34;命令。这与print
类似,但会在结尾处自动添加新行。use File::Copy;
- 给你move
命令。您无法轻松读取和写入同一文件。因此,我不得不使用不同的文件名进行输入和输出。更好的是File::Temp
,它允许您定义保证唯一的临时文件。open
- 对文件句柄使用标量变量。它使文件句柄更容易传递给函数。while
- for循环必须在按下之前读入整个文件。 while
循环逐行读入文件。在循环中读取文件时始终使用while
。你可以消除循环,但这并不意味着代码效率更高:
#! /usr/bin/env perl
use strict;
use warnings;
use autodie; # Automatically kills your program on file errors
use feature qw(say);
my $file = "default.properties";
open my $in_fh, "<", $file;
open my $out_fh, ">", "$file.temp";
my @lines = < $in_fh >; #Read in all the lines at once
map { s/hello/hello hello hello/; } @lines;
say {$out_fh} join "", @lines;
close $in_fh;
close $out_fh;
move "$file.temp", $file;
这是使用map
,这是一种在没有显式循环的情况下对数组进行操作的方法。这是一个难以理解的命令,但它可以作为你传递给它的数组的循环。这是使用花括号中的替换命令更改@lines
中的每个条目。你会在Perl中看到很多,并且在许多情况下它比for
循环更清晰。
最后,您可以将整个文件放入单个标量变量(包括新行)并对其进行替换:
#! /usr/bin/env perl
use strict;
use warnings;
use autodie; # Automatically kills your program on file errors
use feature qw(say);
my $file = "default.properties";
open my $in_fh, "<", $file;
open my $out_fh, ">", "$file.temp";
my @lines = < $in_fh >; #Read in all the lines at once
$file = join "", @lines # Converts file to one long scalar variable
$lines =~ s/hello/hello hello hello/g;
say {$out_fh} $lines;
close $in_fh;
close $out_fh;
move "$file.temp", $file;
效率更高吗?我对此表示怀疑。正则表达式不是非常有效的语句,并且在多行,非常长的标量变量上进行正则表达式并不会有效。
真正的效率是一个可读,可维护的程序。您可能会花费更多时间进行维护,而不是程序实际运行的时间长度。最后一个例子难以理解,可能更难修改。最好坚持使用map
或while
循环。
答案 1 :(得分:2)
我想知道是否有办法(比如哈希)确定文件是否包含特定字符串?
不是,不。
是否有更快的方法在文件中替换字符串而不是按顺序扫描它,以找到行匹配然后替换?
也没有。
也就是说,你的perl脚本可能没有其他选项那么快或优化;对于您的情况,最值得注意的是sed(1)
:
sed -i -e 's/hello/hello hello hello/g' default.properties
答案 2 :(得分:2)
不,没有神奇的方法可以提前知道文件是否包含字符串。
我建议逐行处理而不是诋毁整个文件。
您可以使用perl $INPLACE_EDIT
编辑文件,如下所示,或查看perlfaq5 - How do I change, delete, or insert a line in a file, or append to the beginning of a file中列出的众多其他方法之一。
#!/usr/bin/perl
use strict;
use warnings;
my $file = "default.properties";
local @ARGV = $file;
local $^I = '.bak';
while (<>) {
s/hello/hello hello hello/ig;
print;
}
unlink "$file$^I"; # Delete backup
或者单行中的等价物
perl -i -pe 's/hello/hello hello hello/ig;' default.properties.