124

我正在尝试将 .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">

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

4

15 回答 15

100

我会这样做:

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

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

于 2009-06-05T01:28:49.307 回答
85

添加:

 local $/;

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

$ perldoc -q "整个文件"

请参阅和中与文件句柄相关的变量。perldoc perlvarperldoc -f local

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

此外,Path::Class::File允许您啜饮吐痰

Path::Tiny提供了更多方便的方法例如slurp,slurp_rawslurp_utf8以及它们的spew对应方法。

于 2009-06-05T00:18:40.433 回答
81

使用File::Slurp

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

是的,即使您可以使用 CPAN

于 2009-06-05T08:55:02.027 回答
57

所有的帖子都有点不习惯。成语是:

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

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

于 2009-06-05T03:20:56.977 回答
19

来自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>;

您应该仔细考虑一下为什么需要一次加载所有内容。这不是一个可扩展的解决方案。您可能还会发现使用标准 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。

于 2009-06-05T17:06:47.283 回答
8

一个简单的方法是:

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

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

{
    open(F, "filename");
    local $/ = undef;
    $d = <F>;
}
于 2009-06-05T00:12:26.217 回答
7

设置$/undef(请参阅 jrockway 的答案)或仅连接所有文件的行:

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

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

于 2009-06-05T07:24:24.050 回答
4

另一种可能的方式:

open my $fh, '<', "filename";
read $fh, my $string, -s $fh;
close $fh;
于 2013-05-12T00:43:03.917 回答
4

采用

 $/ = undef;

之前$document = <FILE>;$/输入记录分隔符,默认为换行符。通过将其重新定义为undef,您是说没有字段分隔符。这称为“啜饮”模式。

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

于 2016-03-14T16:29:46.497 回答
3

您只从菱形运算符获得第一行,<FILE>因为您在标量上下文中对其进行评估:

$document = <FILE>; 

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

@lines = <FILE>;
print @lines;
于 2009-06-05T17:18:45.893 回答
2

这更多是关于如何这样做的建议。我刚刚在一个相当大的 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>};

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

我通过使用打开、字符串连接和关闭替换了这个不可读的代码。但是,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};

正确关闭文件句柄。

于 2012-02-20T10:48:24.683 回答
2
open f, "test.txt"
$file = join '', <f>

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

于 2013-05-28T14:36:46.510 回答
2

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

my $text = "";
while (my $line = <FILE>) {
    $text .= $line;
}
于 2014-05-08T20:07:08.617 回答
1

我不知道这是否是一个好习惯,但我曾经使用过这个:

($a=<F>);
于 2017-05-31T10:30:06.987 回答
0

您可以简单地创建一个子例程:

#Get File Contents
sub gfc
{
    open FC, @_[0];
    join '', <FC>;
}
于 2013-12-30T16:44:52.697 回答