在 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::Slurp
或Tie::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; 是什么意思?做什么?$RS
与 English
为您设置的 $/
相同。 $/
是跟踪 <$fh>
的 row seperator
值的变量,它与 get-line
或 $fh->getline()
的概念同义。本质上,它包含内部读取算法用来知道何时读取完整的line
数据的值,并将其设置为undef
意味着“没有指示完整行的标记”,因此它读取整个文件作为“行”【参考方案3】:
不得不到处写“或死”让我发疯。我打开文件的首选方式如下所示:
use autodie;
open(my $image_fh, '<', $filename);
虽然打字很少,但有很多重要的事情需要注意:
我们正在使用autodie pragma,这意味着如果出现问题,所有 Perl 的内置函数都会抛出异常。它消除了在代码中编写or die ...
的需要,它产生友好的、人类可读的错误消息,并且具有词法范围。它可以从 CPAN 获得。
我们使用的是三参数版本的 open。这意味着即使我们有一个有趣的文件名包含诸如<
、>
或|
之类的字符,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 应用程序中读取 XML 文件的最佳/最简单方法是啥? [关闭]
在 asp.net-mvc 中从服务器读取文本文件的最佳方法是啥