在 Perl 中打开和读取文件的最佳方法是啥?

Posted

技术标签:

【中文标题】在 Perl 中打开和读取文件的最佳方法是啥?【英文标题】:What's the best way to open and read a file in Perl?在 Perl 中打开和读取文件的最佳方法是什么? 【发布时间】:2010-09-24 01:03:52 【问题描述】:

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

在我的 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 中有很多方法可以完成任务,我只是想知道是否有打开和读取文件的首选/标准方法?

【问题讨论】:

perlopentut 手册页很好地涵盖了这个问题的大部分内容。 【参考方案1】:

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

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

原因是:

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

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

my @lines = <$input_fh>;

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

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

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

【讨论】:

或稍有变化...打开我的 $input_fh, ' 我仍然认为这是样板文件。只需使用File::SlurpTie::File 还要考虑use autodie;,默认情况下这会使您的 IO 操作致命。比到处写“或死”更容易。 这很好的几个原因:1)文件句柄在词法范围内,而不是包(全局),所以你不太可能让其他代码意外使用它 2)你可以轻松地将文件句柄传递给子例程,而不会弄乱 typeglobs 3) 分离读取模式指示符“”字符开头(例如),它可以防止不良影响。 我用完后还是关闭就可以了吗?如果我拨打 close($input_fh) 会发生什么?【参考方案2】:

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

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 在这里仍然是未定义的,但其余的都是洁净的。

【讨论】:

local $RS = undef; 是什么意思?做什么? $RSEnglish 为您设置的 $/ 相同。 $/ 是跟踪 &lt;$fh&gt;row seperator 值的变量,它与 get-line $fh-&gt;getline() 的概念同义。本质上,它包含内部读取算法用来知道何时读取完整的line 数据的值,并将其设置为undef 意味着“没有指示完整行的标记”,因此它读取整个文件作为“行”【参考方案3】:

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

use autodie;

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

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

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

我们使用的是三参数版本的 open。这意味着即使我们有一个有趣的文件名包含诸如&lt;&gt;| 之类的字符,Perl 仍然会做正确的事情。在我在 OSCON 的 Perl 安全 教程中,我展示了一些让 2-argument open 行为不端的方法。本教程的注释可用于free download from Perl Training Australia。

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

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

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

【讨论】:

伟大的洞察力,谢谢!我也从未见过“打开”的 3 参数方法 - 我想我喜欢这种方式!谢谢!【参考方案4】:

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

【讨论】:

File::Slurp 很棒,但比 Kent Fredric 的直接阅读要慢很多。 (大约 4000 个 10-30k 文件在 7 秒内直接与 56 秒每 nytprof 吞食)【参考方案5】:

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

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

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

祝你好运,:)

【讨论】:

所以也许我不应该问打开/读取文件的最佳方法是什么,但大多数人会做什么。我已经编写了数百个打开文件的 perl 程序,只是想确保我以一种好的方式进行。我没有遇到任何问题——我只是好奇其他人是怎么做到的。谢谢! 再次阅读第一段。最好的方法取决于你在做什么。 我并不是说 Perl::Critic 是法律,但是在“Learning Perl”中打开文件的许多方法都没有通过 Perl::Critic。其实我以前一直打开文件的方式就是我在《Learning Perl》中学习的方式。我认为最佳实践可以应用于需要打开文件的大多数情况,并且您不需要知道微小的细节 - 否则我会问,“打开二进制文件并计数的最佳方法是什么?字节”或类似的东西。我打开的 99% 的文件都是纯文本,我只想把它读入一个数组。我有兴趣了解最佳做法 那你一定有一个旧的Learning Perl。 那可能是 - 2002 年第三次添加 - 我将不得不寻找更新的版本。【参考方案6】:

确实,在 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();

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

【讨论】:

【参考方案7】:

我曾经用过

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

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

【讨论】:

【参考方案8】:

对于 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;

【讨论】:

是的,它将与“迭代运算符”一起使用,但您也可以使用 $handle->getline 或 $handle->getlines【参考方案9】:

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

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

或作为函数

$text = load_file($file);
sub load_file local(@ARGV, $/) = @_; <>

【讨论】:

【参考方案10】:

如果这些程序只是为了提高您的工作效率,不管怎样都行!尽可能多地构建您认为需要的错误处理。

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

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

【讨论】:

【参考方案11】:

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

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

【讨论】:

【参考方案12】:

达米安·康威是这样做的:

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

但我不建议你这样做。

【讨论】:

它将 $/ 设置为 undef(slurp 模式)并将 \$_ 分配给 *"";将引用分配给 glob 只会替换引用类型的插槽,因此 $"" 是 $_ 的别名(其值为 "filename")。这 !否定赋值的值(1,因为标量上下文中的列表赋值给出了赋值右侧的元素数)所以是假的。 open 将 false 值视为 "",因此打开 *"" 文件句柄,并且一个 arg open 从 glob 的标量中获取要打开的文件名。如果 open 返回 true,readline 也会处理 ! 给出的 false作为 *"" 文件句柄

以上是关于在 Perl 中打开和读取文件的最佳方法是啥?的主要内容,如果未能解决你的问题,请参考以下文章

在 Java 中读取精灵表的最佳方法是啥?

在 Java 应用程序中读取 XML 文件的最佳/最简单方法是啥? [关闭]

从 pdf 文件导入/读取数据的最佳方法是啥?

在 asp.net-mvc 中从服务器读取文本文件的最佳方法是啥

保持从文件中读取的字符串值列表的最佳java方法是啥[关闭]

通过网络读取和解析大型文本文件的最佳方式是啥?