【问题标题】:Perl grep through large file to match a stringPerl grep 通过大文件匹配字符串
【发布时间】:2020-05-25 23:40:23
【问题描述】:

我有一个包含元素列表的数组 (@array)。我需要检查这些每个元素是否存在于主文件中。如果元素存在于主文件中,那么在主文件的同一行中,字符串YES(在第 5 位)也应该存在。并且元素应该存储在不同的数组中。

实际上我的脚本使用两个grep shell 命令来实现这一点。我怎样才能在 Perl do grep 中写同样的东西。

...
use Data::Dumper;

my @new_array;
my @array = ('RT0AC1', 'WG3RA3');

print Dumper(\@array);

foreach ( @array ){
    my $line = `grep $_ "master_file.csv" | grep -i yes`;
    next unless($line);
    push( @new_array, $_ );
}

print Dumper(@new_array);
...

master_file.csv 如下所示:

101,RT0AC1,CONNECTED,FAULTY,NO
102,RT0AC1,CONNECTED,WORKING,YES
103,RT0AC1,NOT CONNECTED,WORKING,NO
104,WG3RA3,NOT CONNECTED,DISABLED,NO
105,WG3RA3,CONNECTED,WORKING,NO

所以在这里我将$line 值作为102,RT0AC1,CONNECTED,WORKING,YES 和元素RT0AC1 存储在@new_array 中。

如何避免使用反引号(`)和两个 grep 来实现这一点。我正在尝试使用纯 Perl 来做到这一点。 master_file.csv 还包含数百万条记录。

【问题讨论】:

  • 你要找的词总是在 csv 文件的第二列吗?
  • @Shawn 是的。

标签: perl


【解决方案1】:

由于您要查找的所有单词都在同一个位置,因此很容易将当前行用逗号分开,并查看第二列是否存在于哈希表中,以及第五列是否等于 @ 987654321@:

#!/usr/bin/env perl
use strict;
use warnings;
use autodie;
use Data::Dumper;

my $filename = shift // "master_file.csv"; # Default filename if not given on command line
my @array = qw/RT0AC1 WG3RA3/; # Words you're looking for
my %words = map { $_ => 1 } @array; # Store them in a hash for fast lookup
my @new_array;

# Use Text::CSV_XS for non-trivial CSV files
open my $csv, "<", $filename;
while (<$csv>) {
    chomp;
    my @F = split /,/;
    push @new_array, $F[1] if exists $words{$F[1]} && $F[4] eq "YES";
}

print Dumper(\@new_array);

【讨论】:

  • 感谢@shawn。弹出警告Use of uninitialized value $F[1] in exists at script.pl line 17, &lt;$_[...]&gt; line ..
  • @vkk05 你有一行没有逗号。
  • 对,有时空行或包含不带逗号的行(单列数据)。
  • 我在里面添加了这些行,而 - next if /^\s*$/; my @F = split /,/; next unless $F[1];
【解决方案2】:

形成 regex 以匹配感兴趣的记录,将行拆分为字段并将字段 #5 与 YES 进行比较。如果有匹配项,则增加 %match 哈希中字段 #2 的计数。

处理文件后,%match 哈希将匹配记录字段 #2 作为键,值将反映该字段与 YES 匹配的次数文件。

use strict;
use warnings;
use feature 'say';

use Data::Dumper;

my %match;
my @look_for = qw(RT0AC1 WG3RA3);
my $re_filter = join('|',@look_for);

while(<DATA>) {
    chomp;
    next unless /$re_filter/;
    my @data = split(',',$_);
    $match{$data[1]}++ if $data[4] eq 'YES';
}

say Dumper(\%match);

__DATA__
101,RT0AC1,CONNECTED,FAULTY,NO
102,RT0AC1,CONNECTED,WORKING,YES
103,RT0AC1,NOT CONNECTED,WORKING,NO
104,WG3RA3,NOT CONNECTED,DISABLED,NO
105,WG3RA3,CONNECTED,WORKING,NO

输出

$VAR1 = {
          'RT0AC1' => 1
        };

删除 DATA 以获取最终代码并在命令行上提供文件名以处理包含感兴趣数据的文件

use strict;
use warnings;
use feature 'say';

use Data::Dumper;

my %match;
my @look_for = qw(RT0AC1 WG3RA3);
my $re_filter = join('|',@look_for);

while(<>) {
    chomp;
    next unless /$re_filter/;
    my @data = split(',',$_);
    $match{$data[1]}++ if $data[4] eq 'YES';
}

say Dumper(\%match);

基于正则表达式而不使用split的替代版本

use strict;
use warnings;
use feature 'say';

use Data::Dumper;

my %match;
my @look_for  = qw(RT0AC1 WG3RA3);
my $re_filter = join('|',@look_for);
my $regex     = qr/^\d+,($re_filter),[^,]+,[^,]+,YES$/;

/$regex/ && $match{$1}++ for <DATA>;

say Dumper(\%match);

__DATA__
101,RT0AC1,CONNECTED,FAULTY,NO
102,RT0AC1,CONNECTED,WORKING,YES
103,RT0AC1,NOT CONNECTED,WORKING,NO
104,WG3RA3,NOT CONNECTED,DISABLED,NO
105,WG3RA3,CONNECTED,WORKING,NO

【讨论】:

    最近更新 更多