【问题标题】:Where to open file in a Perl module在 Perl 模块中打开文件的位置
【发布时间】:2017-12-08 11:00:13
【问题描述】:

下面是我正在编写的模块的玩具版本。这个例子忠实地展示了我遇到的问题。

我有一个文件bed_in.txt

2L      0       4953
2L      16204   16284
2L      16612   16805
2L      17086   18561
2L      18757   18758
2L      19040   19120
2L      19445   19635
2L      19894   21366
2L      21582   21583
2L      47501   52365
2L      4698700 4709369

我想用来过滤数组中的值。

我想检查bed_in.txt的第二列是否匹配任何值

my @lines = qw/ 16204 40 200 149 19445 178 /;

如果是,则将 this 推送到数组。

我对这些数据的最终输出应该是@lines 中的两个元素,它们也在bed_in.txt 的第二列中

2L      0       4953
2L      16204   16284 # 16204 == 16204
2L      16612   16805
2L      17086   18561
2L      18757   18758
2L      19040   19120
2L      19445   19635 # 19445 == 19445

但是我应该在哪里打开我的文件?

这应该发生在调用我的模块run_fileOpen.pl 的脚本中,还是发生在模块本身中?

在任何一种情况下,我都没有得到正确的输出,而且我的模块似乎只为 @lines 的第一个元素调用

parseFile.pm

#!/usr/bin/perl

package parseFile;
use strict;
use warnings;

use FindBin qw/ $Bin /;
use lib File::Spec->catdir($FindBin::Bin, '..', 'bin/');

use Data::Printer;

open my $bed, '<', 'bed_in.txt';

sub check_lines {
  my ($line, $filter_ref) = @_;
  my @filter_reasons = @{ $filter_ref };

  my $lines_to_filter_ref = filter_lines($line, $bed, \@filter_reasons);

  return($lines_to_filter_ref);
}


sub filter_lines {
  my ($line, $bed_file, $lines_to_filter) = @_;

  my @filter = @{ $lines_to_filter };

  while(<$bed_file>){
    chomp;
    my $start = (split)[1];
    # print "line: $line  start: $start\n";
    if ($line == $start ) {

      push @filter, "Filter as equal: $start, $line [$_]";
    }
  }
  # p(@filter);
  return(\@filter);
}


1;

run_parseFile.pl

#!/usr/bin/perl

use strict;
use warnings;

use parseFile;


my @lines = qw/ 16204 40 200 149 19445 178 /;

foreach(@lines){
  my @filter_reasons = 'reason 1';
  my $lines2filter = parseFile::check_lines($_, \@filter_reasons);
  @filter_reasons = @{ $lines2filter };
  print "$_\n" foreach @filter_reasons;
}

输出

reason 1
Filter as equal: 16204, 16204 [2L   16204   16284]
reason 1
reason 1
reason 1
reason 1
reason 1

当我在filter_lineswhile 循环中添加print 语句时,我可以看到只有第一个元素正在运行:

line: 16204  start: 0
line: 16204  start: 16204
line: 16204  start: 16612
line: 16204  start: 17086
line: 16204  start: 18757
line: 16204  start: 19040
line: 16204  start: 19445
line: 16204  start: 19894
line: 16204  start: 21582
line: 16204  start: 47501
line: 16204  start: 4698700

这是处理打开我的bed_in.txt 文件的合适方法,还是应该在run_parseFile.pl 脚本中打开它?或者我的代码还有其他完全错误的地方吗?

【问题讨论】:

  • bed_in.txt 在现实中有多大?
  • 一个模块通常不应该打开一个静态路径的文件,这就是为什么它被称为模块。它应该可以在不同的上下文中使用。此外,它不应该在正文中打开文件(在编译时),但只能在调用方法/子例程时打开。否则它不是完全可测试的。
  • @Borodin 21154 行
  • 您只看到一个检查值的原因是因为第一次调用filter_lines 会将文件读取到最后。随后的调用在 eof 处找到文件句柄,因此没有可读取的内容,并且永远不会再次执行 while。你可以在filter_lines 中打开文件来解决这个问题,但是如果总是有多个值要测试,那么效率非常低。无论如何,我倾向于把它做成一个面向对象的模块。
  • 此代码所需的输出是什么? Filter as equal 输出只是调试代码吗?

标签: perl module file-handling


【解决方案1】:

我会这样写。正如我所说,我更喜欢面向对象的解决方案

new 构造函数需要一个文件名作为其参数。它打开文件,将信息读入哈希,并祝福生成对象

唯一的方法是check_lines,它需要一个值和一个原因字符串。它只是询问给定值的哈希值,并根据是否找到该值返回一个或两个元素的数组

最好限制这个对象的范围,否则哈希在需要后会保存在内存中。因此,我将所有需要对象的操作都包含在一个块中。对象在块结束时超出范围会自动销毁

BedInFilter.pm

package BedInFilter;

use strict;
use warnings 'all';

use Carp 'croak';

sub new {
    my $class = shift;
    my ( $bed_file ) = @_;

    my %self;

    open my $fh, '<', $bed_file
            or croak qq{Unable to open "$bed_file" for input: $!};

    while ( <$fh> ) {

        next unless /\S/;
        chomp;

        my $val = ( split )[1];
        $self{$val} = $_;
    }

    bless \%self, $class;
}

sub check_lines {
    my $self = shift;
    my ( $val, $reason ) = @_;

    my $result = [ $reason ];

    my $line = $self->{$val};

    push @$result, "Filter as equal: $val, $val [$line]" if $line;

    $result;
}

1;

bed_in.pl

use strict;
use warnings 'all';

use BedInFilter;

my @values = qw/ 16204 40 200 149 19445 178 /;

{
    my $filter = BedInFilter->new( 'bed_in.txt' );

    for my $val ( @values ) {

        my $reason = 'reason1';

        my $result = $filter->check_lines( $val, $reason );

        print "@$result\n";
    }
}

输出

reason1 Filter as equal: 16204, 16204 [2L      16204   16284]
reason1
reason1
reason1
reason1 Filter as equal: 19445, 19445 [2L      19445   19635]
reason1

【讨论】:

    【解决方案2】:

    一个模块应该在同一个子程序中打开和关闭文件,除非子程序的名称中有“open”,在这种情况下,它应该返回一个文件句柄作为它的返回值之一。并且应该有一个相应的“关闭”子来匹配每个“打开”子。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-04-14
      • 2014-05-03
      • 1970-01-01
      • 2014-10-06
      • 1970-01-01
      • 1970-01-01
      • 2011-11-02
      • 2021-03-15
      相关资源
      最近更新 更多