46

请注意 - 我不是在寻找打开/读取文件的“正确”方式,或者我每次都应该打开/读取文件的方式。我只是想知道大多数人使用什么方式,也许同时学习一些新方法:)*

在我的 Perl 程序中,一个非常常见的代码块是打开一个文件并读取或写入它。我见过很多这样做的方法,多年来我执行这项任务的风格已经改变了几次。我只是想知道最好的(如果有最好的方法)方法是什么?

我曾经打开过这样的文件:

my $input_file = "/path/to/my/file";
open INPUT_FILE, "<$input_file"  || die "Can't open $input_file: $!\n";

但我认为这有错误捕获的问题。

添加括号似乎可以修复错误捕获:

open (INPUT_FILE, "<$input_file")  || die "Can't open $input_file: $!\n";

我知道您也可以将文件句柄分配给变量,因此我可以使用 $input_filehandle 而不是像上面那样使用“INPUT_FILE”——这样更好吗?

对于读取文件,如果文件很小,像这样的通配有什么问题吗?

my @array = <INPUT_FILE>;

或者

my $file_contents = join( "\n", <INPUT_FILE> );

或者你应该总是循环遍历,像这样:

my @array;
while (<INPUT_FILE>) {
  push(@array, $_);
}

我知道在 perl 中有很多方法可以完成任务,我只是想知道是否有在文件中打开和读取的首选/标准方法?

4

12 回答 12

59

没有通用的标准,但有理由偏爱其中一个。我喜欢的形式是这样的:

open( my $input_fh, "<", $input_file ) || die "Can't open $input_file: $!";

原因是:

  • 您立即报告错误。(如果您想要的话,将“die”替换为“warn”。)
  • 您的文件句柄现在是引用计数的,因此一旦您不使用它,它将自动关闭。如果使用全局名称 INPUT_FILEHANDLE,则必须手动关闭文件,否则它将保持打开状态,直到程序退出。
  • 读取模式指示符“<”与 $input_file 分开,增加了可读性。

如果文件很小并且您知道需要所有行,则以下内容非常有用:

my @lines = <$input_fh>;

如果您需要将所有行作为单个字符串处理,您甚至可以这样做:

my $text = join('', <$input_fh>);

对于长文件,您需要使用 while 遍历行,或使用 read。

于 2008-11-25T20:55:46.977 回答
15

如果您希望将整个文件作为单个字符串,则无需遍历它。

use strict;
use warnings;
use Carp;
use English qw( -no_match_vars );
my $data = q{};
{
   local $RS = undef; # This makes it just read the whole thing,
   my $fh;
   croak "Can't open $input_file: $!\n" if not open $fh, '<', $input_file;
   $data = <$fh>;
   croak 'Some Error During Close :/ ' if not close $fh;
}

以上满足perlcritic --brutal,这是测试“最佳实践”的好方法:)。$input_file在这里仍然是未定义的,但其余的是犹太洁食。

于 2008-11-25T21:00:24.267 回答
14

不得不到处写“或死”让我发疯。我打开文件的首选方式如下所示:

use autodie;

open(my $image_fh, '<', $filename);

虽然打字很少,但有很多重要的事情需要注意:

  • 我们正在使用autodie pragma,这意味着如果出现问题,所有 Perl 的内置函数都会抛出异常。它消除了编写or die ...代码的需要,它产生友好的、人类可读的错误消息,并且具有词法范围。它可从 CPAN 获得。

  • 我们正在使用 open 的三参数版本。这意味着即使我们有一个包含<,>或等字符的有趣文件名|,Perl 仍然会做正确的事情。在我在 OSCON 的Perl 安全教程中,我展示了许多让 2-argumentopen行为不端的方法。本教程的注释可从 Perl Training Australia 免费下载

  • 我们正在使用标量文件句柄。这意味着我们不会巧合地关闭其他人的同名文件句柄,如果我们使用包文件句柄,就会发生这种情况。这也意味着strict可以发现拼写错误,并且如果超出范围,我们的文件句柄将被自动清理。

  • 我们正在使用一个有意义的文件句柄。在这种情况下,看起来我们要写入图像。

  • 文件句柄以_fh. 如果我们看到我们像使用常规标量一样使用它,那么我们知道这可能是一个错误。

