키값들을 카운트 ..

thwan2rokmc의 이미지

안녕하세요

#!user/bin/perl
 
 
my $datafile = 'a.text';
my @dataArray;
 
open(IN, $datafile) || die $!;
 
my $datafile2 = 'b.text';
my @dataArray2;
 
open(IN2, $datafile2) || die $!;
 
 
while(<IN>)
{
    chomp;
    my %tmpHash;
        if(!(/Sta/)){
                if(!(/Err/)){
                if(!(/active/)){
    @tmpHash{qw(Time Channel ID Trans State DLC Data1 D2 D3 D4 D5 D6 D7 D8)} = split(" ", $_);
    push(@dataArray, \%tmpHash);
}
}
}
}
close(IN);
 
print "NoEvent_Line ".@dataArray."줄"."\n";
 
foreach my $db (@dataArray)
{
    print "ID:".$$db{ID}." DataFrame:".$$db{Data1}." ".$$db{D2}." ".$$db{D3}." ".$$db{D4}."\n";
}
 
 
 
while(<IN2>)
{
    chomp;
    my %tmpHash2;
    @tmpHash2{qw(TIME CHANEL ID Trans State DLC Data1 D2 D3 D4 D5 D6 D7 D8)} = split(" ", $_);
    push(@dataArray2, \%tmpHash2);
}
 
close(IN2);
 
 
print "Event_Line ".@dataArray2."줄"."\n";
 
my $count=@dataArray;
foreach my $db2 (@dataArray2)
{
    print "ID:".$$db2{ID}." DataFrame:".$$db2{DataFrame}." ".$$db2{D2}." ".$$db2{D3}." ".$$db2{D4}."\n";
}
 
my @result;
print "=============Result_value==============\n";
foreach $db2 (@dataArray2){
                foreach $db1 (@dataArray){
                if(($db2{$ID} eq $db{$ID})){
             print "ID:".$$db2{ID}." DataFrame:".$$db2{DataFrame}." ".$$db2{D2}." ".$$db2{D3}." ".$$db2{D4}."\n";
}
 
 
 
}
   $count++;
 
}
print "=======================================\n";

두 개를 각각 공백으로 잘라 배열에 저장까지는 다 했는데

출력 부분 조건문에서
막혀버렸어요... ㅜㅜㅜ
이 문제의 난이도 수준은 잘 모르겠지만.. 어디가 틀렸을까요..

raymundo의 이미지

db2 라는 이름이 foreach 루프의 스칼라변수로도 쓰이고 그 안에 보면 $db2{$ID} 에서는 해시로도 쓰이는군요. 그리고 db1 을 써야 할 자리에 db 라고 쓴 것도 보입니다.
정확히 의도에 맞게 변수를 쓰고 있는지 확인할 수 있도록 use strict; use warnings; 두 개를 넣으시기를 권하고요.

애초에 파일을 읽은 후 @dataArray의 원소인 해시들이 제대로 구성이 된 게 맞는지조차 매우 의아한데요.
예를 들어 a.txt 의 첫번째 줄은 ID 필드가 00, Time 필드가 "ID:10210040x", DLC가 00이 들어갑니다. 두번째 줄은 ID는 "Length"이고 DLC는 "BitCount"네요. 이런 식으로 동일한 필드에 줄마다 서로 다른 컬럼이 들어가는게 무슨 의미가 있을지 모르겠네요. 실제로 아래 이중 루프에서 ID 필드를 비교해봤자 제대로 골라내지도 못하겠고요.

ID: 와 Dataframe: 부분만 뽑아서 비교한다면,

#!/usr/bin/perl
use strict;
use warnings;
use Data::Dumper;
 
# a.txt 읽고 데이터 구성
my @data_a;
open my $in_a, "<", "a.txt" or die $!;
while ( my $line = <$in_a> ) {
    if ( $line =~ /
        ID:                  # 'ID:' 바로 뒤에
        (                    # 그룹1
            \S+              # 공백 문자가 아닌 문자들이 1번 이상 반복
        )
        .*                   # 임의의 문자들이 나오고
        Dataframe:           # 'Dataframe:' 뒤에
        (                    # 그룹2
            [0-9A-Fa-f]{2}          # 십육진수 두 자리 필수
            (                       # 추가로
                [ ]                 # 스페이스 하나와
                [0-9A-Fa-f]{2}      # 십육진수 두 자리
            )*                      # 0번 이상 반복될 수 있음
        )
        /x )
    {
        my $id = $1;
        my $frame = $2;
        push @data_a, { id => $id, frame => $frame };
    }
}
close $in_a;
 
print Dumper(\@data_a);
 
