特定の行範囲を選択し、それぞれの一意の2番目の文字で最初の文字の特定の発生回数を計算するにはどうすればよいですか? [閉鎖]

特定の行範囲を選択し、それぞれの一意の2番目の文字で最初の文字の特定の発生回数を計算するにはどうすればよいですか? [閉鎖]

こんにちは、次のファイルがあります。

#0
0:()
1b:cg*b
1c
0:cg
xe
#4
0:()
0b:cg*b
xc
0:cg
1e
#8
0:()
0b:cg*b
xc
0:cg
xe
#12
1b:cg*b
xc
0:cg
0e
#16
xb:cg*b
1c
xe
#20
1:()
xb:cg*b
xc
1:cg
1e
#24
x:()
xb:cg*b
xc
xe
#28
0:()
1b:cg*b
0c
x:cg
0e
#29
0:()
0b:cg*b
1c
x:cg
xe
#32
0:()
1b:cg*b

これは#0時間0を意味し、#8時間8を意味する。今、あなたは与えられた時間範囲(例えば2〜30)に基づいてファイルの一部を印刷したいと思います(手動で入力したい)。

このファイルには時間2と30が存在しないため、出力は2以降の次の時間(#4)から30以降の次の時間(#32)前の行まで(結果的にtemp1 = line 7 to)でなければなりません。 50 OK)

temp1の出力は次のようになります。

#4
0:()
0bd*b
xc
0:cg
1e
#8
0:()
0bd*b
xc
0:cg
xe
#12
1bd*b
xc
0:cg
0e
#16
xbd*b
1c
xe
#20
1:()
xbd*b
xc
1:cg
1e
#24
x:()
xbd*b
xc
xe
#28
0:()
1bd*b
0c
x:cg
0e
#29
0:()
0bd*b
1c
x:cg
xe

ここで、()、bd * b、c、:cg、eは、2列目の最初の文字の後の文字列です。 0、1、x は最初の文字です。

temp2の出力は次のようになります。

        4 8 12 16 20 24 28 29
:()     0 0 -  -  1  x  0   0
b:cg*b  0 0 1  x  x  x  1   0
c       x x x  1  x  x  0   1
:cg     0 0 0  -  1  -  x   x
e       1 x 0  x  1  x  0   x

今計算する必要があります。 temp2の列1の各項目のxは、次の規則を出力します。

  1. 前に0または1が続くxのみを考慮してください。
  2. 前に他のxがある場合は、xを計算しないでください。
  3. 時間範囲の始めに発生した場合は、axを計算します。
  4. temp2 出力に 0,1,x 以外の文字がある場合は、この文字を無視する必要があります。

したがって、最終出力は次のようになります。

name     count x
:()      1     x
b:cg*b   1     x
c        2     x
:cg      1     x
e        4     x

注:とにかく最終出力のみが必要なので、中間の一時出力ファイルをアーカイブする必要はありませんが、一時ファイルをアーカイブすることは私にとって有利です。明らかに、入力ファイルに空白行がある場合は、出力ファイルに空白行が必要ないので削除する必要があります)

私はスクリプトが最初で非常に長いtclスクリプトを書いたが、実行するのに時間がかかるので、awkまたはsedソリューションが欲しい。

ベストアンサー1

このPerlスクリプトは、必要な操作を一度に実行します。

#!/usr/bin/env perl
use strict;
use Getopt::Std;

## This hash will hold the options
my %opts;

## Read the options
getopts('t:s:e:',\%opts) || do { print "Invalid option\n"; exit(1); };

## Keep the temp file if the script is run 
## with -t
my $keep_temp_file=$opts{t}||undef;

## The temp file's file handle
my $tmp;
## The temp file
my $temp_file=`mktemp`;
chomp($temp_file);
## Read the time range
my $start=$opts{s}||undef;
my $end=$opts{e}||undef;


