在Perl中,如何将整个文件读入string?

我试图打开一个.html文件作为一个很大的长string。 这是我得到的:

open(FILE, 'index.html') or die "Can't read file 'filename' [$!]\n"; $document = <FILE>; close (FILE); print $document; 

这导致:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN

不过,我希望结果如下所示:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">

这样我可以更轻松地search整个文档。

加:

  local $/; 

在从文件句柄读取之前。 请参阅如何一次读取整个文件? , 要么

  $ perldoc -q“整个文件” 

请参阅perldoc perlvarperldoc -f local 与文件句柄相关的variables

顺便说一句,如果你可以把脚本放在服务器上,你可以拥有你想要的所有模块。 请参阅如何保留自己的模块/库目录?

另外, Path :: Class :: File允许你啜泣和吐出 。

Path :: Tiny提供了更多的便利方法,比如slurp_rawslurp_utf8slurp_utf8以及他们的对手。

我会这样做:

 my $file = "index.html"; my $document = do { local $/ = undef; open my $fh, "<", $file or die "could not open $file: $!"; <$fh>; }; 

请注意使用open的三参数版本。 这比旧的两(或一个)参数版本要安全得多。 还要注意使用词法文件句柄。 由于许多原因,词法文件句柄比旧的裸词变体更好。 我们正在利用其中的一个:在超出范围时closures。

用File :: Slurp :

 use File::Slurp; my $text = read_file('index.html'); 

是的,即使你可以使用CPAN 。

所有的职位都是非惯用的。 成语是:

 open my $fh, '<', $filename or die "error opening $filename: $!"; my $data = do { local $/; <$fh> }; 

大多数情况下,不需要将$ /设置为undef

来自perlfaq5:我怎样才能一次读入整个文件? :


您可以使用File :: Slurp模块一步完成。

 use File::Slurp; $all_of_it = read_file($filename); # entire file in scalar @all_lines = read_file($filename); # one line per element 

