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

データは実際には2つのレコードを表していると言っているのは正しいですか?もしそうなら、あなたは完全な記録のために行を蓄積したいと思います。

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

1行ではなく、複数の行でデータを比較しようとしていると思います。私がそれを間違えた場合は、私の答えの残りを無視してください。

私が行う方法は、フィールド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 に答える