在Perl中,如何将整个文件读入字符串?

时间:2009-06-05 00:04:30

标签: string perl slurp

我正在尝试将.html文件打开为一个很长的字符串。这就是我所拥有的:

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">

这样我就可以更轻松地搜索整个文档。

17 个答案:

答案 0 :(得分:93)

我会这样做:

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

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

答案 1 :(得分:75)

添加:

 local $/;

从文件句柄中读取之前。请参阅 How can I read in an entire file all at once?

$ perldoc -q "entire file"

请参阅perldoc perlvarVariables related to filehandles中的 perldoc -f local

顺便说一句,如果您可以将脚本放在服务器上,则可以拥有所需的所有模块。请参阅 How do I keep my own module/library directory?

此外,Path::Class::File允许您slurpspew

Path::Tiny提供了更多方便的方法,例如slurp, slurp_raw, slurp_utf8以及spew对应方式。

答案 2 :(得分:75)

使用File::Slurp

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

Yes, even you can use CPAN

答案 3 :(得分:49)

所有帖子都略显不恰当。成语是:

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

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

答案 4 :(得分:18)

来自perlfaq5: How can I read in an entire file all at once?


您可以使用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>;

你应该长时间地思考为什么你需要一次装满所有东西。这不是一个可扩展的解决方案。您可能还会发现使用标准的Tie :: File模块或DB_File模块的$ DB_RECNO绑定更有趣,它允许您将数组绑定到文件,以便访问数组实际访问文件中相应行的元素

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

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

暂时取消您的记录分隔符,并在块退出时自动关闭该文件。如果文件已经打开,请使用:

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

对于普通文件,您也可以使用读取功能。

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

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

答案 5 :(得分:7)

$/设置为undef(请参阅jrockway的回答)或者只连接所有文件的行:

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

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

答案 6 :(得分:7)

一个简单的方法是:

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

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

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

答案 7 :(得分:4)

另一种可能的方式:

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

答案 8 :(得分:3)

您只是从钻石运算符<FILE>获取第一行,因为您在标量上下文中对其进行了评估:

$document = <FILE>; 

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

@lines = <FILE>;
print @lines;

答案 9 :(得分:2)

这更像是关于 NOT 如何做的建议。我刚刚在一个相当大的Perl应用程序中找到一个错误。大多数模块都有自己的配置文件。为了整体阅读配置文件,我在Internet上的某个地方找到了这一行Perl:

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

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

这至少有一个副作用,花费我几个小时才能找到:它没有正确关闭隐式文件句柄(因为它根本不会调用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

奇怪的是,每个文件的行计数器$.都会增加一个。它没有重置,也没有包含行数。并且在打开另一个文件之前它不会重置为零,直到读取至少一行。就我而言,我做的是这样的事情:

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

由于此问题,条件为false,因为行计数器未正确重置。我不知道这是一个错误还是错误的代码...同时调用close;或者close STDIN;也无济于事。

我使用open,string concatenation和close替换了这个不可读的代码。但是,Brad Gilbert发布​​的解决方案也有效,因为它使用了显式文件句柄。

开头的三行可以替换为:

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

正确关闭文件句柄。

答案 10 :(得分:2)

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

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

答案 11 :(得分:2)

open f, "test.txt"
$file = join '', <f>

<f> - 从我们的文件中返回一行数组(如果$/具有默认值"\n"),然后join ''会将此数组放入。

答案 12 :(得分:1)

使用

 $/ = undef;
$document = <FILE>;之前

$/输入记录分隔符,默认情况下是换行符。通过将其重新定义为undef,您说没有字段分隔符。这被称为&#34; slurp&#34;模式。

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

答案 13 :(得分:0)

您只需创建一个子例程:

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

答案 14 :(得分:0)

我不知道这是不是很好的做法,但我过去经常这样做:

($a=<F>);

答案 15 :(得分:-1)

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

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

答案 16 :(得分:-2)

你可以在Linux中使用cat:

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