# 이 시점에 @data_a 는
# @data_a = (
#             {
#               'frame' => '00 00 20 00 00 00 20 00',
#               'id' => '10210040x'
#             },
#             {
#               'frame' => '00',
#               'id' => '1045C060x'
#             },
#             {
#               'frame' => '00 00',
#               'id' => '106D0080x'
#             },
#             {
#               'id' => '10220040x',
#               'frame' => '10 00 00 00 00 00 00 00'
#             },
#             {
#               'frame' => '01 00 00 00 00 00 00',
#               'id' => '1036A080x'
#             },
#             {
#               'frame' => '00 00 73',
#               'id' => '106D4099x'
#             },
#             {
#               'id' => '1022A040x',
#               'frame' => '00 B0 00 00 00 00 00 00'
#             }
#         );
 
 
# 마찬가지로 b.txt 읽기
my @data_b;
open my $in_b, "<", "b.txt" or die $!;
while ( my $line = <$in_b> ) {
    if ( $line =~ /ID:(\S+).*Dataframe:([0-9A-Fa-f]{2}( [0-9A-Fa-f]{2})*)/ )
    {
        my $id = $1;
        my $frame = $2;
        push @data_b, { id => $id, frame => $frame };
    }
}
close $in_b;

이렇게 데이터가 구성되었으니, 이제 id 를 비교하거나 한쪽에만 있는 것을 찾을 수 있겠지요. 이중 루프를 돌아도 안 될 것은 없습니다만, 어느 한쪽을 다시 해시( ID => Frame 형태의 )로 만든 후에, 다른 쪽의 데이터를 하나씩 순회하면서 이 해시에 넣어서 비교하는 것이 더 낫겠네요.

좋은 하루 되세요!

thwan2rokmc의 이미지

제가 잘못 기재를 한걸 이제 봤어요ㅠㅠ 소스코드 수정한걸 올렸어야했는데 죄송하네요..
텍스트는 제가 실행한 결과물을 올려버려서...

본래 제가 짠 소스코드는

#!user/bin/perl
 
my $datafile = 'NoEvent.text';
my @dataArray;
 
open(IN, $datafile) || die $!;
 
my $datafile2 = 'Event.text';
my @dataArray2;
 
open(IN2, $datafile2) || die $!;
 
 
while(<IN>)
{
    chomp;
    my %tmpHash;
        if(!(/Sta/)){
                if(!(/Err/)){
                if(!(/active/)){
    @tmpHash{qw(Time Channel ID Trans State DLC Data1 D2 D3 D4 D5 D6 D7 D8)} = split(" ", $_);
    push(@dataArray, \%tmpHash);
}
}
}
}
close(IN);
 
print "NoEvent_Line ".@dataArray."줄"."\n";
 
foreach my $db (@dataArray)
{
    print "ID:".$$db{ID}." DataFrame:".$$db{Data1}." ".$$db{D2}." ".$$db{D3}." ".$$db{D4}."\n";
}
 
while(<IN2>)
{
    chomp;
    my %tmpHash2;
    @tmpHash2{qw(TIME CHANEL ID Trans State DLC Data1 D2 D3 D4 D5 D6 D7 D8)} = split(" ", $_);
    push(@dataArray2, \%tmpHash2);
}
close(IN);
 
 
 
 
print "Event_Line ".@dataArray2."줄"."\n";
 
my $count1=@dataArray;
my $count2=@dataArray2;
foreach my $db2 (@dataArray2)
{
    print "ID:".$$db2{ID}." DataFrame:".$$db2{Data1}." ".$$db2{D2}." ".$$db2{D3}." ".$$db2{D4}."\n";
}
print "=============Result_value==============\n";
foreach $db2 (@dataArray2){
        my $counting=0;
                foreach $db1 (@dataArray){
                if($$db2{ID} eq $$db1{ID}){
                if($$db2{Data1} ne $$db1{Data1}){
                print "ID:".$$db2{ID}." DataFrame:".$$db2{Data1}." ".$$db2{D2}." ".$$db2{D3}." ".$$db2{D4}."\n";
}
}
}
  $count ++;
}
print "=======================================\n";

이것입니다.. 그 조언해주신대로 use strict; use warnings; 습관 들이도록 해야겠습니다.

그 결과화면을 잘못 올려버려 혼동되셨을거라 생각해요 소중한 시간 내 주셔서 답글 해주셨는데 죄송하네요 ㅠㅠ.

본래 텍스트 파일 형식은 이렇게 되구 1번째 필드가 시간 3번째 필드가 ID 그다음 16진수로 된 0~8 비트의 데이터프레임으로 구성된걸
ID가 동일하고 데이터프레임이 다를때와 B.text 파일에만 ID가 존재할때 두가지 조건으로 라인을 출력하려합니다.

Perl을 배우고 있어 계속 도전해보고 하려고 하지만 혼자 하기엔 너무 이해도가 낮아 다시 부탁드려도 될런지요? 늦은밤 답글 죄송하구 좋은밤되세요!!

답글 감사합니다 :D

raymundo의 이미지

시간을 내는 거야 제가 재밌어보여서 하는 거니까 괜찮은데, 답변하는 사람이 데이터 포맷을 추정하면서 답하기는 매우 힘들기 때문에 그런 게 모호한 질문글은 답변 받기 힘들어집니다. 예를 들어 Rx d 바로 다음에 나오는 숫자는, 그 뒤에 오는 프레임 바이트의 개수를 나타내는 숫자 같은데 이 중요한 걸 누락하시면... :-)

프레임 바이트 개수가 라인마다 유동적이니 지금처럼 고정된 split 한 번으로 뽑아내는 건 곤란하고요.
암튼 날짜나 버전 등이 적힌 처음 몇 줄은 없다고 생각하고 다시 만들어봤습니다.