用于处理文件中所有行的习惯Perl方法是一次完成一行:

 open (INPUT, $file) || die "can't open $file: $!"; while (<INPUT>) { chomp; # do something with $_ } close(INPUT) || die "can't close $file: $!"; 

这比将整个文件作为一行数组读取到内存中的效率高得多,然后一次处理一个元素,这通常(如果不是几乎总是)错误的方法。 每当你看到有人这样做:

 @lines = <INPUT>; 

你应该长时间思考,为什么你需要一次加载一切。 这只是一个可扩展的解决scheme。 使用标准的Tie :: File模块或者DB_File模块的$ DB_RECNO绑定,你可能会发现它更有趣,它允许你将一个数组绑定到一个文件,以便访问一个数组实际访问文件中对应的行。

您可以将整个文件句柄内容读入标量。

 { local(*INPUT, $/); open (INPUT, $file) || die "can't open $file: $!"; $var = <INPUT>; } 

暂时取消您的logging分隔符,并将自动closures文件在块出口。 如果文件已经打开,只需使用这个:

 $var = do { local $/; <INPUT> }; 

对于普通文件,你也可以使用读取function。

 read( INPUT, $var, -s INPUT ); 

第三个参数testingINPUT文件句柄上数据的字节大小,并将很多字节读入缓冲区$ var。

可以将$/设置$/ undef (请参阅jrockway的答案),或者只是连接所有文件的行:

 $content = join('', <$fh>); 

build议在任何支持它的Perl版本上使用标量文件句柄。

一个简单的方法是:

 while (<FILE>) { $document .= $_ } 

另一种方法是更改​​inputlogging分隔符“$ /”。 您可以在裸块中本地执行此操作,以避免更改全局logging分隔符。

 { open(F, "filename"); local $/ = undef; $d = <F>; } 

另一种可能的方式

 open my $fh, '<', "filename"; read $fh, my $string, -s $fh; close $fh; 

您只能从钻石运算符<FILE>获得第一行,因为您正在以标量上下文对其进行评估:

 $document = <FILE>; 

在列表/数组上下文中,菱形运算符将返回文件的所有行。

 @lines = <FILE>; print @lines; 
 open f, "test.txt" $file = join '', <f> 

<f> – 从我们的文件中返回一个行数组(如果$/有默认值"\n" ),然后join ''将把这个数组粘贴进去。

我会以最简单的方式来做,所以任何人都可以理解发生了什么,即使有更聪明的方法:

 my $text = ""; while (my $line = <FILE>) { $text .= $line; } 

这是更多关于如何这样做的build议。 我刚刚在一个相当大的Perl应用程序中发现了一个错误。 大多数模块都有自己的configuration文件。 为了读取整个configuration文件,我在Internet上发现了这一行Perl:

 # Bad! Don't do that! my $content = do{local(@ARGV,$/)=$filename;<>}; 

它重新分配行分隔符如前所述。 但它也重新命名STDIN。

这至less有一个副作用,花费我几个小时才能find:它没有正确closures隐式文件句柄(因为它根本不会close )。

例如,这样做:

 use strict; use warnings; my $filename = 'some-file.txt'; my $content = do{local(@ARGV,$/)=$filename;<>}; my $content2 = do{local(@ARGV,$/)=$filename;<>}; my $content3 = do{local(@ARGV,$/)=$filename;<>}; print "After reading a file 3 times redirecting to STDIN: $.\n"; open (FILE, "<", $filename) or die $!; print "After opening a file using dedicated file handle: $.\n"; while (<FILE>) { print "read line: $.\n"; } print "before close: $.\n"; close FILE; print "after close: $.\n"; 

结果是:

 After reading a file 3 times redirecting to STDIN: 3 After opening a file using dedicated file handle: 3 read line: 1 read line: 2 (...) read line: 46 before close: 46 after close: 0 

奇怪的是,行计数$. 每个文件增加一个。 它没有被重置,也没有包含行数。 当打开另一个文件时,它不会重置为零,直到至less读取一行。 就我而言,我是这样做的:

 while($. < $skipLines) {<FILE>}; 

由于这个问题,条件是错误的,因为行计数器没有正确重置。 我不知道这是一个错误还是简单的错误的代码…也调用close; 或者close STDIN; 没有帮助。

我用open,string concatenation和close来replace这个不可读的代码。 但是,由Brad Gilbert发布​​的解决scheme也可以使用显式文件句柄。

开头的三行可以replace为:

 my $content = do{local $/; open(my $f1, '<', $filename) or die $!; my $tmp1 = <$f1>; close $f1 or die $!; $tmp1}; my $content2 = do{local $/; open(my $f2, '<', $filename) or die $!; my $tmp2 = <$f2>; close $f2 or die $!; $tmp2}; my $content3 = do{local $/; open(my $f3, '<', $filename) or die $!; my $tmp3 = <$f3>; close $f3 or die $!; $tmp3}; 

正确closures文件句柄。

你可以简单地创build一个子例程:

 #Get File Contents sub gfc { open FC, @_[0]; join '', <FC>; } 

使用

  $/ = undef; 

$document = <FILE>;$/inputlogging分隔符,默认情况下是换行符。 通过重新定义它undef ,你是说没有字段分隔符。 这被称为“slurp”模式。

其他解决scheme,如undef $/local $/ (但不是my $/ )重新声明$ /,从而产生相同的效果。

这些都是很好的答案。 但是如果你觉得懒惰,文件不是那么大,安全性不是问题(你知道你没有一个受污染的文件名),那么你可以掏腰包:

 $x=`cat /tmp/foo`; # note backticks, qw"cat ..." also works 

我不知道这是不是很好的做法,但是我习惯使用这个:

 ($a=<F>); 

你可以在Linux中使用cat:

 @file1=\`cat /etc/file.txt\`;