• 締切済み

初心者です・・・ファイルの結合

言語の研究をしたいのですが、Excelで進めるにはデータ量が多いことがわかりました。 そこで、perlを使おうと思ったのですが、初心者でわからないことばかりです・・・・どなたか助けてくださると幸いです。 次のような二つのファイルがあるとします。 File1 食べる 10 書く  2 歌う  3 ・ File2 使う 3 見る 10 食べる 3 このファイルを結合するためにはどのようにしたらよいでしょうか? よろしくお願いします。

みんなの回答

  • t140
  • ベストアンサー率39% (59/150)
回答No.9

(1)$hash{$1}->{$ARGV}= $2; (2)$hash{$key}->{$total}+= ($hash{$key}->{$_} or $hash{$key}->{$_}= 0); ------------------------------------------------------------------- 文字列「食べる」が、File1では10、File2では3という値 また、「書く」が、File1では2、File2では0という値ならば $hash{文字列X}->{ファイル名Y}= 値; のパターンで表現できます。 $hash{"食べる"}->{File1}= 10; $hash{"食べる"}->{File2}= 3; $hash{"書く"}->{File1}= 2; $hash{"書く"}->{File2}= 0; これは $hash{"食べる"}= {File1=>10, File2=>3}; $hash{"書く"}= {File1=>2, File2=>0}; のようにまとめて書いたものと同じことです。 $hash{文字列X}は、それぞれ無名ハッシュへのリファレンスです。 単純なハッシュではFile1は10、File2は3などの関係しか 表現することができませんので {File1=>10, File2=>3} という無名ハッシュのアドレスを$hash{"食べる"}に代入することで 「$hash{"食べる"}->{File1}の持つ値は10」ということがわかる構造になります。 例えばスカラー変数$xに無名ハッシュのアドレスを代入しても $x= {File1=>10, File2=>3}; 「$x->{File1}の持つ値は10」というように単純な関係しか表現できません。 というわけでハッシュに無名ハッシュのアドレスを代入することが重要です。 (1)に関してはこういった感じでいかがでしょうか? (2)に関しては文字列Xのときのトータル値を集計しています。 (文字列Xごとに各ファイル名にセットされている値をループでまわして集計) $hash{文字列X}->{トータル}+= $hash{文字列X}->{ファイル名Y}; この集計をするついでに、値が未定義の場合に0をセットしています。 単純に書くと以下のような感じです。 $total+= ($n or $n=0); $nに0よりも大きい値がセットされていれば右辺はその値を返すので $total+= $n; # $n > 0 と書いたのと同じようになります。 もしも$nの値が未定義だったり0だったり空文字列の場合には or 以降が実行されて $nに0値を代入した結果を右辺が返すので $total+= $n; # $n == 0 と書いたのと同じようになり、$totalはプラスされることはありませんが、$nに0値を代入できます。

  • t140
  • ベストアンサー率39% (59/150)
回答No.8

#こんな感じでどうでしょう。 # 実行時に渡された引数ファイル(File1 File2 File3・・・)を行単位で順に$_にセット。 while (<>){ # while ($_=<>)の省略形 # 行頭の文字列(途中に空白含んでいてもOK)および行末の数値を$1,$2にセット /^(.*\S)\s+(\d+)$/ or next; # $_=~/^(.*\S)\s+(\d+)$/ の省略形 # 文字列をハッシュのキーにして、ファイル名ごとにその数値をセット #($ARGVは、読み出し中のファイル名がセットされる特殊変数) $hash{$1}->{$ARGV}= $2; } # 表のヘッダを標準出力へ印字 print join("\t", undef, $total, @argv),"\n"; # 文字列を$keyにセットしていき文字列ごとの処理を繰り返す(ループ) for my $key (keys %hash){ # このループでは、現在の$key文字列の各ファイルでの値を集計してトータル値をセット #(その際に別ファイルでしか出現していない文字列があれば0値をセットして補正) for (@argv){ # for $_ (@argv)の省略形 $hash{$key}->{$total}+= ($hash{$key}->{$_} or $hash{$key}->{$_}= 0); } # 文字列、トータル値、ファイルごとの値の順で標準出力へ印字 #(トータル値、引数指定ファイルの順になるようにハッシュのスライスを使用) print join("\t", $key, @{$hash{$key}}{$total,@argv}),"\n"; }

hiromi310
質問者

お礼