于 2008-11-29T12:29:11.757 回答
11

如果您的文件足够小,可以将整个内容读入内存,请使用File::Slurp。它使用非常简单的 API 读取和写入完整文件,而且它会执行所有错误检查,因此您不必这样做。

于 2008-11-25T21:42:29.097 回答
6

没有打开和读取文件的最佳方法。这是一个错误的问题。文件里有什么?您在任何时候都需要多少数据?您是否需要一次获取所有数据?你需要对数据做什么?在考虑如何打开和读取文件之前,您需要弄清楚这些。

您现在正在做的任何事情都会给您带来麻烦吗?如果没有,你没有更好的问题要解决吗?:)

您的大部分问题只是语法问题,所有这些都在 Perl 文档中得到了解答(尤其是(perlpentut)。您可能还想学习 Learning Perl,它回答了您在问题中遇到的大部分问题。

祝你好运, :)

于 2008-11-25T22:06:59.157 回答
5

对于 OO,我喜欢:

use FileHandle;
...
my $handle = FileHandle->new( "< $file_to_read" );
croak( "Could not open '$file_to_read'" ) unless $handle;
...
my $line1 = <$handle>;
my $line2 = $handle->getline;
my @lines = $handle->getlines;
$handle->close;
于 2008-11-26T01:47:16.280 回答
5

诚然,在 Perl 中打开文件的最佳方法有很多

$files_in_the_known_universe * $perl_programmers

...但是看看谁通常以哪种方式做这件事仍然很有趣。我喜欢的 slurping 形式(一次读取整个文件)是:

use strict;
use warnings;

use IO::File;

my $file = shift @ARGV or die "what file?";

my $fh = IO::File->new( $file, '<' ) or die "$file: $!";
my $data = do { local $/; <$fh> };
$fh->close();

# If you didn't just run out of memory, you have:
printf "%d characters (possibly bytes)\n", length($data);

当逐行进行时:

my $fh = IO::File->new( $file, '<' ) or die "$file: $!";
while ( my $line = <$fh> ) {
    print "Better than cat: $line";
}
$fh->close();

当然需要注意的是:这些只是我在日常工作中致力于肌肉记忆的方法,它们可能根本不适合你试图解决的问题。

于 2008-11-26T04:14:20.383 回答
5

我曾经使用过

open (FILEIN, "<", $inputfile) or die "...";
my @FileContents = <FILEIN>;
close FILEIN;

定期样板。如今,我File::Slurp用于我想完全保存在内存中的小文件,以及Tie::File我想可伸缩地寻址的大文件和/或我想就地更改的文件。

于 2008-11-26T12:27:05.207 回答
3

用一行将整个文件 $file 读入变量 $text

$text = do {local(@ARGV, $/) = $file ; <>};

或作为一个函数

$text = load_file($file);
sub load_file {local(@ARGV, $/) = @_; <>}
于 2008-11-30T00:32:23.017 回答
2

如果这些程序只是为了提高您的工作效率,那么无论如何都行得通!尽可能多地构建您认为需要的错误处理。

如果文件很大,则读取整个文件可能不是长期做事的最佳方式,因此您可能希望在行进入时对其进行处理,而不是将它们加载到数组中。

我从 The Pragmatic Programmer (Hunt & Thomas) 中的一章中得到的一个提示是,您可能希望脚本在开始切片和切块之前为您保存文件的备份。

于 2008-11-25T21:01:29.913 回答
2

运算符具有更高的||优先级,因此在将结果发送到“open”之前首先对其进行评估......在您提到的代码中,请改用“or”运算符,您就不会遇到这个问题。

open INPUT_FILE, "<$input_file"
  or die "Can't open $input_file: $!\n";
于 2008-12-01T10:32:46.407 回答
1

Damian Conway 这样做:

$data = readline!open(!((*{!$_},$/)=\$_)) for "filename";

但我不建议你这样做。

于 2008-12-01T04:02:24.930 回答