#!/usr/bin/perl
use strict;
use warnings;
 
# A.text 읽고 데이터 구성
my @data_a;
open my $in, "<", "A.text" or die $!;
while ( my $line = <$in> ) {
    chomp $line;
    if ( $line !~ /Sta|Err|active/ ) {
        # 처음 여섯 필드만 명시적으로 뽑아내고 일곱번째 필드부터 끝까지는 $others에 넣음
        my ( $time, $channel, $id, $trans, $state, $dlc, $others ) = split(" ", $line, 7);
 
        # $dlc 에 프레임 바이트 개수가 들어 있으니, $others 에서 해당 개수만큼 필드를 뽑아냄
        my @frame_bytes;
        if ( $dlc > 0 ) {
            @frame_bytes = ( split " ", $others )[ 0 .. $dlc-1 ];
        }
        # 굳이 프레임이 배열일 필요는 없을 것 같으니 다시 문자열로
        my $frame = join " ", @frame_bytes;
 
        # 익명 해시 구성
        # {
        #   id    => '106D4099x',
        #   frame => '00 00 73',
        #   출력할 때 쓰기 위해 라인 전체도 같이 저장
        #   line  => '0.043441 1 106D4099x Rx d 3 00 00 73 Length = 2784638 BitCount = 97 ID = 275595417x'
        # }
        my $tmp_hash = {
            id    => $id,
            frame => $frame,
            line  => $line,
        };
 
        # 익명 해시를 배열에 추가
        push @data_a, $tmp_hash;
    }
}
close $in;
 
 
# B.text 파일의 내용도 마찬가지로 재구성해서 저장해도 되지만, 원하는 게 그저 출력 뿐이라면
# 굳이 다시 저장할 필요 없이 파일을 읽으면서 곧바로 처리합시다.
 
open $in, "<", "B.text" or die $!;
 
while ( my $line = <$in> ) {
    chomp $line;
    if ( $line !~ /Sta|Err|active/ ) {
        # id 와 frame 을 얻는 부분은 위와 동일
        # time, channel 등은 어차피 안 쓰니까 사실 변수도 필요 없음
        my ( undef, undef, $id, undef, undef, $dlc, $others ) = split(" ", $line, 7);
 
        my @frame_bytes;
        if ( $dlc > 0 ) {
            @frame_bytes = ( split " ", $others )[ 0 .. $dlc-1 ];
        }
        my $frame = join " ", @frame_bytes;
 
        # @data_a 에 저장된 원소들 중에 id 키가 일치하는 것이 있나 찾아봄
        # XXX 루프를 쓰는 것은 매우 비효율적
        my $found;
        foreach my $line_a ( @data_a ) {
            if ( $line_a->{id} eq $id ) {
                # id 가 일치하는 것을 찾았음
                $found = $line_a;
 
                # frame 이 다르면 출력
                if ( $line_a->{frame} ne $frame ) {
                    print "SAME ID, DIFFERENT FRAME\n";
                    print "A: ", $line_a->{line}, "\n";
                    print "B: ", $line, "\n";
                }
 
                # 더 이상 루프를 돌 필요가 없으니 탈출
                # (A.text에 있는 라인들끼리는 id가 중복되지 않는다는 가정 하에)
                last;
            }
        }
 
        # 루프가 끝난 이 시점에 $found에 저장된 게 없다면
        # A.text에서 $id 에 해당하는 라인이 없었다는 얘기니까 출력
        if ( ! $found ) {
            print "ONLY IN B: ", $line, "\n";
        }
    }
}
 
close $in;

제가 테스트삼아 실행해보면 잘 나오는 것 같긴 한데, 출력 양식을 세세하게 조정하시는 건 직접 하시면 될 거고요.

위에 주석에 보면 "루프를 쓰는 것은 매우 비효율적"이라고 되어 있는데, 예를 들어 A.text 에 1만 라인이 있다면 B.text에서 한 줄을 읽을 때마다 매번 id가 일치하는 라인을 찾기 위해서 최대 1만번의 루프를 돌아야 합니다. B의 라인이 1천 줄이라면 천만 번의 루프를 돌겠네요.

그러니 A.text 의 내용을 읽을 때 해시를 만드는 게 좋습니다. 이 해시의 키는 id이고, 해시의 값은 그 id에 해당하는 라인의 정보를 담은 익명 해시입니다.

%hash_a = (
    '10812060x' => {
        'line' => '0.050251 1 10812060x Rx d 1 00 Length = 2274686 BitCount = 80 ID = 276897888x',
        'frame' => '00'
    },
    '1084A060x' => {
        'frame' => '00',
        'line' => '0.052626 1 1084A060x Rx d 1 00 Length = 2248252 BitCount = 79 ID = 277127264x'
    },
    ...
)

이제는 루프를 돌지 않고도 어떤 id 가 A에 있는지 검사할 수 있습니다.

