【问题标题】:How can I use Perl extract a particular column from a tab-separated file?如何使用 Perl 从制表符分隔的文件中提取特定列?
【发布时间】:2011-10-28 22:40:20
【问题描述】:

我是 Perl 的新手,一直在尝试为此拼凑出一个解决方案。当我运行这个程序时,我没有收到任何错误,也没有显示任何内容。

代码如下:

#!/usr/bin/perl
open (DATA, "<test1.txt") or die ("Unable to open file");
use strict; use warnings;
my $search_string = "Ball";
while ( my $row = <DATA> ) {

    last unless $row =~ /\S/;
    chomp $row;
    my @cells = split /\t/, $row;

    if ($cells[0] =~/$search_string/){
        print $cells[0];
    }
}

我的测试数据文件是这样的

Camera Make     Camera Model    Text    Ball    Swing
a       b       c       d       e
f       g       h       i       j
k       l       m       n       o

在我使用实际的测试数据文件之前,我想看看它是如何工作的。

那么我如何搜索说“Ball”并让它返回“d in”

【问题讨论】:

  • 你不是要求帮助 Perl。您要求为您编写作业,因为您上面的代码与您最后一行的要求无关。请编写一个您尝试在 Perl 中实现的算法,然后询问为什么您的 Perl 实现不起作用 - 目前,您的问题的答案是“您的代码与您的任务无关”
  • 我保证这不是一个作业......但无论如何......这怎么不适用......我正在定义我正在寻找的内容并用 /\t/ 分割文本是选项卡我确实相信并将其存储在一个数组中.. 我要问的是“我如何搜索该数组并获取相关的列?
  • 请参阅我的回答中的提示。您正在正确解析文件;您只是没有使用解决问题的逻辑/算法。提示应该足以让你的球滚动(没有双关语)

标签: perl


【解决方案1】:

您没有收到任何错误的原因是因为您的程序完全按照您的要求执行(打印所有包含字符串“Ball”的第一列值)。由于第一列中没有任何单元格包含该字符串,因此您的程序不会打印任何内容。

您的问题不在于您的 Perl(它可以使用一些小的风格改进 - 特别是您使用的是过时的 open() 形式 - 但大部分都很好),它是您的算法

提示:您在算法中的第一个任务应该是找出哪一列(按数字)是“球”列。

【讨论】:

    【解决方案2】:

    试试这个:

    use strict;
    use warnings;
    use Data::Dumper;
    use List::MoreUtils qw<first_index>;
    
    my $column = first_index { $_ eq 'Ball' } split /\t/, <DATA>;
    say Data::Dumper->Dump( [ $column ], [ '*column' ] );
    my @balls  = map { [split /\t/]->[$column] } <DATA>;
    say Data::Dumper->Dump( [ \@balls ], [ '*balls' ] );
    __DATA__
    Camera Make Camera Model    Text    Ball    Swing
    a   b   c   d   e
    f   g   h   i   j
    k   l   m   n   o
    

    您几乎必须将句柄从 DATA 更改为您 open-ed 的某个文件。

    open( my $in, '<', '/path/to/data.file' ) 
        or die "Could not open file: $!"
        ;
    

    然后将&lt;DATA&gt; 替换为&lt;$in&gt;

    【讨论】:

      【解决方案3】:

      试试这个:

      #!/usr/bin/perl
      use strict;
      use warnings;
      
      open (DATA, "<test1.txt") or die ("Unable to open file");
      my $search_string = "Ball";
      
      my $header = <DATA>;
      my @header_titles = split /\t/, $header;
      my $extract_col = 0;
      
      for my $header_line (@header_titles) {
        last if $header_line =~ m/$search_string/;
        $extract_col++;
      }
      
      print "Extracting column $extract_col\n";
      
      while ( my $row = <DATA> ) {
        last unless $row =~ /\S/;
        chomp $row;
        my @cells = split /\t/, $row;
        print "$cells[$extract_col] ";
      }
      

      【讨论】:

      • 这行得通...但我不明白你是如何让它完成它所做的......当你说'my $header = '时,它会获取文件并将其放入进入 $header,然后是 @header 标题,您按制表符拆分头文件,但这不会将列名放在行中而不是列中吗?
      • @David:这不是 的工作方式。在标量上下文中,它从文件中读取一行。 Please read the I/O operators part of the perlop manual.
      • 使用三个参数open
      • @Con 是的。通过使用过时的编译指示,您可以使它们保持活力。始终使用推荐的三个参数 open,你永远不会有问题。
      • @Con 这里真的没有争论。你教没有经验的程序员糟糕的编程实践是错误的。您不妨排除严格和警告,然后在 cmets 中说“我知道我在做什么!”这仍然是糟糕的编程实践,你仍然会因为强化它们而感到内疚。
      【解决方案4】:

      您可以使用Text::CSV_XS 非常方便地为您提取数据。对于您有限的数据来说,这可能有点矫枉过正,但它是一个非常可靠的解决方案。

      这里我只使用 DATA 标记来包含数据,但如果您愿意,可以将其替换为文件句柄,例如 open my $fh, '&lt;', 'text1.txt'; 并将 *DATA 更改为 $fh

      输出:

      d i n
      

      代码:

      use warnings;
      use strict;
      use Text::CSV_XS;
      use autodie;
      
      my $csv = Text::CSV_XS->new( { sep_char => "\t" } );
      my @list;
      $csv->column_names ($csv->getline (*DATA));
      while ( my $hr = $csv->getline_hr(*DATA) ) {
          push @list, $hr->{'Ball'};
      }
      
      print "@list\n";
      __DATA__
      Camera Make Camera Model    Text    Ball    Swing
      a   b   c   d   e
      f   g   h   i   j
      k   l   m   n   o
      

      ETA:如果您要剪切和粘贴来尝试一下,请确保选项卡在数据中被保留。

      【讨论】:

        猜你喜欢
        • 2019-10-01
        • 2011-12-13
        • 2012-11-23
        • 1970-01-01
        • 1970-01-01
        • 2021-12-21
        • 2017-03-17
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多