t140さん さっそくのご回答、ありがとうございます! 少しずつ理解に近づいているように気がします・・・ しかしながら、1つだけわからないことがあります。 それは ->の存在です。自分の持っている本によれば、これはリファレンスを表すものだと?理解していますが、ここでの内容とどうしても結び付けることができません。自分の本が初心者向きだからでしょうか? (1)$hash{$1}->{$ARGV}= $2; (2)$hash{$key}->{$total}+= ($hash{$key}->{$_} or $hash{$key}->{$_}= 0 の2文で出てきますが、もう少しこの2文でさせていることをご説明くださいませんでしょうか。

  • t140
  • ベストアンサー率39% (59/150)
回答No.7

#4です。コマンドラインから複数ファイルを指定したい場合は#5様のやり方で可能です。 #4をコマンドライン対応に書き換えると以下のような感じになります。 #Usage: ./command.pl File1 File2 File3 >result.txt #!/usr/bin/perl use strict; my %hash; my $total= 'total'; my @argv= @ARGV; # set data while (<>){ /^(.*\S)\s+(\d+)$/ or next; $hash{$1}->{$ARGV}= $2; } # print result print join("\t", undef, $total, @argv),"\n"; for my $key (keys %hash){ for (@argv){ $hash{$key}->{$total}+= ($hash{$key}->{$_} or $hash{$key}->{$_}= 0); } print join("\t", $key, @{$hash{$key}}{$total,@argv}),"\n"; }

hiromi310
質問者

お礼

T140さん 助けてくださり、本当にありがとうございます。 初心者なりにいろいろ調べてもわからないところがあるので、お答えいただけるとうれしいです。 まず、下記の部分なのですが、/^(.*\S)\s+(\d+)$/ で文字列データ、数値データを認識させる?、そして次に、$hash{$1}->{$ARGV}= $2;の記述で何をさせているのでしょうか? while (<>){ /^(.*\S)\s+(\d+)$/ or next; $hash{$1}->{$ARGV}= $2; } 次の部分はどのように解釈したらよいのでしょうか? $hash{$key}->{$total}+= ($hash{$key}->{$_} or $hash{$key}->{$_}= 0); T140さんに頼りっぱなしですみません。お答えいただけると本当に嬉しいです。

  • BLUEPIXY
  • ベストアンサー率50% (3003/5914)
回答No.6

>複数のテキスト #5 の場合、複数のテキストをコマンドラインで指定するだけです。

  • BLUEPIXY
  • ベストアンサー率50% (3003/5914)
回答No.5

#perl add.pl File1 File2 > total while(<>){ @field = split /[ \s]+/; $data{$field[0]} += $field[1]; } while(($key, $value) = each(%data)){ print "$key $value\n"; }

hiromi310
質問者

お礼

ご丁寧にありがとうございました。

  • t140
  • ベストアンサー率39% (59/150)
回答No.4

#書き方いろいろ use strict; my @files= ('File1', 'File2'); my %hash; my $total= 'total'; # set data for my $file (@files){ open(FH, $file) or die; while (<FH>){ /^(.+)\s+(\d+)$/ or next; $hash{$1}->{$file}= $2; } close(FH); } # print result print join("\t", undef, $total, @files),"\n"; for my $key (keys %hash){ for (@files){ $hash{$key}->{$total}+= ($hash{$key}->{$_} or $hash{$key}->{$_}= 0); } print join("\t", $key, @{$hash{$key}}{$total,@files}),"\n"; }

hiromi310
質問者

補足

ありがとうございます。これだけのものをすぐに理解する力はありませんが、ちょっとずつ学んでいこうと思います。 ところで、今「コーパス言語学の技法II(夏目書房)」という本を使ってテキスト処理の方法を学んでいるのですが、そこで、スキャンファイルを使って、複数のテキスト(*今の例ではファイルが二つのみでしたが・・・)一括処理する方法を紹介されているのですが、t140さんが教えてくださったものと関連付け、一括処理をさせることは可能でしょうか? この本で紹介されているのは下記のようなものです。もし、おわかりになるようでしたら、よろしくおねがいします。 use File::Scanfile; use Getopt::Long; $Getopt::Long::autoabbrev = 1; $\ = "\n"; my $usage = "Usage: perl add.pl [-r] -i <file1;file2 ...> -o <file>\n"; # コマンドライン引数処理 my @opt = ("recursive!", "in=s", "out=s"); GetOptions @opt or die $usage; $opt_in && $opt_out or die $usage; $opt_in ne $opt_out or die "The file cannot be copied onto itself\n"; if (! $opt_recursive) { $opt_recursive = 0; } # ファイル名の取得 my @files = scanfile($opt_in, $opt_recursive); unless (@files) { die "No files matching the pattern: $opt_in\n" }

  • moon_night
  • ベストアンサー率32% (598/1831)
回答No.3

何のひねりもないやり方で。 #!/usr/local/bin/perl $File1 = 'file1.txt'; $File2 = 'file2.txt'; $File3 = 'file3.txt'; #FILE1を開く open(IN,"$File1"); @file1 = <IN>; close(IN); foreach (@file1) { chomp; ($a,$b) = split(/\t/); $data1{$a} += $b; $data2{$a} += $b; } #FILE2を開く open(IN,"$File2"); @file2 = <IN>; close(IN); foreach (@file2) { chomp; ($a,$b) = split(/\t/); $data1{$a} += $b; $data3{$a} += $b; } #データ収集 while ( ( $key, $value ) = each( %data1 ) ) { $data .= $key ."\t" .$value ."\t"; if ($data2{$key}) { $data .= $data2{$key}; } else { $data .= 0; } $data .= "\t"; if ($data3{$key}) { $data .= $data3{$key}; } else { $data .= 0; } $data .= "\t\n"; } # 書出す open(OUT,">$File3"); print OUT $data; close(OUT);

hiromi310
質問者

お礼

ありがとうございます。初心者の自分にはいちばんわかりやすい例なのかもしれません。

  • vsba23895
  • ベストアンサー率58% (18/31)
回答No.2

use strict; my @file_list= qw(File1 File2); my $counter= {}; foreach my $file (@file_list) {  get_file($counter,$file); } foreach my $word (sort keys(%$counter)) {  my @out_list;  foreach my $file (@file_list) {   push(@out_list,((defined $counter->{$word}->{$file})?$counter->{$word}->{$file}:0));  }  print join("\t",$word,@out_list),"\n"; } sub get_file {  my($outhash,$fname)= @_;  if (open(F,$fname)) {   while(<F>) {    s/^\s+//;    my($word,$count,$junk)= split(/\s+/);    $outhash->{$word}||= {};    if (defined $outhash->{$word}->{$fname}) {     warn "$fname ($.): word($word) duplication in a file ignored\n";    } else {     $outhash->{$word}->{$fname}= $count;    }   }   close(F);  } else {   warn "cannot find input($fname)\n";  } }

hiromi310
質問者

補足

ありがとうございます。これだけのものをすぐに理解する力はありませんが、ちょっとずつ学んでいこうと思います。 ところで、今「コーパス言語学の技法II(夏目書房)」という本を使ってテキスト処理の方法を学んでいるのですが、そこで、スキャンファイルを使って、複数のテキスト(*今の例ではファイルが二つのみでしたが・・・)一括処理する方法を紹介されているのですが、t140さんが教えてくださったものと関連付け、一括処理をさせることは可能でしょうか? この本で紹介されているのは下記のようなものです。もし、おわかりになるようでしたら、よろしくおねがいします。 use File::Scanfile; use Getopt::Long; $Getopt::Long::autoabbrev = 1; $\ = "\n"; my $usage = "Usage: perl add.pl [-r] -i <file1;file2 ...> -o <file>\n"; # コマンドライン引数処理 my @opt = ("recursive!", "in=s", "out=s"); GetOptions @opt or die $usage; $opt_in && $opt_out or die $usage; $opt_in ne $opt_out or die "The file cannot be copied onto itself\n"; if (! $opt_recursive) { $opt_recursive = 0; } # ファイル名の取得 my @files = scanfile($opt_in, $opt_recursive); unless (@files) { die "No files matching the pattern: $opt_in\n" }

  • BLUEPIXY
  • ベストアンサー率50% (3003/5914)
回答No.1

食べる 13 になるというようなことですか?

hiromi310
質問者

補足

BLUEPIXYさん、すみません。説明不足でした。 結合といってもいろいろな捉え方がありますね。 目標としたいのは次のような形式です。    total   File1   File2 食べる13     10     3 書く 2      2     0 歌う 3      3     0 使う 3      3     0 見る 10      0     10

関連するQ&A