특정 줄 범위를 선택하고 각각의 고유한 두 번째 문자와 함께 첫 번째 문자의 특정 발생 횟수를 계산하려면 어떻게 해야 합니까? [폐쇄]

특정 줄 범위를 선택하고 각각의 고유한 두 번째 문자와 함께 첫 번째 문자의 특정 발생 횟수를 계산하려면 어떻게 해야 합니까? [폐쇄]

안녕하세요 다음과 같은 파일이 있습니다.

#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

이 질문에 답하는 이유는 흥미로운 질문이고 귀하가 여기에 처음 왔기 때문입니다. 그러나 우리는 스크립팅 서비스가 아니라는 점에 유의하십시오. 이렇게 복잡한 솔루션이 필요한 질문은 요점을 벗어났습니다. 우리는 귀하의 특정 문제를 해결하는 데 기꺼이 도움을 드리지만 (보통) 귀하를 위해 전체 스크립트를 작성하지는 않습니다.

다음에는 뭔가 쓰기 시작하고 직면한 문제를 분리하세요. 하나 물어보세요특정한각 질문에 대해 질문을 하면 이런 식으로 스크립트를 구성할 수 있습니다.

관련 정보