# $id, $frame 이 주어졌을 때
if ( exists $hash_a{ $id } ) {
    # A에 $id에 해당하는 라인이 존재함
    if ( $hash_a{ $id }->{frame} ne $frame ) {
        # 프레임 다름
    }
else {
    # 존재하지 않음
}

%hash_a 를 구성하는 것은 처음에 A.text 를 읽는 루프에서 직접 할 수도 있고, 일단 @data_a 를 만들었다면 그걸 이용해서 만들 수도 있을 텐데, 이왕 위에서 배열을 만들었으니...

my %hash_a = map { $_->{id} => { frame => $_->{frame}, line => $_->{line} } } @data_a;

좋은 하루 되세요!

thwan2rokmc의 이미지

해시테이블을 이해하는데 많이 도움이 되었어요 !! 감사해요:D
저번에 올려주신 코드도 그렇고 이번에도 그렇고
제 맥에서 실행하면 Unrecognized character \xC2; marked by <-- HERE after <-- HERE near column 1 at Analyzer line 4.
이라는 문구가 나와요ㅠㅠ

\xC2 라는 문구가 나와 실행조차 되지 않는데 왜이러는걸까요??

raymundo의 이미지

글쎄요, 저도 맥에서 작성한 코드이고... 지금 크롬에서 다시 긁어 붙여넣기 해도 아무 문제없이 동작하는군요.

무슨 에디터를 쓰시는지 모르겠지만 브라우저나 에디터를 다른 것으로 바꿔 해보시는 것이?

여전히 그러면 터미널에서
xxd prog.pl
로 덤프를 떠서 그 결과를 여기 적어보세요.

좋은 하루 되세요!

thwan2rokmc의 이미지

주석을 다 없애고 그냥 제가 타이핑을 해서 했더니 되요:D
주석때문에 그런가봐요 ㅠㅠ. 감사합니다 정말!
실례가 안되면 하나만 더 여쭈어볼게요 ㅠㅠ 저거 두개다 ID별로 카운팅을 해서 제가 수를 입력하면
그 수에 맞는 ID가 출력되게 하려면 어찌해야될지 ,, 그리고 펄도 EXE 파일로 해서 두 파일을 입력받아서 할수 있는지 알고싶어요..

그리고 정말 고맙습니다! 해시테이블 이제 이해했어요 친절한 설명덕분에:)
좋은하루 보내세요!!

raymundo의 이미지

ID별로 카운팅을 한다는 게 무슨 뜻인지 모르겠네요. 한 ID는 한 파일에 한 번만 나오는 게 아니라면 위의 코드도 손을 더 봐야 하는데요. 구체적으로 예를 들어주세요.

ID가 등장하는 파일의 개수를 카운팅한다는 얘기라면, 아니 꼭 이런 경우가 아니더라도 아무튼 "*** 별로"라는 식의 문제가 생긴다면 대부분의 경우 답은 해시입니다.

my %count;   # ID별 카운트
while ( 파일들을 라인 별로 읽다가 ) {
    # $id를 얻고 나면
    $count{$id}++;
}
 
# 다 읽고 나면 %count 는 이런 식이 될 테니
# %count = (
#            'id1' => 2,
#            'id2' => 1,
#            'id3' => 3,
#            ...
# )
 
# 카운트가 2인 id만 출력하기
foreach my $id ( keys %count ) {
    print $id,"\n" if $count{id} == 2;
}
 
# 또는 while + each
while ( my ( $id, $count ) = each %count ) {
    print $id, "\n" if $count == 2;
}
 
 
# 여기서도 저 해시를 다시 뒤집은 형태로 구성한다면 루프를 돌며 찾을 필요 없이 바로 접근할 수 있습니다.
# 여러 ID가 동일한 카운트값에 대응될 수 있으므로, 뒤집은 해시의 값들은 스칼라가 아니라 배열이어야겠죠.
%count_to_id = (
   1 => [ 'id2', 'id4', 'id5' ],
   2 => [ 'id1', 'id6' ],
   3 => [ 'id3' ],
);
 
# count 가 2인 아이디들만 출력
my @ids = @{ $count_to_id{2} };
print "@ids\n";   # "id1 id6"

> 펄도 EXE 파일로 해서 두 파일을 입력받아서 할수 있는지

죄송한데 이 말도 정확히 무슨 뜻인지 모르겠습니다(...) 다음 셋 중에 하나를 말씀하시는 것 같은데,

1) 펄 스크립트를 C,C++ 등의 프로그램처럼 독립적인 exe 실행파일로 만들어서, 펄이 설치되지 않은 컴퓨터에서 바로 실행하게 할 수 있느냐?

PAR::Packer 모듈을 알아보세요.
http://search.cpan.org/~rschupp/PAR-Packer-1.035/lib/pp.pm

2) 실행할 때 다음과 같이 파일 이름을 명령행 인자로 줄 수 있느냐?

$ perl prog.pl a.txt b.txt

프로그램 안에서 @ARGV 라는 배열이 ( 'a.txt', 'b.txt' )라고 자동으로 설정되니까 이걸 이용하세요.

3) 명령행 프로그램이 아니라 윈도우 프로그램들처럼 GUI로 꾸며서 버튼을 눌러 대화상자를 띄우고 여기서 파일을 지정하고 등등 할 수 있느냐?

물론 있고 qt 나 gtk 등을 살펴보시면 됩니다만... 펄GUI에 대해서 저는 안 해봐서 잘 모르고, 윈도우에서 비주얼 스튜디오로 만들 듯이 편하지는 않을 겁니다.

