【发布时间】:2011-02-18 02:29:07
【问题描述】:
Perl 如何读入文件,它如何告诉它前进到文本文件的下一行,以及它如何使它读取 .txt 文件中的所有行,直到,例如,它到达 item “banana” “?
【问题讨论】:
Perl 如何读入文件,它如何告诉它前进到文本文件的下一行,以及它如何使它读取 .txt 文件中的所有行,直到,例如,它到达 item “banana” “?
【问题讨论】:
读取文件基本上有两种方式:
对于这两种方式,您都需要使用“open”命令创建 FILEHANDLE,如下所示:
open(my $yourhandle, '<', 'path/to/file.txt') # always use a variable here containing filename
or die "Unable to open file, $!";
然后您可以通过将文件放入数组来 slurp 文件:
my @entire_file=<$yourhandle>; # Slurp!
或使用 while 循环逐个读取文件
while (<$yourhandle>) { # Read the file line per line (or otherwise, it's configurable).
print "The line is now in the $_ variable";
last if $_ eq 'banana'; # Leave the while-loop.
}
之后,不要忘记关闭文件。
close($yourhandle)
or warn "Unable to close the file handle: $!";
这只是基础知识.. 与文件有很多关系,尤其是在异常处理中(当文件不存在、不可读、正在写入时该怎么办),所以你必须阅读或者问一下:)
【讨论】:
open 和close 在失败时不会生成异常(除非您使用autodie),它们只会返回false。混淆使用重要的技术词汇对新手非常有害。
René 和 Konerak 写了几个很好的回复,展示了如何打开和读取文件。不幸的是,他们在推广最佳实践方面存在一些问题。因此,我会迟到并尝试添加对最佳实践方法的清晰解释以及为什么使用最佳实践方法更好。
什么是文件句柄?
文件句柄是我们用来代表文件本身的名称。当您想要对文件进行操作(读取、写入、移动等)时,请使用文件句柄来指示要操作的文件。文件句柄与文件名或路径不同。
可变范围和文件句柄
变量的作用域决定了在程序的哪些部分可以看到该变量。一般来说,将每个变量的作用域保持在尽可能小的范围内是个好主意,这样复杂程序的不同部分就不会相互破坏。
在 Perl 中严格控制变量范围的最简单方法是使其成为词法变量。词法变量仅在声明它们的块内可见。使用my 声明一个词法变量:my $foo;
# Can't see $foo here
{ my $foo = 7;
print $foo;
}
# Can't see $foo here
Perl 文件句柄可以是全局的或词法的。当您将 open 与一个裸词(不带引号或符号的文字字符串)一起使用时,您将创建一个全局句柄。当你打开一个未定义的词法标量时,你创建了一个词法句柄。
open FOO, $file; # Global file handle
open my $foo, $file; # Lexical file handle
# Another way to get a lexical handle:
my $foo;
open $foo, $file;
全局文件句柄的一个大问题是它们在程序的任何地方都是可见的。所以如果我在子例程中创建了一个名为 FOO 的文件句柄,我必须非常小心,以确保我不会在另一个例程中使用相同的名称,或者如果我使用相同的名称,我必须绝对确定在任何情况下都不能它们相互冲突。简单的替代方法是使用不能有相同类型名称冲突的词法句柄。
词法句柄的另一个好处是很容易将它们作为子例程参数传递。
open 函数
open 函数具有各种功能。它可以运行子进程、读取文件,甚至提供标量内容的句柄。您可以为其提供许多不同类型的参数列表。它非常强大和灵活,但这些功能带有一些陷阱(执行子流程不是您想偶然做的事情)。
对于打开文件的简单情况,最好始终使用 3 参数形式,因为它可以防止意外激活所有这些特殊功能:
open FILEHANDLE, MODE, FILEPATH
FILEHANDLE 是要打开的文件句柄。
MODE是如何打开文件,>是覆盖,'>>for write in append mode,+>for read and write, and
FILEPATH 是要打开的文件的路径。
成功时,open 返回一个真值。失败时,$! 设置为指示错误,并返回 false 值。
因此,要使用 3 参数 open 创建一个词法文件句柄,我们可以使用它来读取文件:
open my $fh, '<', $file_path;
逻辑返回值便于检查错误:
open my $fh, '<', $file_path
or die "Error opening $file_path - $!\n";
我喜欢将错误处理放到一个新行并缩进它,但这是个人风格。
关闭句柄
当您使用全局句柄时,务必小心谨慎地在使用完每个句柄后显式关闭它。不这样做可能会导致奇怪的错误和可维护性问题。
close FOO;
当变量被销毁时词法句柄会自动关闭(当引用计数下降到 0 时,通常是当变量超出范围时)。
在使用词法句柄时,通常依赖于句柄的隐式关闭而不是显式关闭它们。
钻石是 Perl 最好的朋友。
菱形运算符<> 允许我们遍历文件句柄。就像open 它有超能力。我们现在将忽略其中的大部分。 (搜索输入记录分隔符、输出记录分隔符和 NULL 文件句柄的信息以了解它们。)
重要的是,在标量上下文中(例如,分配给标量)它的行为类似于readline 函数。在列表上下文中(例如分配给数组),它的作用类似于 read_all_lines 函数。
假设您要读取包含三个标题行(日期、时间和位置)和一堆数据行的数据文件:
open my $fh, '<', $file_path
or die "Ugh - $!\n";
my $date = <$fh>;
my $time = <$fh>;
my $loc = <$fh>;
my @data = <$fh>;
听到人们谈论啜食文件是很常见的。这意味着一次将整个文件读入一个变量。
# Slurp into array
my @slurp = <$fh>;
# Slurp into a scalar - uses tricks outside the scope of this answer
my $slurp;
{ local $/ = undef; $slurp = <$fh>; }
把它们放在一起
open my $fh, '<', 'my_file'
or die "Error opening file - $!\n";
my @before_banana;
while( my $line = <$fh> ) {
last if $line =~ /^banana$/;
push @before_banana, $line;
}
将所有内容放在一起 - 特别额外信用版
my $fh = get_handle( 'my_file' );
my @banana = read_until( $fh, qr/^banana$/ ); # Get the lines before banana
read_until( $fh, qr/^no banana$/ ); # Skip some lines
my @potato = read_until( $fh, qr/^potato$/ ); # Get the lines before potato
sub get_handle {
my $file_path = shift;
open my $fh, '<', $file_path
or die "Can't open '$file_path' for reading - $!\n";
return $fh;
}
sub read_until {
my $fh = shift;
my $match = shift;
my @lines;
while( my $line = <$fh> ) {
last if $line =~ /$match/;
push @line, $line;
}
return @lines;
}
为什么会有这么多不同的方式?为什么会有这么多陷阱?
Perl 是一门古老的语言;它的包袱可以追溯到 1987 年。多年来,人们发现了各种设计问题并进行了修复——但很少允许修复损害向后兼容性。
此外,Perl 旨在让您灵活地在您想要的时候做您想做的事。这是非常宽容的。这样做的好处是你可以深入到阴暗的深处,做一些非常酷的魔法事情。坏事是,如果你忘记缓和自己的热情,没有专注于编写可读的代码,很容易自取其辱。
仅仅因为你有足够多的绳子,并不意味着你必须上吊。
【讨论】:
$/ 设置为空、未定义或类似的工作)?
$/) 可以设置为任何值。因此可以使用任何表示记录结尾的字节序列。例如,您可以使用0x210xF9 将动画 GIF 拆分为帧(不完美,序列可能出现在图像数据中)。将$/ 设置为undef 也适用于二进制文件。
首先,你必须打开文件:
open (my $SOME_FILEHANDLE, "<", "filename.txt");
您可能想检查文件打开是否成功:
open (my $SOME_FILEHANDLE, "<", "filename.txt") or die "could not open filename";
打开文件后,您可以从 $SOME_FILEHANDLE 中读取每一行。下一行是 <$SOME_FILEHANDLE> 构造:
my $next_line = <$SOME_FILEHANDLE>;
$next_line 在读取最后一行后未定义。所以,你可以把整个事情放到一个while循环中:
while (my $next_line = <$SOME_FILEHANDLE>) {
do_something($next_line);
}
这是有效的,因为未定义的值在 while 条件下计算为 false。
如果你想在遇到“banana”时退出循环,你可能会使用正则表达式来检查香蕉:
while (my $next_line = <$SOME_FILEHANDLE>) {
last if $next_line =~ /banana/;
do_something($next_line);
}
last 运算符退出 while 循环,并在 $next_line 匹配香蕉时“触发”。
【讨论】: