为什么我的Perl脚本在关闭同一个文件的同时仍在读取该文件?

为什么我的Perl脚本在关闭同一个文件的同时仍在读取该文件?,perl,file,Perl,File,我正在编写这个Perl脚本,它得到两个命令行参数:一个目录和一年。该目录中有大量文本文件或html文件(取决于年份)。比如说,2010年包含的文件看起来像是2001到2212之间的rank.html。我希望它能够单独打开每个文件,并在html文件中获取部分标题,然后将其打印到文本文件中。但是,当我运行代码时,它只是将第一个文件标题打印到文本文件中。似乎它只打开了第一个文件2001rank.html,而没有打开其他文件。我将在下面发布代码,并感谢任何提供帮助的人 my $directory = s

我正在编写这个Perl脚本,它得到两个命令行参数:一个目录和一年。该目录中有大量文本文件或html文件(取决于年份)。比如说,2010年包含的文件看起来像是2001到2212之间的
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: $!";