좋은 하루 되세요!

thwan2rokmc의 이미지

답변 ㅠㅠ 감사합니다 너무 기다렸어요:D 계속 하고 있는데 잘 안되어서요 밥도 안먹구 하구있어서 얼마나 반가운지 몰라요 ㅠ.
제가 설명을 잘 못해서 죄송해요.. 구체적으로 말씀드리자면,

raymundo님께서 답변해주신 코드로 B에만 존재하는 ID만 따로 추출한 결과물 안에서
동일한 ID별로 갯수를 세어서 제가 입력한 수와 맞는 ID의 횟수가 있으면 그 ID가 존재하는 라인을 다 출력하는거에요.

<‘b.txt’ 파일에만 존재하는 ID 의 결과물> 4.365427 1 100 Rx d 0 Length = 1408315 BitCount = 51 ID = 256

4.432225 1 1038C040x Rx d 1 00 Length = 2278263 BitCount = 80 ID = 272154688x

4.469258 1 C2F6040x Rx d 1 00 Length = 2274686 BitCount = 80 ID = 204431424x

4.473886 1 C2F8040x Rx d 1 00 Length = 2278263 BitCount = 80 ID = 204439616x

4.478718 1 C2FA040x Rx d 1 00 Length = 2278252 BitCount = 80 ID = 204447808x

4.488325 1 C714040x Rx d 1 0F Length = 2248252 BitCount = 79 ID = 208748608x

4.543692 1 102AA080x Rx d 8 00 00 00 00 00 00 00 00 Length = 4314558 BitCount = 148 ID = 271229056x

4.556685 1 102C2080x Rx d 3 00 00 00 Length = 2878247 BitCount = 100 ID = 271327360x

8.513714 1 102AA080x Rx d 8 00 00 00 00 00 00 00 00 Length = 4314558 BitCount = 148 ID = 271229056x

8.516718 1 102C2080x Rx d 3 00 00 00 Length = 2878232 BitCount = 100 ID = 271327360x

11.703670 1 10438040x Rx d 1 01 Length = 2334682 BitCount = 82 ID = 272859200x

12.108770 1 10438040x Rx d 1 00 Length = 2338248 BitCount = 82 ID = 272859200x

12.803904 1 10438040x Rx d 1 01 Length = 2338248 BitCount = 82 ID = 272859200x

13.203957 1 10438040x Rx d 1 00 Length = 2338248 BitCount = 82 ID = 272859200x

13.704059 1 10438040x Rx d 1 01 Length = 2338248 BitCount = 82 ID = 272859200x

14.149137 1 10438040x Rx d 1 00 Length = 2334682 BitCount = 82 ID = 272859200x

위의 결과물이 raymundo님의 코드로 나온 결과물이에요. 이 결과물 안에서 예를들어, 제가 입력한 수가 6이면 위에서처럼 중복되는 ID의 수가 6개인 ID의 라인들을 출력하는거에요 저 B.txt에만 존재하는 ID의 결과물에서 으로 입력한 수가 6이면
10438040x ID값들이 여섯개나 존재하기때문에 결과는

11.703670 1 10438040x Rx d 1 01 Length = 2334682 BitCount = 82 ID = 272859200x

12.108770 1 10438040x Rx d 1 00 Length = 2338248 BitCount = 82 ID = 272859200x

12.803904 1 10438040x Rx d 1 01 Length = 2338248 BitCount = 82 ID = 272859200x

13.203957 1 10438040x Rx d 1 00 Length = 2338248 BitCount = 82 ID = 272859200x

13.704059 1 10438040x Rx d 1 01 Length = 2338248 BitCount = 82 ID = 272859200x

14.149137 1 10438040x Rx d 1 00 Length = 2334682 BitCount = 82 ID = 272859200x

이렇게 나와야 하는건데요 ㅠㅠ 코드를 짜고 있는데 잘 안되어요,, 뭐가 잘못된건지 모르겠어요,, 제 코드도 혹시 몰라 올려볼게요

#!user/bin/perl
#CAN_Analyzer
 
use strict;
 
my @NoEvent;
open my $IN, "<", 'NoEvent.txt' or die $!;
while( my $line =<$IN>){
        chomp $line;
        if($line !~/Sta|Err|active/){
        my ($time, $channel, $id, $trans, $state, $dlc, $others)=split(" ",$line, 7);
 
        my @frame_bytes;
        if($dlc >= 0){
        @frame_bytes=(split " ",$others)[0..$dlc-1];
        }
 
        my $dataframe= join " ", @frame_bytes;
        my $tmp_hash={
                id =>$id,
                dataframe => $dataframe,
                line =>$line,
                };
        push @NoEvent, $tmp_hash;
        }
    }
        close $IN;
 
