确定文件是否包含特定字符串的最快方法是什么?

时间:2014-07-29 17:13:52

标签: regex perl

我是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。是否有更快的方法在文件中替换字符串而不是按顺序扫描它,以找到行匹配然后替换?

3 个答案:

答案 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;

效率更高吗?我对此表示怀疑。正则表达式不是非常有效的语句,并且在多行,非常长的标量变量上进行正则表达式并不会有效。

真正的效率是一个可读,可维护的程序。您可能会花费更多时间进行维护,而不是程序实际运行的时间长度。最后一个例子难以理解,可能更难修改。最好坚持使用mapwhile循环。

答案 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.