perl - 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;
}
}
解决方案
我建议彻底阅读perlintro
- 它会给你很多你需要的信息。附加评论:
始终使用
strict
和warnings
。第一个将强制执行一些良好的编码实践(例如声明变量),第二个将通知您潜在的错误。例如,您显示的代码产生的一个警告是readline() on unopened filehandle F
,为您提供当时F
未打开的提示(更多内容见下文)。@list = <@ARGV>;
:这有点棘手,我不推荐它 - 你实际上是在使用glob
,并且扩展targetfolder/*
是你的 shell 应该做的事情,如果你在 Windows 上,我建议你Win32::Autoglob
不要手动做。foreach ... { open ... }
: 一旦你打开文件,你就不会对它们做任何事情——从文件中读取的循环需要在foreach
.“是整个文件还是只加载了第一行?”
open
不从文件中读取任何内容,它只是打开它并提供一个文件句柄(您已命名F
),然后您需要从中读取。我强烈建议您使用更现代的三参数形式
open
并检查它是否有错误,以及使用词法文件句柄,因为它们的范围不是全局的,如open my $fh, '<', $file or die "$file: $!";
.“这样判断是否还有文件要加载是否正确?” 是的,
while (<$filehandle>)
这是一种逐行读取文件的好方法,当从文件中读取所有内容时,循环将结束。您可能希望使用更明确的形式while (my $line = <$filehandle>)
,以便您的变量有一个名称,而不是默认$_
变量 - 它确实使代码更加冗长,但如果您刚刚开始,这可能是一件好事。match_replace()
:您没有将任何参数传递给sub
. 尽管此代码可能仍然“工作”,但它会将当前行传递给sub
全局$_
变量,这不是一个好习惯,因为一旦脚本开始变长,它就会令人困惑且容易出错。if (/^\sName\s+/){$name = $1;}
: 既然你命名了sub
match_replace
,我猜你想做一个搜索和替换操作。在 Perl 中,这称为s/search/replacement/
,您可以在perlrequick
和中阅读有关它的信息perlretut
。至于您显示的代码,您正在使用$1
,但您的正则表达式中没有任何“捕获组”((...)
) - 您也可以在这两个链接中阅读相关内容。“如果我想向下阅读第 5 行,该怎么做?” 与 Perl 中的往常一样,有不止一种方法可以做到这一点 (TIMTOWTDI)。一种方法是使用范围运算符
..
next if 1..4;
- 您可以通过在 while 循环的开头说跳过第一行到第四行,这将针对$.
跟踪最近读取的行号的特殊变量测试这些行号。“如果我想读取多数组 [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)