my (@NoEvent_same_ID, @Event_same_ID, @ONLY_ID, @ONLY_ID2);
open $IN, "<", 'Event.txt' or die $!;
while(my $line=<$IN>){
        chomp $line;
        if($line !~/Sta|Err|active/){
        my(undef, undef, $id, undef, $dlc, $others)=split(" ", $line, 7);
 
        my @frame_bytes;
        if($dlc >= 0){
        @frame_bytes =(split " ", $others)[0 .. $dlc-1];
        }
        my $dataframe= join " ", @frame_bytes;
        my $found;
        foreach my $line_a(@NoEvent){
          if($line_a->{id} eq $id){
                $found = $line_a;
          if($line_a->{dataframe} ne $dataframe){
                push @NoEvent_same_ID, $line_a->{line};
 push @Event_same_ID, $line;
          }
                last;
        }
      }
 
          if( ! $found){
                push @ONLY_ID, $line;
                push @ONLY_ID2, $line;
                }
        }
      }
close $IN;
 
 
my $list;
 
print "----------------------------------------------FILTER_ID_LINES------------------------------------------------"."\n";
 
foreach $list (@ONLY_ID){
        print "$list","\n\n";
}
print "                                   The_actual_number_of_actions_INPUT: ";
 
my $INPUT=<STDIN>;
$INPUT = int $INPUT;
 
foreach my $line (@ONLY_ID){
        my @found=();
        my $count=0;
        foreach my $line_s(@ONLY_ID2){
          if($line_s{Id} eq $id){
               push @found, $line_s;
                $count++;
                }
        last;
        }
        if($INPUT == $count){
                foreach my $list (@found){
                   print "$list"."\n\n";
                                }
                        }
                last;
                }

이거에요 ㅠㅠ 이렇게 해서 전체 프로그램을 C처럼 EXE 파일로 만들고싶은데 잘 안되네요 흐아,,,
늦은밤 죄송하고 좋은밤되세요 답변 감사해요 :D
raymundo의 이미지

my $INPUT=<STDIN>;
$INPUT = int $INPUT;
 
foreach my $line (@ONLY_ID){
    my @found=();
    my $count=0;
    foreach my $line_s (@ONLY_ID2){
        if($line_s{Id} eq $id){
            push @found, $line_s;
            $count++;
        }
        last;
    }
    if($INPUT == $count){
        foreach my $list (@found){
            print "$list"."\n\n";
        }
    }
    last;
}

문제가 한두개가 아니라서(...)

1.

위 코드에서 $id 가 뜬금없이 튀어나오기 때문에 컴파일 시점에 에러 납니다.

2.

$line_s{Id} 는 %line_s 라는 해시의 원소인데, 이 변수 역시 선언 없이 튀어나오므로 에러입니다.

foreach 에서 선언하고 있는 $line_s 는 스칼라이므로, 이 스칼라 변수에 담기는 것(즉 @ONLY_ID2 의 각 원소들)이 해시레퍼런스라면 $line_s->{Id} 또는 ${$line_s}{Id} 로 쓰셔서 접근할 수 있습니다(첫번째 표기가 더 간단하므로 권장). 하지만 현재 @ONLY_ID2의 원소는 해시 레퍼런스가 아니라 단순한 스트링이므로, 이마저도 실행 시점에 에러가 날 것입니다.

3.

코드의 의도는 알겠는데(웬만하면 주석으로 적어주시면 더 좋겠지만), 그 의도대로 하려면

1) 바깥쪽 foreach 루프에서 $line으로부터 $id 를 다시 추출해 내야만 안쪽 foreach 루프에서 $id를 쓸 수 있을 것이고

2) 마찬가지로 안쪽 루프에서도 $line_s 로부터 다시 id 를 추출해야 이것을 $id와 비교할 수 있을 텐데

그러자면 위에서 했던 split 을 또 써야합니다. 하지만 사실 그럴 필요가 없습니다. 이미 B에서 라인을 읽는 루프에서 그런 작업을 했었기 때문에, 그렇게 추출한 정보를 같이 @ONLY_ID에 저장하면 됩니다.

        if( ! $found ) {
            # $line 스트링 대신, 해시를 만들고 그 레퍼런스를 저장
#             my %temp_hash = (
#                 id => $id,
#                 line => $line
#             );
#             push @ONLY_ID, \%temp_hash;
 
            # 또는, 처음부터 익명 해시 레퍼런스를 만들어 저장
#             my $temp_hash_ref = {
#                 id => $id,
#                 line => $line
#             };
#             push @ONLY_ID, $temp_hash_ref;
 
            # 또는, 임시 변수를 쓸 필요도 없이 다음과 같이
            push @ONLY_ID, { id => $id, line => $line };
 
            # @ONLY_ID2 는 쓸 필요도 없음
        }

그러면 B를 다 읽고 루프를 나온 시점에 @ONLY_ID는 다음과 같은 형태가 되고

@ONLY_ID = (
          {
            'id' => '100',
            'line' => '3.764043 1 100 Rx d 0 Length = 1408304 BitCount = 51 ID = 256'
          },
          {
            'line' => '3.840844 1 13FFE099x Rx d 0 Length = 2064714 BitCount = 73 ID = 335536281xa',
            'id' => '13FFE099x'
          },
          {
            'line' => '3.846184 1 622 Rx d 8 01 40 00 00 00 00 00 00 Length = 3564590 BitCount = 123 ID = 1570',
            'id' => '622'
          },
          ...
)

이제는 그 아래 코드에서

foreach my $line ( @ONLY_ID ) {
    ...
    foreach my $line_s ( @ONLY_ID ) {
        if ( $line->{id} eq $line_s->{id} ) {    # 이렇게 비교하면 됨
            ...
        }
    }
}