## Open the input file
open($tmp,'<',"$ARGV[0]")|| 
    die("Need an input file as the 1st argument: $!\n");

my ($time,$want);
my (%data,%letters);
## Read the input file
line:while (<$tmp>) {
    ## skip blank lines
    next if /^\s*$/;

    ## remove trailing newlines
    chomp;
    ## Is this line one of the start times?
    if (/^#(\d+)/) {
        if ($1>=$start && $1<=$end) {
            $time=$1;
            $want=1;
        } elsif ($1>=$end) {
            $want=0;
            last line;
        }
    }
    ## If we want this line, save it in
    ## the %data hash.
    if ($want==1) {
        ## Skip if this line is the one that has the time
        ## definition.
        next if /^#/;
        ## Get the two characters of the line
        /^(.)(.+)/;
        $data{$time}{$2}=$1;
        ## Save each letter seen
        $letters{$2}++;
    }  
}
## Once the file has been processed, create
## the temp file.
open($tmp,'>',$temp_file)|| 
    die("Could not open temp file $temp_file for writing: $!\n");

my @times=sort {$a <=> $b } keys(%data);
print $tmp " ";
printf $tmp "%6s", "$_" for @times;
print $tmp "\n";
foreach my $letter (sort keys(%letters)) {
    print $tmp "$letter " ;
    foreach my $time (@times) {
        defined $data{$time}{$letter} ? 
            printf $tmp "%6s","$data{$time}{$letter} " : printf $tmp "%6s","- ";
    }
    print $tmp "\n";
}
close($tmp);
## Process the tmp file to get your desired output
open(my $fh,'<',"$temp_file")|| 
    die("Could not open temp file $temp_file for reading: $!\n");
## Print the header
printf "%-7s%6s%10s\n",'name', 'count', 'x';
while (<$fh>) {
    ## Skip first line
    next if $.==1;

    ## Collect the columns
    my @foo=split(/\s+/);
    ## get the letter
    my $let=shift(@foo);
    my $c=0;
    ## Check if the first one is an x
    $c++ if $foo[0] eq 'x';
    ## Check the rest
    for (my $i=1;$i<=$#foo;$i++) {
        ## Get the previous position. This is complicated
        ## since you want to ignore the non [01x] characters
        my $prev="";
        for (my $k=$i-1; $k>-1; $k--) {
            if ($foo[$k]=~/^[01x]$/) {
                $prev=$foo[$k];
                last;
            }
        }
        ## If this is an x, increment c if 
        ## the previous character was 0 or 1
        if ($foo[$i] eq 'x' && ($prev=~/^[01]$/ || $prev=~/^$/)) {
            $c++;
        }
    } 
    printf "%-7s%6s%10s\n", $let,$c,"x";
}
## If we want to keep the temp file, copy
## it to the file name given.
if ($keep_temp_file) {
    system("cp $temp_file $keep_temp_file");
}
## else, delete it
else {
    unlink($temp_file);
}

別の名前で保存すると、foo.pl次のように実行できます。

foo.pl -s 2 -e 30 -t 2-30.temp file 

-s開始時間を設定し、-e終了時間を設定してください。一時ファイルを保持するには、を指定します-t。それ以外の場合、一時-tファイルは削除されます。

あなたの例では、次のものを生成します。

$ perl foo.pl -s 2 -e 30 -t aa file2
name    count         x
:()         1         x
:cg         1         x
b:cg*b      1         x
c           2         x
e           4         x

この質問に答える理由は興味深い質問であり、あなたがここに初めて来たからです。しかし、我々はスクリプトサービスではないことに注意してください。このような複雑な解決策が必要な質問は要点を超えています。私たちはあなたの特定の問題を解決することを喜んで助けますが、(通常)あなたのために完全なスクリプトを書くことはありません。

次に、何かを書き始め、直面した問題を切り離してください。一つ聞いてください特定各質問について質問をすると、このようにスクリプトを構成できます。

おすすめ記事