使用Text::CSV在Perl中解析制表符分隔的文件

6

我正在尝试使用Text::CSV Perl模块来解析制表符分隔的文件。

我要解析的文件是:

#IGNORE COLUMN1 COLUMN2 COLUMN3 COLUMN4
ROW1    x   y   z   a
ROW2    b   c   d   
ROW3    w

请注意,文件是用制表符分隔的。该文件可能有N列和N行。此外,在ROW2的情况下,它有第四个制表符但没有值。对于COLUMN1的值之后,ROW3没有制表符。也就是说,某些列可能具有未定义的值或空值。
到目前为止,我已经开始编写Perl脚本,但在尝试弄清楚如何编写代码回答以下问题时遇到了困难:
找出有多少ROWn。然后对于每个COLUMNn,检查是否有ROWn的值。因此,在这种情况下,COLUMN2COLUMN3COLUMN4将有缺失值。
任何提示和指导都会有所帮助(我是Perl新手)。我已经查看了CPAN Text::CSV页面,但我还没有能够解决这个问题。
#!/usr/bin/perl
use warnings;
use strict;
use v5.12;
use Text::CSV;

my $csv = Text::CSV->new ({
     escape_char         => '"',
     sep_char            => '\t',
     eol                 => $\,
     binary              => 1,
     blank_is_undef      => 1,
     empty_is_undef      => 1,
     });

open (my $file, "<", "tabfile.txt") or die "cannot open: $!";
while (my $row = $csv->getline ($file)) {
    say @$row[0];
}
close($file);

@TLP 是的。基本上,一行可能有多个选项卡,其中值为“空白”(例如,在ROW2中,COLUMN4具有“空”选项卡值,或者该行可能会过早结束,就像在ROW3中一样,在COLUMN1中的w后面没有字符。 - user2402135
8
使用sep_char => "\t",因为'\t'会将分隔符字面上解释为\t而不是制表符。 - Kenosis
@user2402135,你遇到了什么问题?是不知道如何计算$row的元素数量吗?还是不知道如何比较空值和未定义值? - TLP
我明白了,基本上你只是没有报告你遇到的真正错误:即一次性打印所有行。你怎么能这样提出问题而不包括这个非常重要的信息呢?在这个问题上,我认为你可能也不是想让 eol 成为 $\(输出记录分隔符,默认为 undef),而是 $ /(输入记录分隔符,默认为换行符)。 - TLP
@TLP 的第一个问题是“空白”和“缺失”的区别。您是想查找没有正确数量字段的行,还是要查找存在但为空白的字段?在 4 个字段中,“1\t\t3\t4”和“1\t2\t3”都缺少一个值,只有一个缺少了一个字段。 - user557597
显示剩余7条评论
1个回答

1

一种方法是对于每一行,处理每个字段并在其不为false时增加计数器:

#!/usr/bin/env perl

use warnings;
use strict;
use Text::CSV_XS;

my (@col_counter); 
my ($line_counter, $r, $num_cols) = (0, 0, 0); 

open my $fh, '<', shift or die;

my $csv = Text::CSV_XS->new({
    sep_char => qq|\t|
});

while ( my $row = $csv->getline( $fh ) ) { 
    ## First row (header), get the number of columns.
    if ( $line_counter == 0 ) { 
        $num_cols = $#$row;
        next;
    }
    ## For each data row, traverse every column and increment a 
    ## counter if it has any value.
    for ( 1 .. $#$row ) { 
        ++$col_counter[ $_ ] if $row->[ $_ ];
    }   
}
continue {
    $line_counter++;
}

printf qq|Lines of file: %d\n|, $line_counter - 1;
## Check if any column has missing values. For each column compare the 
## number of lines read (substract 1 of header) with its counter. If they
## are different it means that the column had any row without value.
for my $i ( 1 .. $num_cols ) { 
    $r = $line_counter - 1 - (defined $col_counter[ $i ] ? $col_counter[ $i ] : 0); 
    if ( $r > 0 ) { 
        printf qq|Column %d has %d missing values\n|, $i, $r;  
    }   
}

使用您的示例数据,运行如下:
perl script.pl infile

这将产生:

Lines of file: 3
Column 2 has 1 missing values
Column 3 has 1 missing values
Column 4 has 2 missing values

更新:请看评论。我进行反向查找,找出不包含任何值的列,并将当前行号附加到数组中,然后使用join提取所有行。

我改变了哪些部分?在这里保存行号。

for ( 1 .. $num_cols ) { 
    push @{ $col_counter[ $_ ] }, $line_counter unless $row->[ $_ ];
}

在这里打印它们。您需要注释旧的行为。

if ( defined $col_counter[ $i ] ) { 
    printf qq|Column %d has no value in lines %s\n|, $i, join q|,|, @{ $col_counter[ $i ] };
}

它的翻译是:“它产生:”。
Lines of file: 3
Column 2 has no value in lines 3
Column 3 has no value in lines 3
Column 4 has no value in lines 2,3

当我的文件长这样时,这段代码似乎无法正常工作:#IGNORE COLUMN1 COLUMN2 COLUMN3 COLUMN4 row1 x x x row2.它没有意识到 COLUMN4 缺少了两个值。 - user2402135
@user2402135:在您的输入文件中,“column4”有两个缺失值。我不理解您的示例。 - Birei
@user2402135:我已经修复了从标题中提取列数的问题。 - Birei
现在看起来我完全崩溃了。我甚至无法识别任何缺失的值。 - user2402135
@user2402135:我已经更新了脚本。我不喜欢这样做,因为有两个原因:你改变了原问题的要求,并且没有展示出对我已经完成的工作做出任何努力。今天你有点幸运 :-) - Birei
显示剩余7条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接