首页 > 解决方案 > Perl,找到匹配项并在 perl 中读取下一行

问题描述

我想用

myscript.pl targetfolder/*

从 ASCII 文件中读取一些数字。

脚本文件

@list = <@ARGV>;

# Is the whole file or only 1st line is loaded?

foreach $file ( @list ) {
    open (F, $file);
}

# is this correct to judge if there is still file to load?

while ( <F> ) {
    match_replace()
}

sub match_replace {

    # if I want to read the 5th line in downward, how to do that?
    # if I would like to read multi lines in multi array[row],
    # how to do that?

    if ( /^\sName\s+/ ) {
        $name = $1;
    }               
 }

标签: perl

解决方案


我建议彻底阅读perlintro- 它会给你很多你需要的信息。附加评论:

  1. 始终使用strictwarnings。第一个将强制执行一些良好的编码实践(例如声明变量),第二个将通知您潜在的错误。例如,您显示的代码产生的一个警告是readline() on unopened filehandle F,为您提供当时F未打开的提示(更多内容见下文)。

  2. @list = <@ARGV>;:这有点棘手,我不推荐它 - 你实际上是在使用glob,并且扩展targetfolder/*是你的 shell 应该做的事情,如果你在 Windows 上,我建议你Win32::Autoglob不要手动做。

  3. foreach ... { open ... }: 一旦你打开文件,你就不会对它们做任何事情——从文件中读取的循环需要在foreach.

  4. “是整个文件还是只加载了第一行?” open不从文件中读取任何内容,它只是打开它并提供一个文件句柄(您已命名F),然后您需要从中读取。

  5. 我强烈建议您使用更现代的三参数形式open并检查它是否有错误,以及使用词法文件句柄,因为它们的范围不是全局的,如open my $fh, '<', $file or die "$file: $!";.

  6. “这样判断是否还有文件要加载是否正确?” 是的,while (<$filehandle>)这是一种逐行读取文件的好方法,当从文件中读取所有内容时,循环将结束。您可能希望使用更明确的形式while (my $line = <$filehandle>),以便您的变量有一个名称,而不是默认$_变量 - 它确实使代码更加冗长,但如果您刚刚开始,这可能是一件好事。

  7. match_replace():您没有将任何参数传递给sub. 尽管此代码可能仍然“工作”,但它会将当前行传递给sub全局$_变量,这不是一个好习惯,因为一旦脚本开始变长,它就会令人困惑且容易出错。

  8. if (/^\sName\s+/){$name = $1;}: 既然你命名了sub match_replace,我猜你想做一个搜索和替换操作。在 Perl 中,这称为s/search/replacement/,您可以在perlrequick和中阅读有关它的信息perlretut。至于您显示的代码,您正在使用$1,但您的正则表达式中没有任何“捕获组”((...)) - 您也可以在这两个链接中阅读相关内容。

  9. “如果我想向下阅读第 5 行,该怎么做?” 与 Perl 中的往常一样,有不止一种方法可以做到这一点 (TIMTOWTDI)。一种方法是使用范围运算符..next if 1..4;- 您可以通过在 while 循环的开头说跳过第一行到第四行,这将针对$.跟踪最近读取的行号的特殊变量测试这些行号。

  10. “如果我想读取多数组 [row] 中的多行,该怎么做?” 一种方法是使用push将当前行添加到数组的末尾。由于将文件的行保存在数组中会占用更多内存,尤其是对于大文件,我强烈建议您仔细考虑要在此处使用的算法。您还没有解释为什么要将事物保存在数组中,所以我在这里不能更具体。

所以,说了这么多,这就是我可能编写该代码的方式。我已经添加了一些调试代码Data::Dumper- 查看脚本正在使用的数据总是很有帮助的。

#!/usr/bin/env perl
use warnings;
use strict;
use Data::Dumper; # for debugging
$Data::Dumper::Useqq=1;

for my $file (@ARGV) {
    print Dumper($file);  # debug
    open my $fh, '<', $file or die "$file: $!";
    while (my $line = <$fh>) {
        next if 1..4;
        chomp($line);  # remove line ending
        match_replace($line);
    }
    close $fh;
}

sub match_replace {
    my ($line) = @_;  # get argument(s) to sub
    my $name;
    if ( $line =~ /^\sName\s+(.*)$/ ) {
        $name = $1;
    }
    print Data::Dumper->Dump([$line,$name],['line','name']);  # debug
    # ... do more here ...
}

上面的代码明确地循环@ARGV并打开每个文件,我在上面确实说过,更详细的代码有助于理解正在发生的事情。我只是想指出 Perl 的一个不错的特性,即“魔术”<>运算符(在perlop“I/O 运算符”下讨论),它将自动打开文件@ARGV并从中读取行。(只有一件小事,如果我想使用$.变量并让它计算每个文件的行数,我需要使用continue下面显示的块,这在 中进行了解释eof。)这将是一种更“惯用”的方式编写第一个循环:

while (<>) {  # reads line into $_
    next if 1..4;
    chomp;    # automatically uses $_ variable
    match_replace($_);
} continue { close ARGV if eof }  # needed for $. (and range operator)

推荐阅读