多个文件句柄打开同一个文件 - 这是一个好习惯吗?

zub*_*nel 6 perl filehandle

我有一个包含三列显示学生姓名、科目和成绩的grades.tsv 文件:

Liam    Mathematics 5
Liam    History 6
Liam    Geography   8
Liam    English 8
Aria    Mathematics 8
Aria    History 7
Aria    Geography   6
Isabella    Mathematics 9
Isabella    History 4
Isabella    Geography   7
Isabella    English 5
Isabella    Music   8
Run Code Online (Sandbox Code Playgroud)

我想计算每个学生的平均成绩并将其添加到单独的列中。为此,我使用了两个文件句柄 DATA 和 OUT 打开同一个文件:

use strict;
use warnings;

# Open file with grades for calculation of average grade for each student
open (DATA,"grades.tsv") or die "Cannot open file\n";

my %grade_sums;
my %num_of_subjects;

# Calculate sum of grades and number of subjects for each student
while( <DATA> ) {

   chomp;
   my ($name, $subject, $grade) = split /\t/;

   $grade_sums{$name} += $grade;
   $num_of_subjects{$name} += 1;
}

close DATA;


# Open file with grades again but this time for a purpose of adding a separate column with average grade and printing a result
open (OUT,"grades.tsv") or die "Cannot open file\n";

while ( <OUT> ) {
   chomp;
   my ($name, $subject, $grade) = split /\t/;

   # Calculate average grade
   my $average_grade = $grade_sums{$name} / $num_of_subjects{$name};
   my $outline = join("\t", $name, $subject, $grade, $average_grade);

   # Print a file content with new column
   print "$outline\n";
}

close OUT;
Run Code Online (Sandbox Code Playgroud)

该代码有效,但我不确定它是否适合此任务。这是一种很好的做法还是应该首选更好的方法?

ike*_*ami 7

重新打开文件就好了。一种替代方法是寻找文件的开头。

use Fcntl qw( SEEK_SET );

seek(DATA, 0, SEEK_SET);
Run Code Online (Sandbox Code Playgroud)

搜索更有效,因为它不必检查权限等。它还保证您获得相同的文件(但不是没有人更改它)。

另一种选择是将整个文件加载到内存中。这就是我通常会做的。


注意

open(FH, $qfn) or die "Cannot open file\n";
Run Code Online (Sandbox Code Playgroud)

最好写成

open(my $FH, '<', $qfn)
   or die("Can't open file \"$qfn\": $!\n");
Run Code Online (Sandbox Code Playgroud)
  • 三参数open避免了一些问题。
  • 在错误消息中包含错误原因是有益的。
  • 在错误消息中包含路径是有益的。
  • 应该避免,DATA因为 Perl 有时会自动创建该名称的句柄。
  • 应该避免使用全局变量(例如FH)或词法变量(my $FH)。