4.

출력하는 코드에서 last 가 불필요하게 쓰이고 있습니다. 루프를 계속 진행해야 개수를 세든 말든 할 텐데 한번만 세고 빠져나가버리니까 제대로 된 결과가 안 나옵니다.

5.

여기까지 말한 걸 다 수정하고 나서도 여전히 문제가 있는데, 예를 들어 id 가 100인 라인이 세 번 들어가 있다면, (각각을 100a 100b 100c 라고 하고)

- 100a 에 대해서 안쪽 루프에서 카운트를 하여 100a 100b 100c 출력
- 100b 에 대해서 안쪽 루프에서 카운트를 하며 100a 100b 100c 출력
- 100c 에 대해서...

이렇게 세 라인을 세 번 중복해서 출력할 거란 얘깁니다.

그러니 이중 루프를 쓰는 것은 애초에 좋은 생각이 아닙니다. 계속 강조하는데 뭔가 막힌다 싶을 때는 일단 '해시'가 답이 아닐까 고민하시면 얼추 맞습니다.

my %count;
foreach my $line (@ONLY_ID){
#     my $id = $line->{id};
#     $count{$id}++;
 
    # 또는 임시 변수 없이
    $count{ $line->{id} }++;
}

이 간단한 루프를 돌고 나면 id 별로 등장횟수가 카운트되어 있습니다.

%count = (
  'C7920AFx'  => 1,
  '102340BCx' => 1,
  '13FFE040x' => 1,
  '어떤ID'    => 6;
)

여기서 '6번 나오는 id 찾기'는 %count 해시에서 '값이 6인 키들을 찾기' 문제가 되니까, 이건 직접... (앞 댓글 중에 답도 다 있습니다)

조언을 조금 드리자면, 펄을 쓰려면 스칼라/배열/해시 세 가지 구조와, 복잡한 자료 구조를 만들기 위해 레퍼런스를 다루는 법을 간단한 샘플 예제를 많이 작성해보며 익히시는 게 좋습니다. 예를 들어 이 글타래에서 저는 계속 "해시의 배열(정확히는 해시 레퍼런스들로 이루어진 배열)"을 쓰고 있습니다. 특히나 해시를 잘 쓰면 웬만한 문제는 다 간단히 풀립니다. 과제가 언제까지인지는 모르겠지만, 아마 과제 코드를 잠시 접고 몇 시간 정도 책이나 웹을 보며 공부하는 게 오히려 과제를 더 빨리 끝낼 수 있을지도 모르겠군요.

P.S. 이거 KLDP 개편된 후 버그가 있는지... 댓글의 댓글로 작성한 건데 막상 작성 버튼 누르고 나니 원글의 댓글로 레벨(?)이 당겨져 있네요. 어떤 경우에 이렇게 되어버리는지는 모르겠는데(미리 보기를 한다거나 글 작성에 일정 시간 이상 쓴다거나?)

좋은 하루 되세요!

thwan2rokmc의 이미지

과제이긴한데 빨리해야하는거라 ㅠㅠ 그런데 목표가 GUI 구현까지에요 !
감사해요 정말 덕분에 많이도 배웠어요 raymundo님:D

제가 결과값이 이렇게 나왔어요~
11.703670 1 10438040x Rx d 1 01 Length = 2334682 BitCount = 82 ID = 272859200x

12.108770 1 10438040x Rx d 1 00 Length = 2338248 BitCount = 82 ID = 272859200x

12.803904 1 10438040x Rx d 1 01 Length = 2338248 BitCount = 82 ID = 272859200x

13.203957 1 10438040x Rx d 1 00 Length = 2338248 BitCount = 82 ID = 272859200x

13.704059 1 10438040x Rx d 1 01 Length = 2338248 BitCount = 82 ID = 272859200x

14.149137 1 10438040x Rx d 1 00 Length = 2334682 BitCount = 82 ID = 272859200x

The_actual_number_of_actions_HIT: 6

The_actual_number_of_action_ID: 10438040x

ㅎㅎㅎㅎ:D
그런데 저기에서 아이디만 덩그러니 나오는게 아니라 위에처럼 전체 여섯개 라인이 다 나오게 할순없을까요? 아까 저녁에 계속 두드려봤는데 되질 않아요 !
해시 그래도 조금 아주 쪼금 이해했다고 생각했는데 아닌가봐요 ㅠㅠ

#!user/bin/perl
#CAN_Analyzer
 
use strict;
 
my @NoEvent;
open my $IN, "<", 'NoEvent.txt' or die $!;
while( my $line =<$IN>){
        chomp $line;
        if($line !~/Sta|Err|active/){
        my ($time, $channel, $id, $trans, $state, $dlc, $others)=split(" ",$line, 7);
 
        my @frame_bytes;
        if($dlc >= 0){
        @frame_bytes=(split " ",$others)[0..$dlc-1];
        }
 
        my $dataframe= join " ", @frame_bytes;
        my $tmp_hash={
                id =>$id,
                dataframe => $dataframe,
                line =>$line,
                };
        push @NoEvent, $tmp_hash;
        }
    }
        close $IN;
 
