我正在编写这个获取两个命令行参数的Perl脚本:一个目录和一年。在这个目录中有大量的文本文件或html文件(取决于年份)。让我们说比如2010年,它包含的文件看起来像这个<number>rank.html
,数字从2001到2212不等。我希望它单独打开每个文件,并在html文件中取一部分标题并打印它到一个文本文件。但是,当我运行我的代码时,它只是将第一个文件标题打印到文本文件。它似乎只打开第一个文件2001rank.html而没有其他人。我将在下面发布代码并感谢任何有帮助的人。
my $directory = shift or "Must supply directory\n";
my $year = shift or "Must supply year\n";
unless (-d $directory) {
die "Error: Directory must be a directory\n";
}
unless ($directory =~ m/\/$/) {
$directory = "$directory/";
}
open COLUMNS, "> columns$year.txt" or die "Can't open columns file";
my $column_name;
for (my $i = 2001; $i <= 2212; $i++) {
if ($year >= 2009) {
my $html_file = $directory.$i."rank.html";
open FILE, $html_file;
#check if opened correctly, if not, skip it
unless (defined fileno(FILE)) {
print "skipping $html_file\n";
next;
}
$/ = "\n";
my $line = <FILE>;
if (defined $line) {
$column_name = "";
$_ = <FILE> until m{</title>};
$_ =~ m{<title>CIA - The World Factbook -- Country Comparison :: (.+)</title>}i;
$column_name = $1;
}
else {
close FILE;
next;
}
close FILE;
}
else {
my $text_file = $directory.$i."rank.txt";
open FILE, $text_file;
unless (defined fileno(FILE)) {
print "skipping $text_file\n";
next;
}
$/ = "\r";
my $line = <FILE>;
if (defined $line) {
$column_name = "";
$_ = <FILE> until /Rank/i;
$_ =~ /Rank(\s+)Country(\s+)(.+)(\s+)Date/i;
$column_name = $3;
}
else {
close FILE;
next;
}
close FILE;
}
print "Adding $column_name to text file\n";
print COLUMNS "$column_name\n";
}
close COLUMNS;
换句话说,即使我知道html文件不同,$column_name
在循环中的每次传递都被设置为相同的东西。
答案 0 :(得分:5)
如果你使用本地词法转换为文件句柄而不是全局变量,你可能能够更快地调试它,以及打开严格的检查:
use strict;
use warnings;
while (...)
{
# ...
open my $filehandle, $html_file;
# ...
my $line = <$filehandle>;
}
这样,在每次循环迭代期间,文件句柄将超出范围,因此您可以更清楚地看到引用的内容和位置。 (提示:您可能错过了文件句柄关闭的情况,因此下次不正确地重复使用。)
有关open
和文件句柄的最佳做法的更多信息,请参阅:
其他一些观点:
$_
,这就是要求麻烦。声明自己的变量来保存您的数据:my $line = <$filehandle>
(如上例所示)$1
,$2
等,并且仅对您实际需要的部分使用括号:my ($column_name) = ($line =~ m/Rank\s+Country\s+.+(\s+)Date/i);
如果您应用以上几点我很确定您会发现您的错误。我在进行最后一次编辑时发现了它,但我想如果你自己发现它,你会学到更多。 (我不是想要傲慢;相信我!)
答案 1 :(得分:2)
您对HTML和文本文件的处理方式类似,因此请简化您的生活,并将其分解为常见部分:
sub scrape {
my($path,$pattern,$sep) = @_;
unless (open FILE, $path) {
warn "$0: skipping $path: $!\n";
return;
}
local $/ = $sep;
my $column_name;
while (<FILE>) {
next unless /$pattern/;
$column_name = $1;
last;
}
close FILE;
($path,$column_name);
}
然后使其特定于两种类型的输入:
sub scrape_html {
my($directory,$i) = @_;
scrape $directory.$i."rank.html",
qr{<title>CIA - The World Factbook -- Country Comparison :: (.+)</title>}i,
"\n";
}
sub scrape_txt {
my($directory,$i) = @_;
scrape $directory.$i."rank.txt",
qr/Rank\s+Country\s+(.+)\s+Date/i,
"\r";
}
然后你的主程序很简单:
my $directory = shift or die "$0: must supply directory\n";
my $year = shift or die "$0: must supply year\n";
die "$0: $directory is not a directory\n"
unless -d $directory;
# add trailing slash if necessary
$directory =~ s{([^/])$}{$1/};
my $columns_file = "columns$year.txt";
open COLUMNS, ">", $columns_file
or die "$0: open $columns_file: $!";
for (my $i = 2001; $i <= 2212; $i++) {
my $process = $year >= 2009 ? \&scrape_html : \&scrape_txt;
my($path,$column_name) = $process->($directory,$i);
next unless defined $path;
if (defined $column_name) {
print "$0: Adding $column_name to text file\n";
print COLUMNS "$column_name\n";
}
else {
warn "$0: no column name in $path\n";
}
}
close COLUMNS or warn "$0: close $columns_file: $!\n";
请注意关闭全局文件句柄的注意事项。请使用词法文件句柄,如
open my $fh, $path or die "$0: open $path: $!";
将$fh
作为参数传递或将其填入哈希值要好得多。此外,词法文件句柄在超出范围时会自动关闭。没有机会踩到其他人已经在使用的手柄。
答案 2 :(得分:0)
您考虑过grep吗?
grep
只显示包含标题的HTML行,然后处理grep
的输出。
更简单,因为您不必编写任何文件处理代码。你没有用标题说出你想要的东西 - 如果你只需要一个清单,你可能根本不需要编写任何代码。
尝试类似:
grep -ri title <directoryname>