0

我有一个像下面这样的文件,其中以数字开头的行是我的样本的 ID,以下行是数据。

10001;02/07/98;;PI;M^12/12/59^F^^SP^09/12/55
;;;;;M1|F1|SP1;9;9;9;9;9;9;;D16S539
;;;;;M1|F1|SP1;9;9;9;9;9;9;;D7S820
;;;;;M1|F1|SP1;9;9;9;9;9;9;;D13S317
;;;;;M1|F1|SP1;9;9;9;9;9;9;;D5S818
10002;02/07/98;;RJ;F^20/04/86^SP^
;;;;;F1|SP1;;;12;10;12;11;;D10S212
;;;;;F1|SP1;;;8;8;10;8;;D7S820
;;;;;F1|SP1;;;12;11;14;11;;D13S317
;;;;;F1|SP1;;;13;12;13;8;;D5S818

对于包含数据的行,我想测试字段 6-11 是否相同,因为只有当它们不相等时我才想要数据(在第一种情况下,它们都是“9”)。

于是我想到了将行拆分并存储为数组,然后用 ~~ 运算符比较数组。但是,如果我在 while 循环中读取文件并且每行都重新定义了数组,我该怎么做呢?或者也许有更好的方法来做到这一点。

提前致谢!

这是一个伪代码来说明我想要做什么:

open FILE, $ARGV[0] or die $!;
while (<FILE>) {
    chomp;
    my @field = split /;/;
    if ($field[0] eq '') {
        if @fields[6 .. 11] is not equal to @fields[6 .. 11] in all the next lines {
            do my calculation;
        }
    }
}
4

4 回答 4

3

我说数据真的代表两条记录是否正确?如果是这样,您想为完整记录累积行数。

my @super_rec;
while (<>) {
    chomp;
    my @fields = split /;/;
    if ($fields[0] ne '') {
       process_rec(\@super_rec) if @super_rec;
       @super_rec = \@fields;
    } else {
       push @super_rec, \@fields;
    }
}

process_rec(\@super_rec) if @super_rec;

那么,你的问题就可以得到解答了。

sub process_rec {
    my ($super_rec) = @_;
    my ($rec, @subrecs) = @$super_rec;

    my $do_calc = 0;
    for my $i (1..$#subrecs) {
        if (  $subrecs[0][ 6] ne $subrecs[$i][ 6]
           || $subrecs[0][ 7] ne $subrecs[$i][ 7]
           || $subrecs[0][ 8] ne $subrecs[$i][ 8]
           || $subrecs[0][ 9] ne $subrecs[$i][ 9]
           || $subrecs[0][10] ne $subrecs[$i][10]
           || $subrecs[0][11] ne $subrecs[$i][11]
        ) {
           $do_calc = 1;
           last;
        }
    }

    if ($do_calc) {
       ...
    }
}
于 2012-10-22T19:42:30.183 回答
0

我假设您正在寻找跨行比较数据,而不是在一行内。如果我错了,请忽略我的其余答案。

我这样做的方法是将字段 6 到 11 重新连接为字符串。将第一行的数据保留为 $firstdata,并将每个后续行的数据作为 $nextdata 进行比较。每次数据不匹配时,您都会增加 $differences 计数器。当你得到一个 ID 行时,检查之前的 $differences 是否大于零,如果是的话,你的计算(你可能需要将 ID 行和其他字段保存在一些其他变量中)。然后重新初始化 $differences 和 $firstdata 变量。

my $firstdata = "";
my $nextdata = "";
my $differences = 0;
open FILE, $ARGV[0] or die $!;
while (<FILE>) {
    chomp;
    my @field = split /;/;
    if ($field[0] eq '') {
        $nextdata = join(';', @fields[6..11]);
        if ($firstdata && ($nextdata ne $firstdata)) {
            $differences++;
        } else {
            $firstdata = $nextdata;
        }
    } else {
        if ($differences) {
            # do your calculation for previous ID
        }
        $firstdata = "";
        $differences = 0;
    }
}
if ($differences) {
    # do your calculation one last time for the last ID
}
于 2012-10-22T19:22:26.573 回答
0

这是一种使用Regex. 如果索引固定在 6 到 11 之间,这可能比其他方法效率低,并且只知道这些索引,因为它将遍历整个字符串:-

open FILE, $ARGV[0] or die $!;
while (<FILE>) {
    chomp;
    my $num = 0;
    my $same = 1;
    while (/;(\d+);/) {

       if ($num == 0) { $num = $1; } 
       elsif ($1 != $num) { $same = 0; last; }

       # Substitute current digit matched with x (or any char) 
       # to avoid infinite loop
       s/$1/x/; 
    }  

    if ($same) {
       print "All digits same";
    }
}
于 2012-10-22T19:24:31.857 回答
0

使用该Text::CSV_XS模块,您可以执行以下操作:

use strict;
use warnings;
use Text::CSV_XS;
use feature 'say';

my $csv = Text::CSV_XS->new({
        sep_char    => ";",
        binary      => 1,
    });

my %data;
my @hdrs;  # store initial order of headers
my $hdr;
while (my $row = $csv->getline(*DATA)) {
    if ($row->[0] =~ /^\d+$/) {
        $csv->combine(@$row) or die "Cannot combine: " .
            $csv->error_diag();
        $hdr = $csv->string();   # recreate the header 
        push @hdrs, $hdr;        # save list of headers
    } else {
        push @{ $data{$hdr} }, [ @{$row}[6..11] ];
    }
}

for (@hdrs) {
    say "$_\n   arrays are: " . (is_identical($data{$_}) ? "same":"diff");
}

sub is_identical {
    my $last;
    for (@{$_[0]}) {         # argument is two-dimensional array
        $last //= $_;
        return 0 unless ( @$_ ~~ @$last );
    }
    return 1;                # default = all arrays were identical
}


__DATA__
10001;02/07/98;;PI;M^12/12/59^F^^SP^09/12/55
;;;;;M1|F1|SP1;9;9;9;9;9;9;;D16S539
;;;;;M1|F1|SP1;9;9;9;9;9;9;;D7S820
;;;;;M1|F1|SP1;9;9;9;9;9;9;;D13S317
;;;;;M1|F1|SP1;9;9;9;9;9;9;;D5S818
10002;02/07/98;;RJ;F^20/04/86^SP^
;;;;;F1|SP1;;;12;10;12;11;;D10S212
;;;;;F1|SP1;;;8;8;10;8;;D7S820
;;;;;F1|SP1;;;12;11;14;11;;D13S317
;;;;;F1|SP1;;;13;12;13;8;;D5S818

输出:

10001;02/07/98;;PI;M^12/12/59^F^^SP^09/12/55
   arrays are: same
10002;02/07/98;;RJ;F^20/04/86^SP^
   arrays are: diff
于 2012-10-22T20:08:04.413 回答