ウォーターサーバーとコーヒーマシンが一体化した画期的マシン >>

いつもお世話になっています。
今回perlで2つのCSVファイルを比較して、マッチしたものをエクセルで表示させるスクリプトを作ろうとしています。
エクセルに表示するときに、OLE32を使うことは前回教えていただいたのですが、2つのファイルを比較する方法が分かりません;
ネットなどでは調べたのですが、参考になるサイトなど見つけられなかったので、詳しい方お力をお貸しください。
参考になりそうなサイトも教えていただけると、とても助かります。
よろしくお願いします。

このQ&Aに関連する最新のQ&A

A 回答 (3件)

>一致した項目だけをエクセルで書き出したいのです



 CSVファイルでは、行のことを「レコード」、その行に含まれるデータ1つ1つを「項目」と呼びますが、その認識でよろしいでしょうか?
 ファイルAとBのレコードの数が一致しており、それぞれ1行ずつ読み込んで項目を順番に比較すればいいのであれば、

open(A, "<fileA");
open(B, "<fileB");
while ( <A> ) {
 @a = split(/,/,$_);
 @b = split(/,/,<B>);
 for($i = 0; $i < @a; $i++ ) {
  if ( $a[$i] eq $b[$i] ) {
   print "$a[$i]\t";
  }
 }
 print "\n";
}

 これでイケるはずです。
 でもこれだとデータの順番とかデタラメになっちゃいますけど、いいんですか?(^_^;
 最終的に何がやりたいのか分からないので、これ以上アドバイスしようがないのですが。
    • good
    • 0
この回答へのお礼

お返事遅くなりました。
先ほど何とか解決しました。
何度も解答していただきありがとうございました。

お礼日時:2004/09/15 17:48

>CSVファイルを比較して


 とありますが、サンプルとしてどういった
構造なのかを書いていただけるとありがたいです。

 他の方がやられているように『eq』でやる文字列比較
のやり方にあう場合とhashによるデータ比較と大まかに
2種類ぱっと思いつきます。
 只、やはりデータの構造が分からないとどちらをお勧め
すべきかは判断がつきません。
    • good
    • 0
この回答へのお礼

[eq]で何とか出来ました!
ご解答ありがとうございました。

お礼日時:2004/09/15 17:49

 どういう方法で比較するんでしょうか?


 この質問文からだと、「読み込んで比較演算子 eq で比較する」という回答の仕方しかできませんが……。

この回答への補足

>>どういう方法で比較するんでしょうか?
中身が似ているけれど、違うファイルAとBを比較して、一致した項目だけをエクセルで書き出したいのです。。。
分かりにくくてすみません。これで分かるでしょうか…?

補足日時:2004/09/09 10:42
    • good
    • 0

このQ&Aに関連する人気のQ&A

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!

このQ&Aを見た人はこんなQ&Aも見ています

このQ&Aを見た人が検索しているワード

このQ&Aと関連する良く見られている質問

Q複数ファイルの読み込みについて

perl初心者です。

あるディレクトリから拡張子がdataであるファイルを全て読み込みたいのですが、方法がわかりません。
cshで書くと
foreach arg (*.data)
コマンド $arg

のようになりますが、perlだと
foreach $arg (@arg){
コマンド $arg

となりますよね?
引数がリストなのでよくわかりません。
そもそもperlではできないのでしょうか?


それともう一点ですが、ファイルオープンするときに
foreachループの中で
open(FILE, "$arg");
とすることは可能ですか?
上の質問と組み合わせて全てのファイルを開いて作業を行いたいので。

説明が下手ですいません。補足しますのでよろしくお願いします。

Aベストアンサー

while(<*.data>)
{
## $_には、*.DATAなファイル名が格納されている。
open(F,"$_"); ##openする。
while(<F>)
{
##読み出された内容が$_に格納されている。
print $_; ##出力してみる。
}
}

というのが最短コーディングです。

QPerlで特定行から特定行までを抜き出したい

皆さんのお知恵をお貸し頂ければ幸いです。

Perlで以下のようなことをしたいと考えています。
例えば、次のようなテキストファイルがあったとします。

example.log
==================================
aaaa
hogehoge
test
okok
perl
script
==================================

上記ファイルを読み込んで、「hogehoge」から「perl」の間に挟まれた行だけ抜き出したいのです。
イメージとしては、読み込んだファイルを配列に入れて、一行づつ読ませ、キーワード「hogehoge」が現れたらそこでフラグを立て、それ以降の行を表示し、キーワード「perl」が現れた時点で表示を止めるという処理になるのかな?と思っています。

このような場合、どういう風にすればいいのでしょうか?
恐れ入りますが、ご教授頂ければ幸いです。

それでは、どうぞよろしくお願い致します。

Aベストアンサー

> 一行づつ読ませ、キーワード「hogehoge」が現れたらそこでフラグを立て、それ以降の行を表示し、キーワード「perl」が現れた時点で表示を止めるという処理になるのかな?と思っています。

それでいいと思いますよ?これをそのままコード化すると、こんな感じでしょうか。(No.1さんのとはちょっと結果が違います。)

open FH, "example.log" or die $!;
$flag = 0;
while ($data = <FH>) {
  chomp $data;
  if  ($data eq "hogehoge") { $flag = 1 }
  elsif ($data eq "perl")    { $flag = 0 }
  elsif ($flag) { print "$data\n" }
}
close FH;

で、もっと略したいPerlな人だとこんな感じ。Perl独特の記法がふんだんに使われているので、勉強するには不向きかもしれませんが^^;

open FH, "example.log" or die $!;
while (<FH>) {
  print if /^hogehoge$/ .. /^perl$/ and !/^(?:hogehoge|perl)$/;
}
close FH;

※インデントに全角空白を使っているので、コピーする場合はタブなどに置換して下さい。

> 一行づつ読ませ、キーワード「hogehoge」が現れたらそこでフラグを立て、それ以降の行を表示し、キーワード「perl」が現れた時点で表示を止めるという処理になるのかな?と思っています。

それでいいと思いますよ?これをそのままコード化すると、こんな感じでしょうか。(No.1さんのとはちょっと結果が違います。)

open FH, "example.log" or die $!;
$flag = 0;
while ($data = <FH>) {
  chomp $data;
  if  ($data eq "hogehoge") { $flag = 1 }
  elsif ($data eq "perl")    { $fl...続きを読む

QUse of uninitialized value ---

初心者です。フォームに文字を入力してもらい、↓
print blockquote(
textfield(
-name => 'die Antwort',
省略----条件にあえば、
my $value = param('die Antwort');#として
if (($value eq $ans || $value eq $ans2) and ($c eq $num)){
「正解」と表示することにしました。すると、うまくいくのですが、
if (($value eq $ans || $value eq $ans2) and ($c eq $num)){
について「Use of uninitialized value ----」とApacheのerror logに書かれてしまいます。このためerror logがすぐに巨大なファイルになってしまいます。これを避ける方法をお教えください。よろしく、お願いいたします。

Aベストアンサー

コードに use warnings が入っているのですよね。
デバッグも済んでいるのでしたら、
no warnings qw ( uninitialized );
で、この警告の表示を抑制してもよいと思います。

QPerlの初心者です。2重ループの方法で困ってます。

ある二つのファイル(moto1.csvとmoto2.csv)の2番目のフィールドが
おなじときに二つのファイルの中身をあわせて別のファイル(kekka.csv)を
作る作業をしています。
下記のソースで※2の場所で何回もファイルをオープンさせるととても重いので
※1でファイルを一回だけオープンさせて処理しようと思ったら。
内側のループ(moto2_Log)が一回しか処理されないので困っております。

何かよいアイデアがありましたらよろしくお願いします。

open(moto1_Log,"< moto1.csv");
open(kekka_Log,"> kekka.csv");
※1open(moto2_Log,"< moto2.csv");

while( <moto1_Log> ) {
chop;
@moto1_List=split(/,/);

※2#open(moto2_Log,"< moto2.csv");
while( <moto2_Log> ) {
chop;
@moto2_List=split(/,/);

if($moto1_[1] eq $moto2_List[1]){
print kekka_Log $S_List[0];
print kekka_Log ",";
print kekka_Log $S_List[1];
print Export_Log ",";
print Export_Log $S_List[2];
print Export_Log ",";
print Export_Log $S_List[3];
print Export_Log ":";
print Export_Log $E_List[0];
print Export_Log ",";
print Export_Log $E_List[1];
print Export_Log ",";
print Export_Log $E_List[2];
print Export_Log ",";
print Export_Log $E_List[3];
print Export_Log "\n";#改行コード
continu;
}
}
}

close (moto2_Log);
close(kekka_Log);
close(moto1_Log);

ある二つのファイル(moto1.csvとmoto2.csv)の2番目のフィールドが
おなじときに二つのファイルの中身をあわせて別のファイル(kekka.csv)を
作る作業をしています。
下記のソースで※2の場所で何回もファイルをオープンさせるととても重いので
※1でファイルを一回だけオープンさせて処理しようと思ったら。
内側のループ(moto2_Log)が一回しか処理されないので困っております。

何かよいアイデアがありましたらよろしくお願いします。

open(moto1_Log,"< moto1.csv");
open(kekka_Log,"> kekka...続きを読む

Aベストアンサー

原因についてはa-kumaさんが仰ってる通りですので、
代替案を挙げたいと思います。

「何回もファイルをオープンさせるととても重いので」という
ことですが、ファイルを一々巻き戻す(seekする)現在の方法でも、
実行時間としてはほとんど変わらないと思いますよ。
openにかかる時間が中身をリードする時間と比較して、
誤差以上に意味のある時間になるとは思えません。

同様のプログラムをつくって、moto1.csvとmoto2.csvとして
500行のテキストファイル(具体的には500行に切り詰めた
Linuxのシステムログ/var/log/messagesファイル)
を使って実験したところ、
一々オープンする方法の実行時間(47.63秒)は、
巻き戻す方法の実行時間(47.28秒)と比較して、
たった0.7%しか増加しませんでした。

この実験では、moto2.csvは全てキャッシュに乗っていますが、
キャッシュに乗りきらないほど大きなファイルになったとしても、
さして結果は変わらないと思います。

そこで代替案ですが、一旦、片方のファイルを全て
配列に読み込んではどうでしょう?

open(moto2_Log, "<moto2.csv");
my @moto2_List;
while(<moto2_Log>){
 chomp;
 my @cols = split(/,/);
 push(@moto2_List, \@cols);
}
close(moto2_Log);

open(moto1_Log, "<moto1.csv");
open(kekka_Log, ">kekka.csv");
while(<moto1_Log>){
 chomp;
 my @moto1_List = split(/,/);
 foreach $m2lst ( @moto2_List ){
  if( $moto1_List[1] eq $m2lst->[1] ){
   print kekka_Log ...
  }
 }
}
close(kekka_Log);
close(moto1_Log);

上の実験と同じ500行のテキストファイルに対して、
この方法だと実行時間は約5分の1の、10.17秒と
なりました。まあ、ファイルアクセスの時間だけ
じゃなくて、splitの回数も減ってるので、その
影響もあるんでしょう。

ところで、このアルゴリズムだと、moto1.csvの
中の各行とmoto2.csvの中の全行を照合してますが、
それはそれで合ってるんでしょうか?

> 2番目のフィールドが
> おなじときに二つのファイルの中身をあわせて別のファイル(kekka.csv)を
> 作る

という辺りから、moto1.csvの各行と、moto2.csvで
対応する(ファイルの先頭から数えた行数が同じ)行を
照合することを意図しているようにも読めるんですが…。

原因についてはa-kumaさんが仰ってる通りですので、
代替案を挙げたいと思います。

「何回もファイルをオープンさせるととても重いので」という
ことですが、ファイルを一々巻き戻す(seekする)現在の方法でも、
実行時間としてはほとんど変わらないと思いますよ。
openにかかる時間が中身をリードする時間と比較して、
誤差以上に意味のある時間になるとは思えません。

同様のプログラムをつくって、moto1.csvとmoto2.csvとして
500行のテキストファイル(具体的には500行に切り詰めた
Linuxのシステ...続きを読む

QPerlで行頭にある文字が含まれている行を全部削除して詰めたい

perl初心者です。以下のようにデータがならんでいる時、
test111 aaaaaaaaabbbbbbbbcccccc
test112 aaaaccccabbbbbbbbcccccc
test113 aaaaccaaabbbbbbbbcccccc
test114 acccaaaaabbbbbbbbcccccc

test111 aacaaaaaabbbbbbbbcccccc
test112 accaaaaaabbbbbbbbcccccc
test113 aaacccaaabbbbbbbbcccccc
test114 aaaaaccaabbbbbbbbcccccc

test112の行だけ削除して、さらにそこを詰めたい時のスクリプトを作成しています。

途中からわかりません。
行を削除する関数が調べても見つからないのです。

#!/usr/bin/perl ;
open(IN, "test.doc") or die ;

open(OUT, ">testout.doc");

while(<IN>) {
chomp ;
if (/(\S+)/) {

$name = $1 ;

if ($name =~ /^test112(\S+)/) {
#ここでマッチさせて、一気に行を削除して、しかも行を詰めたいのですが

;

}
print OUT " \n" ;
}
}
close (IN) ;
close (OUT) ;

大変困っております。宜しくお願いします。

perl初心者です。以下のようにデータがならんでいる時、
test111 aaaaaaaaabbbbbbbbcccccc
test112 aaaaccccabbbbbbbbcccccc
test113 aaaaccaaabbbbbbbbcccccc
test114 acccaaaaabbbbbbbbcccccc

test111 aacaaaaaabbbbbbbbcccccc
test112 accaaaaaabbbbbbbbcccccc
test113 aaacccaaabbbbbbbbcccccc
test114 aaaaaccaabbbbbbbbcccccc

test112の行だけ削除して、さらにそこを詰めたい時のスクリプトを作成しています。

途中からわかりません。
行を削除する関数が調べても見つからないのです。

...続きを読む

Aベストアンサー

一致しなかったときのみ出力するということで、

while(<IN>){
unless(/^test112/){
print OUT;
}
}

更に簡単に書くと、
while(<IN>){
print OUT unless(/^test112/);
}
となります。

削除にこだわるなら、
while(<IN>){
s/^test112\s.+//;
print OUT;
}
あたりでしょうか。

もしくは、明示的に削除したいなら、
while(<IN>){
if(/^test112\s.+/){
$_ = "";
}
print OUT;
}
というふうに、カラの文字列を代入してやるのも、値を削除するときの常套手段ですね。

Q2つのファイルのレコードの差異を抽出したいのです。

2つのファイルのレコードの差異を抽出したいのです。

ファイルAとファイルBがあります。

ファイルAの中身
id001,日本次郎,25才,東京都,2010/04/06,batch
id002,日本花子,20才,千葉県,2010/04/06,batch




ファイルBの中身
id000,日本太郎,30才,東京都,2010/04/06,batch(Bにしか存在しない)
id001,日本次郎,28才,東京都,2010/04/08,batch(年齢と更新日付が違う)
id002,日本花子,20才,千葉県,2010/04/08,batch(更新日付だけ違う)




レコードは順不同です。
AとBを比較して同じid(一番左の値です)で更新日付と更新者(一番右です)以外が更新されていたらファイルCに書き出したいと考えています。
※もしかしたら除外の項目が増えるかもしれないので、左から何文字(何項目)目同士で比較する方法は使うことができません。

どうすればできるか(レコードは数十万件以上になるので速度面も含めて)考えているのですが、何かよい方法はないでしょうか?

性能どころかまだ形にもなっていないので、取り合えず正しい結果を得られることだけを考えて下記のように進めています(それぞれの実装方法は調べながらですが)。

1.Aファイルから1行読み出す。
2.読み出した1行をカンマを区切り文字にして分解する。
3.Bファイルを1行ずつ読み込んで同じように分解して、2で分解したidと同じidだったら、更新日付と更新者以外を比較して違っていたらファイルに書き出す。
4.Bファイルに該当idがなかったらファイルに書き出す。
※Aファイルの最終行まで2~4を繰り返す。

もっといい方法があると思うのですが、シェルスクリプトの経験がないためごり押しみたいな方法になっています・・・。
もしよろしければアドバイス頂けないでしょうか。

2つのファイルのレコードの差異を抽出したいのです。

ファイルAとファイルBがあります。

ファイルAの中身
id001,日本次郎,25才,東京都,2010/04/06,batch
id002,日本花子,20才,千葉県,2010/04/06,batch




ファイルBの中身
id000,日本太郎,30才,東京都,2010/04/06,batch(Bにしか存在しない)
id001,日本次郎,28才,東京都,2010/04/08,batch(年齢と更新日付が違う)
id002,日本花子,20才,千葉県,2010/04/08,batch(更新日付だけ違う)




レコードは順不同です。
AとBを比較して同じid(一番左の値...続きを読む

Aベストアンサー

>分からなかったのはAにしかない行があるならawkに追加処理が必要とありますが、片方にしか存在しない行は片方が@になるので、このままでもうまく差分が出るかと思うのですが、

キーidがAにあって、Bにない場合、
id000,日本太郎,30才,東京都,2010/04/06,batch,@,@,@,@,@,@
のようになりますが、#1に書いたawkコマンドだとそれを考慮してません。
テストしてませんが、こんな感じかな。
awk -F, -v OFS=, '$2!=$8 || $3!=$9 || $4!=$10 {if($1=="@"){print $7,$8,$9,$10,$11,$12
}else{print $1,$2,$3,$4,$5,$6}}'

>また固定長のファイルの場合は、awkで同じような処理ができるのでしょうか?

gnu-awk (Linuxのawkだとこれ) だと、
>FIELDWIDTHS
> 空白で区切られたフィールド長のリスト。もしこの値が設定されていれば、
>gawk は FS の値を用いてフィールド分割するかわりに、固定長のフィールド分割を行います。
という機能がありますので、例えば

awk -v FIELDWIDTHS="2 4 1 3 10" '{print $1,$4}'

>分からなかったのはAにしかない行があるならawkに追加処理が必要とありますが、片方にしか存在しない行は片方が@になるので、このままでもうまく差分が出るかと思うのですが、

キーidがAにあって、Bにない場合、
id000,日本太郎,30才,東京都,2010/04/06,batch,@,@,@,@,@,@
のようになりますが、#1に書いたawkコマンドだとそれを考慮してません。
テストしてませんが、こんな感じかな。
awk -F, -v OFS=, '$2!=$8 || $3!=$9 || $4!=$10 {if($1=="@"){print $7,$8,$9,$10,$11,$12
}else{print $1,$2,$3,$4,$5,$6}...続きを読む

Qファイルの行数取得

超初心者です。

いま、表計算的なスクリプトを記述しています。

あるファイルの行数を取得する関数ってあるんでしょうか?

ファイルに記述されている数値を足したり引いたりするのですが、forを使っての計算の際にファイルの行数が必要となりました。

Aベストアンサー

Perlにですね。ないはずです。
行数とはファイルに書かれた改行文字の個数ということなので
実際にファイルを全て読み込まないと行数はわかりません。
以下のように色々な方法があると思います

#### 単純な例
$a = 0;
open FD, "<file.txt" || die $!;
while (<FD>) {
$a++;
}
close FD;
print "行数:$a\n";

### 少しマニアックな方法
open FD, "<file.txt" || die $!;
@a = <FD>;
close FD;
print "行数:" . ($#a + 1) + "\n";


### 反則的方法(外部コマンド) ... UNIXの場合
print "行数:" . `wc -l file.txt` . "\n";

QPerlで変数を使用した置換ができない

ファイル内の「1 2 3 4 5」などの数字の箇所を、「1回 2回 3回 4回 5回」のように、「回」をつけた形に変えることを考えています。
以下のようなスクリプトを書きましたが、うまくいきません。

# 「$search_replace」の参照先のテキストファイルの内容
(\d) タブ記号 $1回


EOF

# 問題のスクリプト(一部)
chomp($search_replace);
(my $search, my $replace) = split(/\t/, $search_replace);
$string[$i+1] =~ s/$search/$replace/g; #ここが問題と思われる箇所

ほしい結果は右のとおり。「1回 2回 3回 4回 5回」
現実の結果は右のとおり。「$1回 $1回 $1回 $1回 $1回」

置換文字列の$1の部分がうまく展開(?)できないようです。
どうすればよいか、ご教示ください。

Aベストアンサー

正規表現の置換部はダブルクォート文字列と同様に評価されるので、次の2つの文は同じように機能します。質問のケースでは、変数を2重に置き換えなければならないので、明示的にダブルクォートが必要になります。

$string[$i+1] =~ s/$search/$1回/g;
$string[$i+1] =~ s/$search/"$1回"/eg;

QPerlでファイルを読み込み重複データの検索

お世話になります。
次のようなテキストファイルを読み込んで重複を探し、表示したいのですが、Perlで、どのようなコードを書けば良いのでしょう。

尚、重複チェックは2通りあります。
パターン1 例えば1,2が2回以上出現する
パターン2 1,2と2,1や2,3と3,2は重複とみなす。

テキストファイル
1,2
1,3
1,4
1,5
2,1
2,3
2,4
2,5
3,1
3,2
1,2

チェック後の表示
1,2,重複
1,3,重複
1,4,
1,5,
2,1,重複
2,3,重複
2,4,
2,5,
3,1,重複
3,2,重複
1,2,重複

宜しくお願いします。

Aベストアンサー

解説:
while(<DATA>){#実際は、ファイルハンドル(か<>)になるべき所、DATAにしておくと、__END__から読み込める、この場合は、別にテキストファイルを作るのがめんどくさかったからと、これだけで実行(追試)ができるという意味
chomp;#末尾の改行を切り捨てる、引数が無い時は$_現在の読み込み行に対して実行される
$data[$i++]=$_;#現在の行を、あとで元データとして利用するために配列に入れておく
$test{$_}++;#既にあったかどうかテストするために行データをキーにハッシュにしておく、値は、出現回数
($a, $b)=split ",";#今読み込んでる行を「,」で分離する
$test{"$b,$a"}++;#逆順の並びでハッシュに登録
}
foreach $x (@data){#元のデータを順に取り出す
print "$x";#オリジナルの表示
print ",重複" if($test{$x}>1);#ハッシュで、2回以上カウントされていたら重複しているって表示する
print "\n";#改行をつけて一行分処理終わり
}
__END__

解説:
while(<DATA>){#実際は、ファイルハンドル(か<>)になるべき所、DATAにしておくと、__END__から読み込める、この場合は、別にテキストファイルを作るのがめんどくさかったからと、これだけで実行(追試)ができるという意味
chomp;#末尾の改行を切り捨てる、引数が無い時は$_現在の読み込み行に対して実行される
$data[$i++]=$_;#現在の行を、あとで元データとして利用するために配列に入れておく
$test{$_}++;#既にあったかどうかテストするために行データをキーにハッシュにしてお...続きを読む

QPerlでファイルの列を削除する。

どのように書けばいいかわからないので教えてください。
","で区切られたcsvファイルがあるとします。(file.csv)
そのファイルは全部で5列ありますが、4列目と5列目は値が入っている場合と無い場合があります。(1~3列目は必ず値は存在します。)
このファイルの1列目と2列目を削除して、3列目から5列目だけのファイルを作成したいのですが、どうすればいいのでしょうか?

よろしくお願いします。

Aベストアンサー

$line='a,b,c,d,,f';
($first, $second, $rest) = split(/,/,$line,3);
print $rest;
みたいにすれば、3列目以降を$restにとりだせます。
3列目以降に値が入っていない列があっても大丈夫です。


このQ&Aを見た人がよく見るQ&A

人気Q&Aランキング