• ベストアンサー
※ ChatGPTを利用し、要約された質問です(原文:Perl5で同時刻のデータを統合したい)

Perl5で同時刻のデータを統合する方法

N60-BASICの回答

  • ベストアンサー
  • N60-BASIC
  • ベストアンサー率80% (17/21)
回答No.3

use strict; use warnings; # 時刻をキーとする出力用ハッシュ my %data; open(my $fh1, '<', 'test1.txt') or die($!); while(<$fh1>) { chomp; my($time, $data1, $data2) = split /,\s*/; # 配列のリファレンスを生成し、$timeをキーとするハッシュに値として代入する $data{$time} = [$data1, $data2, "-999.0", "-999.00"]; } close($fh1); open(my $fh2, '<', 'test2.txt') or die($!); while(<$fh2>) { chomp; my($time, $data3, $data4) = split /,\s*/; if(exists $data{$time}) { # ハッシュのキーがすでに存在する = test1.txtに同じ時刻の値があった場合 # 配列のリファレンスを参照して配列の値を一部上書きする $data{$time}->[2] = $data3; $data{$time}->[3] = $data4; } else { # ハッシュのキーが存在しない = test1.txtに同じ時刻の値が無かった場合 # 配列のリファレンスを生成し、$timeをキーとするハッシュに値として代入する $data{$time} = ["-999.0", "-999.0", $data3, $data4]; } } close($fh2); open(my $fh_out, '>', 'test.txt') or die($!); # ハッシュのキー(=時刻文字列)をソートしてループを回す foreach my $time (sort keys %data) { # 配列のリファレンスを配列に戻したものを時刻と一緒にカンマ区切りでくっつける print $fh_out join(', ', $time, @{$data{$time}})."\n"; } close($fh_out); exit; =comment ・Perlで「値の重複チェック」をするなら、まず最初にハッシュを使うアルゴリズムを検討します。 ハッシュとは、(誤解を恐れずに言うのなら)任意の文字列を添字に持つことが出来る配列変数のようなものです。 同一のキーがすでに存在するかどうかを高速に(ほぼ一瞬で)調べることができるという利点があります。 ただし、キー(とペアになる値)の順序情報はハッシュ単体では持っていませんので、 データの順序関係が必要になる場合は、(今回の回答サンプルのように)キーでソートしたり、 別途ソート用の配列を作るなどの工夫が必要です。 ・ハッシュは配列と同様に、1つのキー(添字)につき1つの値しか持つことができませんが、 リファレンスという仕組みを使うことで「配列のハッシュ」や「配列の配列」などを実現できます。 サンプルではハッシュの値として配列データを持つ「配列のハッシュ」を作りました。詳細は検索してみてください。 ・今回のサンプルでは、元データのデータ検証や出力フォーマットの再整形が不要に見えましたので 簡略化のために全て文字列データとして扱っています。 再整形の必要があるならprintfでどうぞ。 ・時刻文字列(hh:mm:ss)は秒単位での一致確認をする限りにおいては分割の必要がありません。 桁ごとに数値比較するよりは、まとめて文字列比較したほうが簡単で速いです。 例として、分単位でまとめる場合には、時刻をばらして「hh:mm」をキーとするハッシュを作ることになるでしょう。 ・利用する変数を必ずmyで宣言するなど、(Perl4ではなく)Perl5の文法で書くことをお勧めします。 use strict; use warnings; と組み合わせれば、変数名の間違いや初期化忘れに対して警告やエラーを出してくれます。 一番厄介な「エラーの出ない不具合」をある程度未然に防ぐことができます。 質問者さんのサンプルで言うと、$mや$nを0で初期化せずに使っている部分などが警告対象となります。 ・詳細は検索してみてください。 参考になるキーワード:「Perl ハッシュ」「Perl 変数 リファレンス」「Perl5 use strict」など。 =cut

Lydiard
質問者

お礼

