Perl의 크로스라인 비교

Perl의 크로스라인 비교

나는 다음을 가지고 있습니다 file:

Name v1 v2 
Type1 ABC 32
Type1 DEF 44
Type1 XXX 45
Type2 ABC 78 
Type2 XXX 23 
Type3 DEF 22 
Type3 XXX 12 
Type4 ABC 55 
Type4 DEF 78 
Type5 ABC 99 
Type6 DEF 00

다음 조건으로 파일의 일부만 인쇄하려고 합니다.

  • 예를 들어 특정 이름이 Type1열에 존재하는 경우 해당 파일의 모든 항목 인쇄를 건너뛰고 싶습니다.XXXv1Type1
  • 주어진 이름에 대해 열에 합계가 Type4있으면 더 작은 값을 가진 행만 인쇄하고 싶습니다.ABCDEFv1v2
  • or 와 같은 이름의 경우 Type5or Type6만 있는 경우 ABC인쇄 DEF하고 싶습니다.

나는 무엇을 해야 합니까? 파일을 배열로 읽을 수 있지만 여러 행에서 특정 열을 검색하는 방법을 모르겠습니다.

답변1

이를 위해 필요한 도구는 해시입니다. 이는 키-값 쌍을 저장하는 Perl의 방식입니다. 특히 가장 낮은 값이나 발생 항목을 "찾을" 수 있도록 데이터를 해시로 전처리해야 합니다 XXX.

다행스럽게도 세 번째 조건은 두 번째 조건의 하위 집합처럼 보입니다. 가장 낮은 값만 인쇄하면 하나만 있을 때 가장 낮은 값이 동일합니다.

그래서 나는 이렇게 할 수 있습니다:

#!/usr/bin/env perl
use strict;
use warnings;
use Data::Dumper;

#read header line, because we don't want to process it; 
#note - diamond operators are 'magic' file handles. 
#they read either piped input on STDIN, or 
#open/read files specified on command line. 
#this is almost exactly like how sed/grep work. 
my $header_line = <>;
#turn the rest of our intput into an array of arrays, split on whitespace/linefeeds. 
my @lines = map { [split] } <>;

#print for diag
print Dumper \@lines;

#this hash tracks if we've 'seen' an XXX
my %skip_type;
#this hash tracks the lowest V2 value. 
my %lowest_v2_for;
foreach my $record (@lines) {
    #we could work with $record ->[0], etc.
    #this is because I think it's more readable this way. 
    my ( $type, $v1, $v2 ) = @$record;

    #find all the lines with "XXX" - store in a hash.
    if ( $v1 eq "XXX" ) {
        $skip_type{$type}++;
    }

    #check if this v2 is the lowest for this particular type. 
    #make a note if it is. 
    if ( not defined $lowest_v2_for{$type}
        or $lowest_v2_for{$type} > $v2 )
    {
        $lowest_v2_for{$type} = $v2;
    }
}

#print for diag - things we are skipping. 
print Dumper \%skip_type;


print $header_line;

#run through our list again, testing the various conditions:
foreach my $record (@lines) {
    my ( $type, $v1, $v2 ) = @$record;

    #skip if it's got an XXX. 
    next if $skip_type{$type};
    #skip if it isn't the lowest value
    next if $lowest_v2_for{$type} < $v2;
    #print otherwise.
    print join( " ", @$record ), "\n";
}

이는 다음과 같은 결과를 제공합니다(일부 진단 결과가 적으므로 Dumper필요하지 않은 경우 자유롭게 폐기하십시오).

Name v1 v2 
Type4 ABC 55
Type5 ABC 99
Type6 DEF 00

답변2

내 관점 에선:

perl -wE ' 
    # read the data 
    chomp( my $header = <> ); 
    my %data; 
    while (<>) { 
        chomp; 
        my @F = split; 
        $data{$F[0]}{$F[1]} = $F[2]; 
    } 

    # requirement 1 
    delete $data{Type1} if exists $data{Type1}{XXX}; 

    # requirement 2 
    if (exists $data{Type4}{ABC} and exists $data{Type4}{DEF}) { 
        if ($data{Type4}{ABC} <= $data{Type4}{DEF}) { 
            delete $data{Type4}{DEF}; 
        } 
        else { 
            delete $data{Type4}{ABC}; 
        } 
    } 

    # requirement 3 
    for my $name (qw/Type5 Type6/) { 
        delete $data{$name} unless ( 
            scalar keys %{$data{$name}} == 1 
            and (exists $data{$name}{ABC} or exists $data{$name}{DEF}) 
        ); 
    } 

    $, = " "; 
    say $header; 
    for my $name (sort keys %data) { 
        for my $v1 (sort keys %{$data{$name}}) { 
            say $name, $v1, $data{$name}{$v1}; 
        } 
    } 
' file 

산출

Name v1 v2 
Type2 ABC 78
Type2 XXX 23
Type3 DEF 22
Type3 XXX 12
Type4 ABC 55
Type5 ABC 99
Type6 DEF 00

Type2 및 Type3에 대한 요구 사항은 없습니다.

답변3

세 가지 다른 임무가 있습니다. 다음을 사용하여 모든 작업을 수행할 수 있습니다 awk.

  1. XXX 이후 인쇄 건너뛰기

    $1 == "Type1" {if($2 == "XXX")f=1;if(! f)print}

  2. Type4의 최소값

    $1 == "Type4" {if(min > $3 || ! min)min = $3} END{print min}

  3. 선택라인 인쇄

    $1$2 ~ "^(Type5|Type6)(ABC|DEF)$"

관련 정보