- 締切済み
配列の操作とログの書き込み方法について
配列の操作とログの書き込み方法について悩んでいます。まず、 @array=(A,B,C)とした場合、 <FORM method="POST" action="AAA.cgi"> 必要個数<select name="kosu"> <option>1 <option>2 ~~~~ <option>100 必要個数($kosu)分だけログに書き込む為にはどうしたらよいのでしょうか? 理想としては、以下のような形でログへ入力できるようにしたいのですが、なかなかうまく動いてくれません。 説明が下手で大変申し訳ありませんが、どうかご教授お願いします。 $kosu=10であった場合 10<>A<>B<>C\n 9<>A<>B<>C\n 8<>A<>B<>C\n ~~~~~~~ 1<>A<>B<>C\n
- みんなの回答 (7)
- 専門家の回答
みんなの回答
- Dpop
- ベストアンサー率51% (279/544)
Dpop です。 > 個人的に色々相談したいですが、利用規程上無理なのが残念です。 そうです。OkWebの規約なので致し方ありません。 僕は、アンサリストなので、明確に規約を破るのはまずいかな。やっぱり。なんて思っています。 グレーゾーンな事はやっていますけど(^_^;;) > 今回教えてもらったスクリプトの稼動条件は、 > perl5以上、モジュールはcgi.pmとJcode.pmがインストールされていることが > 条件ですが、 > 設置するサーバーの環境は、perl5ではあったのですが、 > Jcode.pmがインストールされていない環境でした。 たまに見かけるパターンです。 Jcode.pm について、明記するのを忘れていました。 CGI を提供する時には、明記する様に心がけているのですが、今回は忘れました。 申し訳ありません。 Jcode.pm は、 http://openlab.jp/Jcode/Jcode.pm にありますので、ダウンロードしてこの CGI と同じディレクトリへ格納してください。 use でも、スクリプトがあるディレクトリを探索してくれるので、動作には支障がありません。 jcode.pl は、既に古いライブラリなので、使用するのは辞めましょう。 どうしても、jcode.pl を使用するのであれば、 use Jcode; を require './jcode.pl'; とし、 &Jcode::convert(); を &jcode::convert(); とすれば動くと思います。が、Jcode.pm をホームに置いて挙げれば良い事なので、Jcode.pm を使用する事をお奨めします。 (本当は、Jcode.pm も既に使用を推奨されているパッケージでは無いのですが(^_^;;) 今は、Encode.pm を利用する事が推奨されています。僕は、未だに Jcode.pm を愛用しています(^_^;;;) 詳しくは、 http://openlab.jp/Jcode/index-j.html をご覧下さい。(Jcode.pm の場所も、このページから引用しています。)) > また、更に質問なのですが、データ入力した際の出力結果最画面下部に、 > 最初のレコードと最後のレコードが表示されていますが、 > 下記の用に変更する場合はこの部分を変更すればよろしいのでしょうか > 211行目 %data_1 = %{$data[0]}; > 212行目 %data_n = %{$data[-1]}; 大雑把に言えば、その通りです。 でも、この様な取得の方法では、更新後のデータファイルに対する操作しかできず、 今回入力したデータに対する操作ができません。 そのため、今回入力したデータをデータへ結合する直前で、 今回振る番号の最初の番号を取得しておいて、それを利用する様にします。 今回、取得するデータを増やしたので、 $data_1{}, $data_n{} を廃止して、$data_x[]{} と言う形式に統合しました。 $data_x[0]{} が $data_n の事で、$data_x[2]{} が $data_1 の事です。 $data_x[1]{} には、今回入力したデータを持たせてあります。 > 年度表示をするという事は可能でしょうか? $data_x[]{'hizuke'} から、年度を求める処理を追加して置きました。 $data_x[]{'nondo'} に、年度を格納しています。 > レコード表示の場合()(かっこ)はつけない予定です。 > 【実装例】 > あなたが取得した個数は$basyo(04)No.5~$basyo(04)No.9です。 ちょっとレイアウトは違いますが、この様な感じに表示する様にしました。 > 何度も何度も質問してしまって大変申し訳ありません。 > 教えていただいて本当に感謝しています。 > 大変参考になります。有難うございます。 それは全く問題ありません。好きでやっている事ですから(笑) 回答の文章です: http://www.dpop.tk/okweb/1245178/1245178_2.txt CGIです: http://www.dpop.tk/okweb/1245178/index_2.cgi CGIをテキスト化した物です: http://www.dpop.tk/okweb/1245178/index_2.txt 上記サイトで実行可能です。適当に触って構いませんよ。 何度でもテストできる様に、データファイルを削除するためのボタンを付けて置きました。 不明点があれば、再度質問してください。 スクリプト中で、分からない表現があったら徹底的に質問して、 自分の物にしてくださいね。 その方が僕も張り合いがでます。
- Dpop
- ベストアンサー率51% (279/544)
Dpop です。 最近、OkWeb重いですね(^^;) まともに投稿が受け付けられない。 ログファイルの容量は、あまり大きくなりそうにありませんね。 300KB程度であれば、メモリーに持ってしまっても問題無いでしょう。 致し方無い事ではあると思うのですが。。 > 以前、PHPとMySQLで作成しようと考えたのですが、 MySQL とバークレーDBとは、全く無関係です。 MySQL は、RDBの一種です。バークレーDBは、ハッシュを効率の良い形式でファイル化した物と考えると分かりやすいと思います。 バークレーDBが利用可能かどうか、簡単なソースを流せば分かるとは思いますが、とりあえず目的の物を仕上げてしまいましょう。 > サーバーの設定はperl5しかありませんでした。 Perl が動作すればバークレーDBは使えます。 GDBM は無くても、SDBM は使えると思います。 今回、データ容量もさほど大きく無いので、考える必要はないかも知れません。 もし、データファイルの読み込みに時間が係るな。。。 と思える様に成ってきたら、バークレーDBに乗り換える。と言う手もあるでしょう。 > >・同時に複数のブラウザから頻繁にアクセスされる可能性はありますか? > 可能性はあります。 > 同時アクセスの際は、mkdirのファイルロックを考えています。 手元にある、ロックルーチンを組み込んで置きました。 ただし、このルーチンはあまり性能が良くありません。(特に、autounlock周り。) 適当なルーチンがあれば、置き換えて頂いた方が良いでしょう。 なかなか投稿できない見たいなので、こちらの回答を掲載して起きます。 回答の文章です: http://www.dpop.tk/okweb/1245178/1245178.txt CGIです: http://www.dpop.tk/okweb/1245178/index.cgi CGIをテキスト化した物です: http://www.dpop.tk/okweb/1245178/index.txt 不明点があれば、再度質問してください。
お礼
Dpop様 お礼が遅れて大変申し訳ありません。 すごいとしか言いようがありません。 大変有難うございます。 個人的に色々相談したいですが、利用規程上無理なのが残念です。 理想どおりの動きで感動しています。 が、 今回教えてもらったスクリプトの稼動条件は、 perl5以上、モジュールはcgi.pmとJcode.pmがインストールされていることが 条件ですが、 設置するサーバーの環境は、perl5ではあったのですが、 Jcode.pmがインストールされていない環境でした。 この場合、Jcode.pmファイルを用意して、 require './Jcode.pm'; とすればよろしいのでしょうか? また、jcode.plを代用する場合、 require './jcode.pl'; とし、 &Jcodeとなっている部分を変更すればよろしいのでしょうか? できれば前者の方が楽だと思うのですが、 実証する方法がわからないので推測で考えています。すいません。 また、更に質問なのですが、データ入力した際の出力結果最画面下部に、 最初のレコードと最後のレコードが表示されていますが、 下記の用に変更する場合はこの部分を変更すればよろしいのでしょうか 【レポート画面が以下の場合】 8<>2005/03/03<>氏名3<>場所3<>備考3<>5┐ 7<>2005/03/03<>氏名3<>場所3<>備考3<>5│ 6<>2005/03/03<>氏名3<>場所3<>備考3<>5├ここが今回取得した部分 5<>2005/03/03<>氏名3<>場所3<>備考3<>5│ 4<>2005/03/03<>氏名3<>場所3<>備考3<>5┘ 3<>2005/02/02<>氏名2<>場所2<>備考2<>1 2<>2005/01/01<>氏名1<>場所1<>備考1<>2 1<>2005/01/01<>氏名1<>場所1<>備考1<>2 あなたが取得した個数は$basyoNo.4~$basyoNo.8です。 【スクリプト変更部分】 211行目 %data_1 = %{$data[0]}; 212行目 %data_n = %{$data[-1]}; 【スクリプト変更部分】 さらに質問なのですが、このスクリプトに 年度表示をするという事は可能でしょうか? 具体的な例は以下の通りです。 年度設定 2004/04/01~2005/03/31の期間は、04年度 2005/04/01~2006/03/31の期間は、05年度 レコード表示の場合()(かっこ)はつけない予定です。 【実装例】 あなたが取得した個数は$basyo(04)No.5~$basyo(04)No.9です。 何度も何度も質問してしまって大変申し訳ありません。 教えていただいて本当に感謝しています。 大変参考になります。有難うございます。
- Dpop
- ベストアンサー率51% (279/544)
Dpop です。 > 教えていただいた方法は、ログに書き込む際に全て1~入力となっています。 > これを以下のように改造したいのですが、何かいい方法はありますでしょうか? > > 4<>日付3<>名前3<>場所3<>備考3<>個数3 ←3回めのログ書き込み > 3<>日付2<>名前2<>場所2<>備考2<>個数2 ←2回目のログ書き込み > 2<>日付<>名前<>場所<>備考<>個数 ←1回目のログ書き込み > 1<>日付<>名前<>場所<>備考<>個数 ←1回目のログ書き込み > > 何度も何度も聞いてしまって大変申し訳ありません。宜しくお願いします。 方法は幾つかあるとは思いますが、 単純な方法で行うか、より安全な方法で行うか、思案する所です。 一つの方法は、ログの内容を全て配列に読み込んでから、 配列操作で、番号の降順に並べて書き出して挙げる。 と言う方法です。 コードは簡単なのですが、ログの容量によっては現実的ではありません。 より現実的な方法としては、今回発生したログを一端ファイルに書き込んでから、 現行のログとマージする方法です。 ファイル操作で対応するので、ログの容量には依存しません。 ただし、ファイル操作を伴いので時間が係るのが難点です。 他のブラウザから実行される事を意識する必要があるので、 ロックをかけてから実行する必要があるでしょう。 もっと賢い方法もあります。DBを利用すればもっと確実にできます。 DBと言ってもRDBではなくて、バークレーDBを使います。 Perlの標準パッケージを使って簡単に作る事ができます。 ただし、サーバー側に GDBM などのシステムが必要ですが。。。 幾つか確認させてください。 ・ログファイルの容量はどの程度になると予測されていますか? ・バークレーDBは利用可能ですか? ・同時に複数のブラウザから頻繁にアクセスされる可能性はありますか? それによって、方針を立てていきましょう。 > 3,ログに書き込んだ番号の最初と最後の番号を画面に表示する。 これは、配列操作でもできますし、ファイル操作でも可能ですね。 > 4,指定のsubmitボタンを押すとエクセルが立ち上がり、指定のテンプレートファイルに書き込んだ数字部分を代入して自動印刷する。(マクロ使うぐらいしか想像できません。) この部分は、具体的に見えてきません。PerlからExcelのスプレットシートを作るためのパッケージがあるらしいのですが、 僕はまだ利用した事が無いので、実力のほどは良く分かりません。 「テンプレートファイルに書き込んで。」と言う部分がくせものですね。その後の印刷処理はExcelのマクロ処理の問題ですね。 CGI部分に関しては、配列操作だけで行って良いのか、ファイルを仲介させた方が良いのか。 より安全策として、バークレーDBを利用するか。 と言う選択肢になると思います。 回答をいただいてから、次の手を講じましょう。
お礼
Dpop様 親切な対応本当に本当に有難うございます! 本当に感謝しています。 また、質問していただいた件ですが、 >・ログファイルの容量はどの程度になると予測されていますか? 個数で見ると、年間1000~2000程度ぐらいです。 行単位でも同じであり、テキストの容量を調べたところ、 大体150KB~300KBをみれば十分な量だと考えられます。 >・バークレーDBは利用可能ですか? 残念ながら利用できません。 以前、PHPとMySQLで作成しようと考えたのですが、 サーバーの設定はperl5しかありませんでした。 その為、テキストファイルベースで作成するしか方法がないのです。 >・同時に複数のブラウザから頻繁にアクセスされる可能性はありますか? 可能性はあります。 同時アクセスの際は、mkdirのファイルロックを考えています。 色々考えていただき本当に感謝しています。 かなり勉強になり、大変ありがたいです。 宜しくお願いします。
- Dpop
- ベストアンサー率51% (279/544)
#1 です。 やはりそうでしたか。なんかおもしろい事をするな。と思っていました。 んー。であれば、 $s = join('<>', $i, @array); の行を少し工夫しましょう。 なお、このプログラムを僕は実行していません。理屈の上では間違っていませんが、旨く動作しなかった場合、その動作内容を極力明確にして頂けると助かります。 #!/perl/bin/perl use CGI; $cgi = CGI->new(); # 作成したCGIオブジェクトに対するメソッドを以下に記述していく $kosu = $cgi -> param("kosu"); $hizuke = $cgi -> param("hizuke"); $name = $cgi -> param("name"); $basyo = $cgi -> param("basyo"); $biko = $cgi -> param("biko"); ##入力情報のチェック【省略します】 ## ファイル出力処理 # ユーザファイルのパスを指定し、ファイルをファイル変数に設定する $datafile = "./user.txt"; @array= qw(hizuke name basyo biko kosu ); # ファイルオープン ※エラー時は右側のエラー処理を実施する open(LOG, ">$datafile") || &error("open error: $datafile"); for ($i = $kosu; $i > 0; $i--) { @s = (); push(@s, $i); foreach (@array) { push(@s, ${$main::{$_}}); } $s = join('<>', @s); print LOG $s. "\n"; } close(LOG); ## 書込み完了のメッセージ出力 # HTMLの出力(head部)【省略します】 # HTMLの出力(body部) print <<"HTML"; 取得日:$hizuke<br> 名前:$name<br> 場所:$basyo<br> 備考:$biko<br> 取得個数:$kosu HTML # 当プログラムの終了 exit; ## エラー処理 sub error { 【エラー処理は省略します。】 exit; } とでもしましょうか。 ${$main::{$_}} は、package main の中のスカラー変数を示す記述です。 $_ = 'abc'; print ${$main::{$_}}; は、 print $abc; と、実質的に同じ事を指しています。 この様にすると、文字列として持った変数名を、変数の実態名として利用する事ができます。 ただし。この指定には大きな制約が伴います。他のpackageの中からは参照できません。 この辺りの理屈は、Perlの「空間」の概念が分かっていないと難しいので、実はビギナーの方にはあまりお奨めではありません。 他の方法としては、 #!/perl/bin/perl use CGI; $cgi = CGI->new(); # 作成したCGIオブジェクトに対するメソッドを以下に記述していく $in{'kosu'} = $cgi -> param("kosu"); $in{'hizuke'} = $cgi -> param("hizuke"); $in{'name'} = $cgi -> param("name"); $in{'basyo'} = $cgi -> param("basyo"); $in{'biko'} = $cgi -> param("biko"); ##入力情報のチェック【省略します】 ## ファイル出力処理 # ユーザファイルのパスを指定し、ファイルをファイル変数に設定する $datafile = "./user.txt"; @array= qw(hizuke name basyo biko kosu ); # ファイルオープン ※エラー時は右側のエラー処理を実施する open(LOG, ">$datafile") || &error("open error: $datafile"); for ($i = $in{'kosu'}; $i > 0; $i--) { @s = (); push(@s, $i); foreach (@array) { push(@s, $in{$_}); } $s = join('<>', @s); print LOG $s. "\n"; } close(LOG); ## 書込み完了のメッセージ出力 # HTMLの出力(head部)【省略します】 # HTMLの出力(body部) print <<"HTML"; 取得日:$hizuke<br> 名前:$name<br> 場所:$basyo<br> 備考:$biko<br> 取得個数:$kosu HTML # 当プログラムの終了 exit; ## エラー処理 sub error { 【エラー処理は省略します。】 exit; } の様に、パラメータをハッシュで受けて頂いて、ハッシュキーを配列として持つ。と言う方法の方が無難であり、寄り推奨できる方法です。 ただ。。。 まだ、疑問を覚える点があります。 現在の作りだと、10の行も、9の行も、同じ内容の$hizuke, $name・・・ が設定されます。 そう言う意図であれば、それで良いのですが。 不明点があれば、再度質問してください。
お礼
Dpop様、とてもわかりやすい解説付きで回答して頂き、大変感謝しています。教えていただいたうち、ハッシュキーを配列として持つ方を使ってみました。結果としては、問題なくログに書き込みができました。ありがとうございます。 このプログラムの全体像としては、以下の様な形を目標に作成しています。もし、助言していただける部分がありましたら教えていただけないでしょうか? 1,登録(日付、名前、登録場所、備考、必要個数) 2,必要個数によってログに書き込み(Dpop様に教えてもらった所です。*1) 3,ログに書き込んだ番号の最初と最後の番号を画面に表示する。 ======== 4,指定のsubmitボタンを押すとエクセルが立ち上がり、指定のテンプレートファイルに書き込んだ数字部分を代入して自動印刷する。(マクロ使うぐらいしか想像できません。) *1についてDpop様に教えていただいた方法はかなり参考になり、今後とてもいい勉強になりました。が、 教えていただいた方法は、ログに書き込む際に全て1~入力となっています。 これを以下のように改造したいのですが、何かいい方法はありますでしょうか? 4<>日付3<>名前3<>場所3<>備考3<>個数3 ←3回めのログ書き込み 3<>日付2<>名前2<>場所2<>備考2<>個数2 ←2回目のログ書き込み 2<>日付<>名前<>場所<>備考<>個数 ←1回目のログ書き込み 1<>日付<>名前<>場所<>備考<>個数 ←1回目のログ書き込み 何度も何度も聞いてしまって大変申し訳ありません。宜しくお願いします。
- Dpop
- ベストアンサー率51% (279/544)
#1 です。 それから、 open(LOG, ">$datafile") || die "open error"; の、die は CGI では都合が悪いので(#1 を書いた時点ではエラー処理が不明だったので、die で逃げました。) open(LOG, ">$datafile") || &error("open error: $datafile"); とでもして頂いて、error 側でパラメータの内容を表示するなりして下さい。 不明点があれば、再度質問してください。
お礼
Dpop様。ログに書き込むことができました! しかし、書き込まれた内容を見ると、 100<>hizuke<>name<>basyo<>biko<>kosu ~~~~~~~~~~~ 1<>hizuke<>name<>basyo<>biko<>kosu となっていました。解析したところ、以下の部分がそのまま書き込まれている事がわかりました。 @array= qw(hizuke name basyo biko kosu); 実は、<form>から与えられた値をここに代入して書き込みをしたかったのですが、なにぶん自分の力不足を痛感している為、うまい解決策が出てきません。またご教授いただけないでしょうか? 宜しくお願いします。
- Dpop
- ベストアンサー率51% (279/544)
#1 です。 for ($i = $in{'kosu'}; $i > 0; $i--) { は、 for ($i = $kosu; $i > 0; $i--) { では無いでしょうか? $kosu の内容が0なので、forループは一度も廻らずに抜けてしまいます。 不明点があれば、再度質問してください。
- Dpop
- ベストアンサー率51% (279/544)
パラメータ kosu は、CGI.pm を使って取得する物とします。 また、@array は定数定義されている物とします。 #/usr/local/bin/perl use strict; use CGI; my($cgi, %in, @array, $i, $s); $cgi = new CGI; $in{'kosu'} = $cgi->param('kosu'); @array= qw(A B C); open(LOG, ">logfile.txt") || die "open error"; for ($i = $in{'kosu'}; $i > 0; $i--) { $s = join('<>', $i, @array); print LOG $s. "\n"; } close(LOG); __END__ こんな感じかな。 不明点があれば、再度質問してください。
お礼
Dpop様、すばやいご教授有難うございます。 教えていただいたソースは大変参考になりました、、が、 自分のソースに書き加えてもうまく動いてくれません。 formから受け取ったデータをブラウザに表示することはできるのですが、ログに書き込まれないのです。 何か致命的な間違い等あるのでしょうか? 朝からずーっと調べているのですがわかりません。 大変申し訳ないのですが、以下ソースのどこがいけないのか教えていただけないでしょうか? 宜しくお願い致します。 #!/perl/bin/perl use CGI; $cgi = CGI->new(); # 作成したCGIオブジェクトに対するメソッドを以下に記述していく $kosu = $cgi -> param("kosu"); $hizuke = $cgi -> param("hizuke"); $name = $cgi -> param("name"); $basyo = $cgi -> param("basyo"); $biko = $cgi -> param("biko"); ##入力情報のチェック【省略します】 ## ファイル出力処理 # ユーザファイルのパスを指定し、ファイルをファイル変数に設定する $datafile = "./user.txt"; @array= qw(hizuke name basyo biko kosu ); # ファイルオープン ※エラー時は右側のエラー処理を実施する open(LOG, ">$datafile") || die "open error"; for ($i = $in{'kosu'}; $i > 0; $i--) { $s = join('<>', $i, @array); print LOG $s. "\n"; } close(LOG); ## 書込み完了のメッセージ出力 # HTMLの出力(head部)【省略します】 # HTMLの出力(body部) print <<"HTML"; 取得日:$hizuke<br> 名前:$name<br> 場所:$basyo<br> 備考:$biko<br> 取得個数:$kosu HTML # 当プログラムの終了 exit; ## エラー処理 sub error { 【エラー処理は省略します。】 exit; }
お礼
Dpop様 お礼が遅れて大変申し訳ありません。 スクリプトを修正して教えて頂いて本当に感謝しています。 パスを指定してサーバーで動作確認した所、 デフォルトではなぜか動かないのです。 色々変更してHTML出力部分は表示されるようになったのですが、決定ボタンを押すと「エラーが発生しました。」となってしまい、うまく動いてくれません。 テスト環境では以下のフォルダ構成で調べてみました。 test ├index_2.cgi ├Jcode.pm(jcode.plを使う場合は使用しませんでした。) ├index_2.cgi.lock※1 ├index_2.lock※2 ├user_2.txt └jcode.pl(Jcode.pmを使う場合は使用しませんでした。) ※ロックファイルの名前なのですが、 $lockfile = $CGIname. ".lock";と指定した場合、 ※1、2のどちらが正しいのでしょうか? HTML部分が出力できたのは、useをrequire './jcode.pl';とした場合のみでしたが、採番は「エラーが発生しました」となり、行えませんでした。ちなみに、ファイル削除ボタンを押しても同じでした。 何かサーバーに致命的な原因があるのでしょうか? 大変申し訳ありませんがまたご教授お願いできませんでしょうか? 自分の技術不足を痛感しています・・