1

次のようにレイアウトされたテキストファイルがあります。

1   a, b, c
2   c, b, c
2.5 a, c

これを生成するために、キー (数字) と値 (CSV) (タブ文字で区切られています) を逆にしたいと思います。

a   1, 2.5
b   1, 2
c   1, 2, 2.5

(c に対して 2 が重複していないことに注意してください。)

この正確な出力は必要ありません。入力の数値は順序付けされていますが、値は順序付けられていません。出力のキーは、値と同様に順序付けする必要があります。

これどうやってするの?標準のシェル ユーティリティ (awk、sed、grep...) と GCC にアクセスできます。必要に応じて、他の言語用のコンパイラ/インタプリタを入手できます。

4

6 に答える 6

1

ここにphpの小さなユーティリティがあります:

// load and parse the input file
$data = file("path/to/file/");
foreach ($data as $line) {
    list($num, $values) = explode("\t", $line);
    $newData["$num"] = explode(", ", trim($values));
}
unset($data);

// reverse the index/value association
foreach ($newData as $index => $values) {
    asort($values);
    foreach($values as $value) {
        if (!isset($data[$value]))
            $data[$value] = array();
        if (!in_array($index, $data[$value]))
            array_push($data[$value], $index);
    }
}

// printout the result
foreach ($data as $index => $values) {
    echo "$index\t" . implode(", ", $values) . "\n";
}   

本当に最適化されていないか見栄えがよくありませんが、機能します...

于 2009-03-22T23:17:04.097 に答える
1

利用できる場合は、perl を試してみます。一度に 1 行ずつ入力をループします。タブで行を分割し、コンマで右側の部分を分割します。文字をキーとして値を連想配列に押し込み、値を別の連想配列にします。2 番目の連想配列は、重複を排除するためにセットの役割を果たします。

入力ファイルを読み取ったら、連想配列のキーに基づいてソートし、ループして結果を吐き出します。

于 2009-03-22T22:41:43.650 に答える
0

awk(1) と sort(1) の回答は次のとおりです。

データは基本的に多対多のデー​​タ セットであるため、最初のステップは、1 行に 1 つのキーと値を使用してデータを正規化することです。また、キーと値を交換して新しいプライマリ フィールドを示しますが、下位の部分は順序に依存しないため、これは厳密には必要ありません。タブまたは [スペース],[スペース] をフィールド区切りとして使用するため、キーと値の間、および値の間でタブを分割します。これにより、値に埋め込まれたスペースが残りますが、前後から削除されます。

awk -F '\t| *, *' '{ for (i=2; i<=NF; ++i) { print $i"\t"$1 } }'

次に、並べ替え順序を適用して重複を排除します。bash の機能を使用して、区切り文字としてタブ文字を指定します (-t $'\t')。Bourne/POSIX シェルを使用している場合は、'[tab]' を使用する必要があります。[tab] はリテラル タブです。

sort -t $'\t' -u -k 1f,1 -k 2n

次に、必要な形式に戻します。

awk -F '\t' '{ 
    if (key != $1) { 
        if (key) printf "\n";
        key=$1;
        printf "%s\t%s", $1, $2
    } else {
        printf ", %s", $2
    }
  }
  END {printf "\n"}'

それらを完全にパイプすると、目的の出力が得られるはずです。GNU ツールでテストしました。

于 2009-03-23T04:49:39.180 に答える
0
# use Modern::Perl;
use strict;
use warnings;
use feature qw'say';


our %data;

while(<>){
  chomp;
  my($number,$csv) = split /\t/;
  my @csv = split m"\s*,\s*", $csv;
  push @{$data{$_}}, $number for @csv;
}

for my $number (sort keys %data){
  my @unique = sort keys %{{ map { ($_,undef) } @{$data{$number}} }};
  say $number, "\t", join ', ', @unique;
}
于 2009-03-23T05:14:27.513 に答える
0

以下は、CSV フィールドを手動で解析するのではなく、CPAN の Text::CSV モジュールを使用した例です。

use strict;
use warnings;
use Text::CSV;

my %hash;
my $csv = Text::CSV->new({ allow_whitespace => 1 });

open my $file, "<", "file/to/read.txt";

while(<$file>) {
  my ($first, $rest) = split /\t/, $_, 2;
  my @values;

  if($csv->parse($rest)) {
    @values = $csv->fields()
  } else {
    warn "Error: invalid CSV: $rest";
    next;
  }

  foreach(@values) {
    push @{ $hash{$_} }, $first;
  }
}

# this can be shortened, but I don't remember whether sort()
# defaults to <=> or cmp, so I was explicit
foreach(sort { $a cmp $b } keys %hash) {
  print "$_\t", join(",", sort { $a <=> $b } @{ $hash{$_} }), "\n";
}

標準出力に出力されることに注意してください。標準出力をリダイレクトすることをお勧めします。このプログラムを拡張する場合は、エラーを単にing するwarn()のではなく、使用して出力するようにしてください。print()また、エントリの重複はチェックされませんが、自分のコードを Brad Gilbert のコードのように見せたくありません。これは、Perlite から見ても少し奇妙に見えます。

于 2009-03-23T05:18:11.497 に答える