为什么我的Perl脚本在关闭同一个文件的同时仍在读取该文件?
我正在编写这个Perl脚本,它得到两个命令行参数:一个目录和一年。该目录中有大量文本文件或html文件(取决于年份)。比如说,2010年包含的文件看起来像是2001到2212之间的为什么我的Perl脚本在关闭同一个文件的同时仍在读取该文件?,perl,file,Perl,File,我正在编写这个Perl脚本,它得到两个命令行参数:一个目录和一年。该目录中有大量文本文件或html文件(取决于年份)。比如说,2010年包含的文件看起来像是2001到2212之间的rank.html。我希望它能够单独打开每个文件,并在html文件中获取部分标题,然后将其打印到文本文件中。但是,当我运行代码时,它只是将第一个文件标题打印到文本文件中。似乎它只打开了第一个文件2001rank.html,而没有打开其他文件。我将在下面发布代码,并感谢任何提供帮助的人 my $directory = s
rank.html
。我希望它能够单独打开每个文件,并在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;
my$directory=shift或“必须提供目录\n”;
my$year=班次或“必须供应年份”\n;
除非(-d$directory){
die“错误:目录必须是目录\n”;
}
除非($directory=~m/\/$/){
$directory=“$directory/”;
}
打开列“>COLUMNS$year.txt”或die“无法打开列文件”;
我的$column\u名称;
对于(my$i=2001;my$i=2009){
my$html_file=$directory.$i.“rank.html”;
打开文件,$html\u文件;
#检查是否正确打开,如果没有,跳过它
除非(定义文件号(文件)){
打印“跳过$html\u文件\n”;
下一个
}
$/=“\n”;
我的$line=;
如果(定义的$行){
$column_name=“”;
$\=直到m{};
${CIA-世界概况——国家比较::(.+)}i;
$column_name=$1;
}
否则{
关闭文件;
下一个
}
关闭文件;
}
否则{
my$text_file=$directory.$i.“rank.txt”;
打开文件,$text\u文件;
除非(定义文件号(文件)){
打印“跳过$text\u文件\n”;
下一个
}
$/=“\r”;
我的$line=;
如果(定义的$行){
$column_name=“”;
$u=until/Rank/i;
$\=~/Rank(\s+)国家(\s+)(\s+)日期/i;
$column_name=$3;
}
否则{
关闭文件;
下一个
}
关闭文件;
}
打印“将$column\u名称添加到文本文件\n”;
打印列“$column\u name\n”;
}
封闭柱;
换句话说,
$column\u name
在循环中的每次传递都被设置为相同的内容,即使我知道html文件是不同的。如果您对文件句柄使用本地词汇表而不是全局词汇表进行转换,并且启用严格检查,则调试速度可能会快得多:
use strict;
use warnings;
while (...)
{
# ...
open my $filehandle, $html_file;
# ...
my $line = <$filehandle>;
}
使用严格;
使用警告;
而(…)
{
# ...
打开我的$filehandle,$html\u文件;
# ...
我的$line=;
}
这样,文件句柄将在每次循环迭代期间超出范围,因此您可以更清楚地看到引用的确切内容和位置。(提示:您可能错过了关闭文件句柄的条件,因此下次会不正确地重用它。)
有关使用打开
和文件句柄的最佳做法的更多信息,请参阅:
- 永远不要显式地分配给
,这是自找麻烦。声明自己的变量以保存数据:$\uuu
(如上面的示例所示)my$line=
- 直接将匹配项提取到变量中,而不是使用
、$1
等,并且只对实际需要的部分使用括号:$2
my($column\u name)=($line=~m/Rank\s+Country\s++(\s+)Date/i)代码>
- 把错误条件放在第一位,这样你的大部分代码就可以超过一个(或多个)级别。这将提高可读性,因为当您的算法的大部分立即在屏幕上可见时,您可以更好地可视化它正在做什么并捕获错误
grep
仅从包含标题的HTML中取出一行,然后处理grep
的输出
更简单,因为您不必编写任何文件处理代码。你没有说你想要什么标题-如果你只需要一个列表,你可能根本不需要写任何代码
尝试以下方法:
grep -ri title <directoryname>
grep-ri标题
对于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);
}
将
$fh
作为参数传递或将其填充在散列中要好得多。此外,词法文件句柄在超出范围时自动关闭。不可能踩到其他人已经在使用的句柄。使用词法文件句柄。为什么您要使用fileno
检查open
的成功?我应该如何检查成功?使用open调用的结果检查成功,但您确实应该先阅读文档:始终!:)谢谢你的建议,我试过了,但还是不行。有没有一种方法可以让我看到文件句柄使用的是什么文件?那么,如果行在$line中,我如何将匹配直接输入到$column\u name中呢?@Silmaril89:使用=~
操作符:请看(或)我遵循了你的建议,老实说,我不确定使它工作的新代码有什么不同,但现在确实如此。所以,谢谢你。@Silmaril89:woohoo!起初,我认为问题可能在于您打开和关闭文件句柄的方式(因此我构建了基于此的原始响应),但我认为真正的问题在于以下几行:$\u=~/Rank(\s+)Country(\s+)(\s+)Date/I$列名=$3代码>--您将第三个匹配项抓取到$column\u name中,但有四组匹配的括号(或者您计算错误,或者可能认为匹配项从0开始计数?)
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: $!";