my %count;
my (@NoEvent_same_ID, @Event_same_ID, @ONLY_ID, @ONLY_ID2);
open $IN, "<", 'Event.txt' or die $!;
while(my $line=<$IN>){
        chomp $line;
        if($line !~/Sta|Err|active/){
        my(undef, undef, $id, undef, $dlc, $others)=split(" ", $line, 7);
 
        my @frame_bytes;
        if($dlc >= 0){
        @frame_bytes =(split " ", $others)[0 .. $dlc-1];
        }
        my $dataframe= join " ", @frame_bytes;
        my $found;
        foreach my $line_a(@NoEvent){
          if($line_a->{id} eq $id){
                $found = $line_a;
          if($line_a->{dataframe} ne $dataframe){
                push @NoEvent_same_ID, $line_a->{line};
                push @Event_same_ID, $line;
  }
                last;
        }
      }
 
          if( ! $found){
                 push @ONLY_ID, {id=>$id, line=>$line};
                }
        }
      }
close $IN;
my $list;
 
my $Filter_Lines= @ONLY_ID;
print "----------------------------------------------FILTER_ID "."$Filter_Lines "."LINES------------------------------------------------"."\n";
 
foreach my $list (@ONLY_ID){
    print "$list->{line}"."\n\n";
}
my %count;
foreach my $line (@ONLY_ID){
    my $id = $line->{id};
    $count{$id}++;
}
print "                                   The_actual_number_of_actions_HIT: ";
my $Num += <>;
chomp $Num;
 
while ( my ( $id, $count ) = each %count ) {
    if ($count == $Num){
        print"\n"."                                The_actual_number_of_action_ID: ".$id."\n\n";
    }
}

요거인데요! 여기에서 제가 하려는게 저 상태에서 텍스트파일을 하나더 파일을 읽어와서 현재 존재하는 ID랑 또 똑같은 필터링을 하는것과 아이디 횟수로도 필터링하고 시간 범위에따라서도 필터링을 하고싶은데 어찌해야할까요!! GUI 구현은 아무래도 무리겠죠 주말까지도 ㅠㅠ
주말까지만 하구 저도 책을 정독해려구요 ㅠㅠ 이해도가 낮아 잘 안되는것같아요 ,,
여하튼 raymundo님 항상 감사해요:D 이렇게 답변도 해주시는데 조언까지 해주셔서 !! 프로그램 코딩에 흥미가 생기기 시작했어요!!
아 원글의 댓글로 레벨이 당겨진다는 말이 무슨 말씀이신지 잘 모르겠어요,, 댓글의 댓글로 되는게 아니라 원글의 새 댓글로 생성이 된다는 말이신거에요~?
제 컴퓨터엔 댓글의 댓글로 작성되어있어요 ㅎㅎ 좋은밤 되세요~:)
raymundo의 이미지

> 그런데 저기에서 아이디만 덩그러니 나오는게 아니라 위에처럼 전체 여섯개 라인이 다 나오게 할순없을까요?
> 아까 저녁에 계속 두드려봤는데 되질 않아요 !

while ( my ( $id, $count ) = each %count ) {
    if ($count == $Num){
        # 이 지점에서
        # 원하는(즉 등장횟수가 입력받은 Num과 같은) id 를 찾았고
        # @ONLY_ID 배열에는 각 라인들의 id 와 line 쌍이 해시의 배열로 저장되어 있으니
        foreach my $hash_ref ( @ONLY_ID ) {
            # $hash_ref의 id 필드가 $id 와 같으면 line 필드 출력
        }
#        print"\n"."                                The_actual_number_of_action_ID: ".$id."\n\n";
    }
}

> 시간 범위에따라서도 필터링을 하고싶은데

처음에 파일에서 각 라인을 읽을 때 time 필드도 뽑아냈으니, 그 값을 해시에 같이 보관하세요.

{
   id => '...',
   line => '.....',
   time => '14.149137'   # 이렇게
}

그러면 해시의 배열을 순회하면서 각 해시의 time 필드 값이 원하는 범위일 때만 line 을 출력하면 되죠.

좋은 하루 되세요!

thwan2rokmc의 이미지

덕분에 많이 배우고 가요 :)
정말 감사하고 좋은보내세요 ~
추석 가족들이랑 즐겁게 보내시길 바래요 ㅎㅎ

댓글 달기

Filtered HTML

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

BBCode

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param>
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

Textile

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • You can use Textile markup to format text.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Markdown

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • Quick Tips:
    • Two or more spaces at a line's end = Line break
    • Double returns = Paragraph
    • *Single asterisks* or _single underscores_ = Emphasis
    • **Double** or __double__ = Strong
    • This is [a link](http://the.link.example.com "The optional title text")
    For complete details on the Markdown syntax, see the Markdown documentation and Markdown Extra documentation for tables, footnotes, and more.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Plain text

  • HTML 태그를 사용할 수 없습니다.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 줄과 단락은 자동으로 분리됩니다.
댓글 첨부 파일
이 댓글에 이미지나 파일을 업로드 합니다.
파일 크기는 8 MB보다 작아야 합니다.
허용할 파일 형식: txt pdf doc xls gif jpg jpeg mp3 png rar zip.
CAPTCHA
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.