ご回答ありがとうございました。 お礼が遅くなり申し訳ありませんでした。 N60-BASICさまのプログラムを参考にしてして テストデータより複雑な本データの処理が無事にできました。 >・利用する変数を必ずmyで宣言するなど、(Perl4ではなく)Perl5の文法で書くことをお勧めします。 >use strict; use warnings; と組み合わせれば、変数名の間違いや初期化忘れに対して警告やエラーを出してくれます。 はい、C言語などでしたらコンパイルエラーですね… 「動くから」ではなく今後気をつけてプログラムを作成していきたいと思います。 丁寧な解説や参考キーワードなどを教えていただき本当にありがとうございました。

関連するQ&A

  • Perlでの Timeの足し算

    時間の足し算をやりたい。訳あってライブラリが使えません。 方法は無いでしょうか? 仮にライブラリが使えるなら、下記のようでうまく行っています。 --------------------------------------------------- use Time::Local; $year=2008; $mon =10; $mday=24; $hours = 23; $min = 0; $sec = 0; $time = timelocal($sec, $min, $hours, $mday, $mon - 1, $year); $a1=($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) = localtime($time ); $mytimeORG= sprintf("%04d/%02d/%02d %2d:%2d", $year + 1900, $mon +1, $mday,$hour,$min); print "$mytimeORG\n"; ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) = localtime($time + 32400); #9H加算 $mytime9H= sprintf("%04d/%02d/%02d %02d:%02d", $year + 1900, $mon +1, $mday,$hour,$min); print "$mytime9H\n"; ---------------------------------------------

    • ベストアンサー
    • Perl
  • PerlからPHPへ移行 Part2

    以下、Perlのソースですが、これをコメントどおりに PHPに直すとどうなりますか? Time::Localは ppm install Time-Localでインストールできます。 use strict; BEGIN{ # BEGINはPHPで $|=1; # バッファリングしない } use Time::Local; #PHPでUseは? sub test_date_time{ my ($year, $mon, $mday, $hours, $min, $sec) = @_; # 可変引数な方法 my $serial = timelocal($sec, $min, $hours, $mday, $mon - 1, $year); my $moji = sprintf("$year年$mon月$mday日 $hours時$min分$sec秒"); return ($serial, $moji); # 複数の戻り値で様々な型で返す方法 } my ($serial, $moji) = test_date_time(2006, 10, 2, 10, 25, 30); # 一度に受け取る方法 print q{シリアルは} . $serial . "\n"; #q print qq{文字列は$moji\n}; my $ptn = qr{^(.+?)年(.+?)月(.+?)日\s(.+?)時(.+?)分(.+?)秒$}; if($moji =~ /$ptn/i){ print "$1/$2/$3 $4:$5:$6\n"; } exit(0); # 結果 #シリアルは1159752330 #文字列は2006年10月2日 10時25分30秒 #2006/10/2 10:25:30

    • ベストアンサー
    • PHP
  • perlの構文がおかしいようです。

    構文は以下の通りです。IF文のところがおかしいようですが、どこに間違いがあるのか教えていただけないでしょうか。 sub ippack { my ($a, $b, $c, $d) = @_; return ($a << 24) | ($b << 16) | ($c << 8) | $d; } sub ipunpack() { my ($n) = @_; return (($n>>24)&0xFF,($n>>16)&0xFF,($n>>8)&0xFF,$n&0xFF); } open (IN,"<useIP.txt") while(my $input = <IN>) { if ($input =~ /([0-9]+)\.([0-9]+)\.([0-9]+)\.([0-9]+) ([0-9]+)\.([0-9]+)\.([0-9]+)\.([0-9]+)/){; my $n = &ippack($1,$2,$3,$4); my $m = &ippack($5,$6,$7,$8); $n = $n & $m; $m = ~$m & 0xFFFFFFFF; for(my $i = 0; $i <= $m; $i++) { print join(".", &ipunpack($n+$i))."\n "; } } if-else($input =~ /([0-9]+)\.([0-9]+)\.([0-9]+)\.([0-9]+)/);{ print "$input\n"; } print "\n"; }

    • ベストアンサー
    • Perl
  • パッケージを利用した際の値に謎が!!

    Perlのパッケージについて質問させてもらいます。 Time::Localを利用して1970年1月1日からの経過秒数を取得しようと考えています。 次の様な記述をして実験したのですが、私が想定している通りの動作になりません。 記述(テスト1) (以下からスクリプト開始) use Time::Local; $year=1970; $mon =1; $mday=1; $hours = 10; $min = 0; $sec = 0; $time = timelocal($sec, $min, $hours, $mday, $mon - 1, $year); print $time; (この1行上の行でスクリプト終了) この場合の$timeは、"3600"という答えを得ました。よって、"0"にするために次の様に スクリプトを書き変えて実験しました。 記述(テスト2) (以下からスクリプト開始) use Time::Local; $year=1970; $mon =1; $mday=1; $hours = 9; $min = 0; $sec = 0; $time = timelocal($sec, $min, $hours, $mday, $mon - 1, $year); print $time; (この1行上の行でスクリプト終了) この場合の$timeは、"-3600"という答えを得ました。 なんか変ですね。"0"の答えを得るためにはどうすればよいのでしょうか? よろしくお願いします。

  • 大量のファイルを読み込み、その各ファイルの中の最大値と最小値の出力の仕方

    各ファイルの名前はinput_0.txtからinput_4.txtまであるとします。これらのファイルには(1)ナンバー(2)身長(3)体重がスペースをはさんで入力されています。 例:input_0.txt 1 172.3 65.3 2 164.3 54.6 3 176.4 55.4 4 170.2 70.4 5 167.4 63.8 この例では3番目の176.4が最大値として認識し2番目の164.3を最小値として出力させたいのですが、うまくいきません。プログラムを以下のように作りました。どこがいけないでしょうか?ご教授の方よろしくお願いします。 FILE *fpr,*fpw; int no[N],i,j,max_j,min_j; char fname[30]; float height[N],weight[N],max=0,min=0; for(i=0;i<4;i++) //file題名用ループ { sprintf(fname,"input_%d.txt",i); if((fpr=fopen(fname,"r"))==NULL) {puts("file open error!!");return 0;} for(j=0;j<5;j++){ //ファイル内容用ループ while((fscanf(fpr,"%d%f%f",&no[j],&height[j],&weight[j]))!=EOF) if(height[j]>max){ max=height[j];max_j=j; printf("number=%d__height=%.2f__weight=%.2f\n",no[max_j],max,weight[max_j]); } if(height[j]<min){ min=height[j];min_j=j;} printf("number=%d__height=%.2f__weight=%.2f\n",no[min_j],min,weight[min_j]); } fclose(fpr); }

  • 時刻取得について

    C言語において時刻取得を以下のようにプログラムしたら、プログラムを3回目以上実行すると前回時間内容が常に1回目が表示されてしまいます。どこを直せばちゃんと前回の時刻が正しく表示されるようになるのでしょうか。put()のfopenの"a"の部分は"w"に変えるだけで正しく実行されるのですが、これを"a"にした場合について正しく表示されるようにするということです。 #include <time.h> #include <stdio.h> void get(void); void put(void); char data_file[] = "time.dat"; int main(void) { get(); put(); return(0); } /* 前回の日付・時刻を読込む   */ void get(void) { FILE *fp; time_t t; struct tm *local; time(&t); local = localtime(&t); if ((fp = fopen(data_file, "r")) == NULL){ printf("\a本プログラムを実行するのは初めて\n"); }else { int year, month, day, h, m, s; fscanf(fp, "%d %d %d", &h, &m, &s); printf("前回は%d時%d分%d秒\n", h, m, s); fclose(fp); } } /* 今回の日付・時刻を書き込む   */ void put(void) { FILE *fp; time_t t; struct tm *local; time(&t); local = localtime(&t); if ((fp = fopen(data_file, "a")) == NULL) printf("\aファイルをオープンできません。\n"); else { printf("現在の日付・時刻を書き出しました。\n"); fprintf(fp, "%d %d %d\n", local->tm_hour, local->tm_min, local->tm_sec ); fclose(fp); } }

  • 2つの日付・時間の差分を求めるには

    2つの日付、時間の差分を求めるには、 timelocal を使うのかと思うのですが、 $time1 = timelocal($sec1,$min1,$hours1,$day1,$month1,$year1); $time2 = timelocal($sec2,$min2,$hours2,$day2,$month2,$year2); $result = int(($time2 - $time1)/(60*60*24)); return $result; ではいけないのでしょうか?

    • ベストアンサー
    • Perl
  • 因数分解プログラム(C言語)について(3)

    つづきです /*求めた最大公約数で約分*/ if(*flag == 1){ *d = *m1 / *i; *e = *n1 / *i; } else{ printf("約分できません。\n"); *d = *m1; *e = *n1; } return 0; } int yakubun2(int *m2,int *n2,int *min2,int *flag,int *i,int *f,int *g) { /*最大公約数を見つける*/ if(*m2 < *n2){ *min2 = *m2; } else{ *min2 = *n2; } *flag = 0; for(*i = min2; *i > 0; *i--){ if(*m2 % *i == 0){ if(*n2 % *i == 0){ *flag = 1; break; } } } /*求めた最大公約数で約分*/ if(*flag == 1){ *f = *m2 / *i; *g = *n2 / *i; } else{ printf("約分できません。\n"); *f = *m2; *g = *n2; } return 0; } /*因数分解の結果を表示*/ int output(int *d,int *e,int *f,int *g) { printf("(%dχ-%d)(%dχ-%d)",*d,*e,*f,*g); return 0; } 関連URL:http://www.okweb.ne.jp/kotaeru.php3?q=474597

  • 1秒未満間隔のファイル更新について

    皆様 下記のようなプログラム(Perl)で、1秒未満のファイル更新を試みております。sleep(1)として、待ち時間を1秒とすると、data.txtに時刻が更新されますが、sleep(0.8)、sleep(0.5)等として待ち時間を1秒未満にすると、プログラムは動作するのですが、data.txtはファイルとして作成されません。 これは、printf()関数のバッファリング、及びフラッシュに関する仕様に起因するものと思われますが、1秒未満の間隔でdata.txtを更新して時刻をdata.txtに書き込む処理は可能でしょうか? #強制フラッシュの"$| = 1;"を使い、sleep(0.8)としても駄目でした。 すみませんが、よろしくお願いします。 (コード) #use Time::HiRes; #$| = 1; while(){ ($sec, $min, $hour, $day, $mon, $year) = localtime(time); open(OUT, "> data.txt"); printf(OUT "%2s:%2s:%2s\n", $hour, $min, $sec); close(OUT); sleep(1); }

  • Bシェルで、1文字づつ読み込む方法を教えてください。(バイナリ)

    Bシェルで、1文字づつ読み込む方法を教えてください。(バイナリ) 以前、下記の質問をして、回答を頂きました。 この中の入力ファイル『test.txt』がバイナリ形式だった場合は、 どのようにしたら良いでしょうか。 ### 質問 ############################################ 次のように文字が並んでいて、 A B C D E F G H I J K L M N O P それぞれの番地と共に、上記の文字を出力する場合、 どのようにしたら宜しいでしょうか。 入力ファイル:test.txt A B C D E F G H I J K L M N O P ↓ 1 2 3 4 1 A B C D 2 E F G H 3 I J K L 4 M N O P ↓ 出力ファイル A 1 1 B 2 1 C 3 1 D 4 1 E 1 2 F 2 2 G 3 2 H 4 2 I 1 3 J 2 3 K 3 3 L 4 3 M 1 4 N 2 4 O 3 4 P 4 4 ### 回答 ############################################ yy=1 while read line; do xx=1 set -f $line while [ $# -gt 0 ]; do echo $1 $xx $yy shift xx=`expr $xx + 1` done yy=`expr $yy + 1` done < test.txt ##################################################### どうぞ宜しくお願い致します。