私は以下を持っています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
列に存在する場合は、そのファイル内のすべてのアイテムの印刷をスキップしたいと思います。XXX
v1
Type1
- 与えられた名前の列に合計が
Type4
ある場合は、小さい値を持つ行だけを印刷したいと思います。ABC
DEF
v1
v2
- or と同じ名前の場合
Type5
orType6
だけがある場合はABC
印刷DEF
したいと思います。
私は何をすべきですか?ファイルを配列として読み取ることができますが、複数の行から特定の列を取得する方法がわかりません。
答え1
これに必要なツールはハッシュです。これは、キーと値のペアを格納するPerlの方法です。特に、最も低い値または発生項目を「見つける」ことができるように、データをハッシュとして前処理する必要がありますXXX
。
幸いなことに、3番目の条件は2番目の条件のサブセットのように見えます。最も低い値だけを印刷すると、1つしかないときは最も低い値が同じになります。
だから私はこれを行うことができます:
#!/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
3つの異なる任務があります。以下を使用してすべての操作を実行できますawk
。
XXX以降の印刷をスキップ
$1 == "Type1" {if($2 == "XXX")f=1;if(! f)print}
Type4の最小値
$1 == "Type4" {if(min > $3 || ! min)min = $3} END{print min}
選択ライン印刷
$1$2 ~ "^(Type5|Type6)(ABC|DEF)$"