在Perl中读取SIMPLE文件

Bef*_*all 13 perl file

Perl如何读取文件,它如何告诉它前进到文本文件中的下一行,以及它如何使它读取.txt文件中的所有行,直到它到达项目"banana"?

Kon*_*rak 22

基本上,有两种方法可以读取文件:

  1. 压缩文件意味着一次读取文件.这会占用大量内存并需要一段时间,但之后整个文件内容都在内存中,你可以用它做你想做的事情.
  2. 如果您不想读取整个文件(例如,当您到达"banana"时停止),则每行读取一个文件行(在while循环中)会更好.

对于这两种方法,您需要使用"open"命令创建FILEHANDLE,如下所示:

open(my $yourhandle, '<', 'path/to/file.txt') # always use a variable here containing filename
    or die "Unable to open file, $!";
Run Code Online (Sandbox Code Playgroud)

然后你可以通过将文件放入一个数组来啜饮文件:

my @entire_file=<$yourhandle>; # Slurp!
Run Code Online (Sandbox Code Playgroud)

或者使用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.
}
Run Code Online (Sandbox Code Playgroud)

之后,不要忘记关闭文件.

close($yourhandle)
    or warn "Unable to close the file handle: $!";
Run Code Online (Sandbox Code Playgroud)

这只是基础知识..与文件有很多关系,特别是在异常处理中(当文件不存在时,该文件不可读,正在写入时该怎么办),所以你必须阅读或要求:)

  • 现在使用词法文件句柄需要成为标准做法,特别是在向新手谈论系统时.我已经编辑了你的问题以反映现代实践,因为它被接受为答案.如果您对此感到非常强烈,请继续并将其还原,但请考虑保留它.在没有充分理由的情况下教导人们10年以上的做事方式对于Perl社区来说真的是一种伤害. (4认同)
  • Konerak,你能开始使用和教授词法文件句柄吗? (3认同)

dao*_*oad 19

René和Konerak写了几个非常好的回复,展示了如何打开和读取文件.不幸的是,他们在推广最佳实践方面存在一些问题.所以,我会迟到参加聚会并尝试添加最佳实践方法的清晰解释以及为什么最好使用最佳实践方法.

什么是文件句柄?

一个文件句柄是我们用来表示文件本身的名称.如果要对文件进行操作(读取,写入,移动等),请使用文件句柄指示要操作的文件.文件句柄与文件的名称或路径不同.

变量范围和文件句柄

变量的范围决定了程序的哪些部分可以看到变量.一般来说,最好将每个变量的范围保持在尽可能小的范围内,以便复杂程序的不同部分不会相互破坏.

在Perl中严格控制变量范围的最简单方法是使其成为词法变量.词法变量仅在声明它们的块内可见.使用my声明一个词法变量:my $foo;

# Can't see $foo here

{   my $foo = 7;
    print $foo;
}

# Can't see $foo here
Run Code Online (Sandbox Code Playgroud)

Perl文件句柄可以是全局的或词法的.当您使用open with a bare word(没有引号或sigil的文字字符串)时,您将创建一个全局句柄.当您在未定义的词法标量上打开时,您将创建一个词法句柄.

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;
Run Code Online (Sandbox Code Playgroud)

全局文件句柄的一个大问题是它们在程序中的任何位置都可见.因此,如果我在子程序中创建一个名为FOO的文件句柄,我必须非常小心地确保我在另一个例程中不使用相同的名称,或者如果我使用相同的名称,我必须绝对确定在任何情况下都不能他们互相冲突.简单的替代方法是使用不能具有相同类型名称冲突的词法句柄.

词法句柄的另一个好处是很容易将它们作为子例程参数传递.

open功能

open功能具有各种功能.它可以运行子进程,读取文件,甚至提供标量内容的句柄.您可以为它提供许多不同类型的参数列表.它非常强大和灵活,但这些功能带来了一些问题(执行子进程不是你想要意外做的事情).

对于打开文件的简单情况,最好始终使用3参数形式,因为它可以防止意外激活所有这些特殊功能:

open FILEHANDLE, MODE, FILEPATH
Run Code Online (Sandbox Code Playgroud)

FILEHANDLE 是要打开的文件句柄.

MODE是如何打开文件,>覆盖,'>> for write in append mode,+> for read and write, and<`进行读取.

FILEPATH 是要打开的文件的路径.

成功时,open返回真值.失败时,$!设置为指示错误,并返回false值.

因此,要创建一个带有3参数的词法文件句柄open,我们可以使用它来读取文件:

open my $fh, '<', $file_path;
Run Code Online (Sandbox Code Playgroud)

逻辑返回值可以轻松检查错误:

open my $fh, '<', $file_path
    or die "Error opening $file_path - $!\n";
Run Code Online (Sandbox Code Playgroud)

我喜欢将错误处理归结为新行并缩进,但这是个人风格.

关闭手柄

当您使用全局句柄时,仔细,明确地关闭每个句柄,这一点至关重要.如果不这样做可能会导致奇怪的错误和可维护性问题.

close FOO;
Run Code Online (Sandbox Code Playgroud)

当变量被销毁时,词法句柄会自动关闭(当引用计数降为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>;
Run Code Online (Sandbox Code Playgroud)

听到人们谈论啜饮文件是很常见的.这意味着立即将整个文件读入变量.

 # Slurp into array
 my @slurp = <$fh>;

 # Slurp into a scalar - uses tricks outside the scope of this answer
 my $slurp;
 { local $/ = undef; $slurp = <$fh>; }
Run Code Online (Sandbox Code Playgroud)

把它们放在一起

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;
}
Run Code Online (Sandbox Code Playgroud)

把它们放在一起 - 特别额外的信用版

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;
}
Run Code Online (Sandbox Code Playgroud)

为什么这么多不同的方式?为什么这么多陷阱?

Perl是一种古老的语言; 它的行李约会一直追溯到1987年.多年来发现了各种设计问题并进行了修复 - 但很少有修复允许损害向后兼容性.

此外,Perl旨在让您灵活地按照自己的意愿行事.这是非常宽容的.关于这一点的好处是,你可以深入到黑暗的深处,并做真正酷的神奇的东西.糟糕的是,如果你忘记锻炼自己的繁荣并且不专注于生成可读的代码,那很容易让自己陷入困境.

仅仅因为你有足够的绳索,并不意味着你必须自己吊死.

  • 哇,真棒。感谢您的如此深入的回应,我真的很感激。这有助于我大量了解该过程。 (2认同)