• ベストアンサー

DBMとテキストファイルのどちらが良いと思われますか?

こんにちは、 windows + ActivePerl の環境で、 簡単なデータを記録して後から検索、修正、削除したいのですが、 データを格納するのに、テキストファイルにするか、DBMを使用するか 悩んでいます。 テキストファイルに、(行番号,"*" x 100)のレコードを10万行出力したら、約10MBで、 DBMファイルに (行番号=>"*" x 100)のハッシュを出力したpagファイルは510MBでした。 その後、99999番目のレコードを以下のスクリプトで表示したところ、 体感的には違いがありませんでした。(どちらも一瞬で終了します) ******************************* #テキストファイル 検索 my $id = 99999; my $data; open(FILE,"<TEXTfile.txt") or die ; while(my $line = <FILE>){ if($line =~ /$id/){ $data = $line; last; } close FILE; print $data; ******************************* #DBMファイル 検索 my %DBM = (); my $id = 99999; my $data; dbmopen(%DBM,"DBMfile",0666) or die; if (defined $DBM{$id}){ $data = $DBM{$id}; } dbmclose %DBM; print $data; DBMを使用した場合、すべてのデータをメモリ上に展開するのでしょうか? もしそうなら、510MBも消費されてはたまりませんので、テキストファイルになりますが、 コーディング等はDBMの方が楽かな?と考えています。 データベースを利用できない環境の場合、どちらが良いのでしょうか? ご意見お願いします。

  • g_p_
  • お礼率78% (26/33)
  • Perl
  • 回答数2
  • ありがとう数2

質問者が選んだベストアンサー

  • ベストアンサー
  • guci-ok
  • ベストアンサー率33% (49/146)
回答No.2

今晩は。 この話題では、様々な論点があるので、狭い紙面では難しいこともありますが、 幾つか書かせてもらおうと思います。 まず、dbmopenと書いてありますが、これは少し古い書き方だと思います。 最近の書籍などでは別の書き方をしていると思いますので当たってみて下さい。 次に10MBくらいでは、余り時間はかからないかもしれませんが、もう少しサイズ が大きくなるといくらか時間がかかるようになります。 テキストファイルではseek関数によってポインターを何処にでも移動できますから、 たとえば固定長のレコードの場合は、レコード長xレコード番号までseekすること によって即座に目的のレコードを読むことができます。 DBMを使うのは、キー(文字列)でダイレクトにレコードを操作できるからです。 しかし、たとえばSDBMの場合、1レコードの長さに制限があったりするので注意 が必要です。(2KBとか4KBが最大) また、普通のDBMはキーと値(=文字列)のペアでしかなく、単純なデータしか 扱うことができません。 MLDBM(Multi Level DMB)は、これを改善したものですが、やはりデータ長の制限が あります。 私がお勧めするのは、DBM::Deepです。 これは100%Perlで書かれたDBMで、Perlの配列やハッシュなどの変数を「そのまま」 保存することができ、データのサイズに制限がありません。パフォーマンスも かなり良いと思います。 少し前に調べていたことなので、 最新のバージョンは知りませんが、完成度はかなり高いと思います。 私なら、DBMの類を使うならDBM::Deepを使います。ソースコードを見てみれば、 とても丁寧にきちんと書かれているのが分かります。作者とはメールでしか やり取りしたことはありませんが、とても好印象でした。

g_p_
質問者

お礼

こんにちは、丁寧なご意見ありがとうございます。 >まず、dbmopenと書いてありますが、これは少し古い書き方だと思います。 そうなんですか…、ありがとうございます。調べます。 本題の、DBM::Deep ですが、 驚きました。とても簡単に使えて、しかも配列やハッシュまで保存できる。 まだ、ホンの少しのテストしかしていませんが、今回使用する環境で使ってテストしてみます。 初心者とあまり変わらないスキルの私は、CPAN の使い方とか、 モジュールのインストール方法にちょっとした不安があるんですが、(とにかく英語が…) DBM::Deep の様に100% Perl のモジュールだと、本当にありがたいです。 Perl にはたくさんのモジュールが公開されてますが、私の様に 有効に利用できない(探す事ができない)者にとってとてもありがたい回答でした。 本当にありがとうございました。

その他の回答 (1)

  • onosuke
  • ベストアンサー率67% (310/456)
回答No.1

ア.DBMを使用した場合のメモリ使用量について Perl標準添付のSDBM実装だと、ファイルデータの保持目的では、ブロックバッファ分のメモリ(数kバイト)しか消費しません。 >99999番目のレコードを以下のスクリプトで表示したところ このテストでは、人間の時間感覚で計時する限り、有意な差は得られないでしょうね。(CPUクロック数の単位精度でやらないと…) また、OSのバッファメモリに必要なデータが全て収まっていて、テキストとDBMのファイルサイズ差も問題にならないでしょう。 イ.データベースを利用できない環境の場合、どちらが良いのか? 「DBMもデータベースですが…」というツッコミはさておき、 サイジングを気にするのであれば、もっと他のDBM系実装についても比較検討すべきです。 参考URLにQDBMのページを掲示します。 「兄弟」というセクションに他のDBM系実装と、ベンチマーク結果が掲載されています。

参考URL:
http://qdbm.sourceforge.net/
g_p_
質問者

補足

早速ご意見ありがとうございます。 >「DBMもデータベースですが…」というツッコミはさておき、 すみません、MySQLとかPostgreSQLとかのつもりでした、RDBMSと書けばよかったのかな?と反省しています。 QDBM 見てみました、ベンチマークの結果は速そうですし、オブジェクト指向な書き方で使えるようなので、 とてもよさそうですね。 でも、ちょっと見た感じでは、windowsの場合はCygwinを使わないとならない感じですかね? もしそうなら、今回は見送りになりそうですが、もう少し詳しく読んでみます。 オライリーの ”初めてのPerl” をざっと読んだ程度のスキルしか持ち合わせていないので、 Perl標準のDBM以外にもいろいろな方法が在ると知りませんでした。とても勉強になります。 >Perl標準添付のSDBM実装だと、ファイルデータの保持目的では、ブロックバッファ分のメモリ(数kバイト)しか消費しません。 >このテストでは、人間の時間感覚で計時する限り、有意な差は得られないでしょうね。 少し安心しました。 つまり、ディスクの容量に制限がなく、前記のデータの量程度ならば、どちらを使ってもほとんど差が無いと考えて、 ランダムアクセスが使えるDBMにしようかなと考えていますが、間違っていないでしょうか? 大変恐縮ですが、お時間のあるときにご回答頂けたら助かります。

関連するQ&A

  • DBMのことで・・・

    現在DBMを用いて簡単なデータベースを作成しています。 どこぞのCGIゲームのように、リアルタイムにデーターベースの内容が書き換えられる(戦闘をすればお金がプラス等)ようにしたいのですが、うまくいきませんorz #!/usr/bin/perl use AnyDBM_File; use Fcntl; print "Content-type: text/html\n\n"; dbmopen(%DBM, 'id', 0666); $DBM{"baka"} = ("0<>1<>2<>"); dbmclose(%DBM); print<<"HTMLTAG"; DBを作成しました HTMLTAG tie %DBM,AnyDBM_File,"id",O_RDONLY,0666; @aaa = split(/<>/,$DBM{"baka"}); untie %DBM; print"$aaa[0]"; $aaa[0]++; とりあえずこのスクリプトで、ブラウザの更新をおすたびに表示される数字が1あがっていく(プラスされて書き換えられる)ようにしたいのですが・・・ 多分ですがゲーム等の場合、いちいちdbmopenで書き換えてないとおもうので、その方法を知りたいというわけです。 いちおうこの段階ではDB作成、DBの読み込みと表示はできています。 ご教授おねがいします。

  • DBMオープン時の警告

    既存のDBMファイルをオープンする処理で、 以下のような警告メッセージが出るのですが、 何が悪いのか、見当がつきません。 すみませんが、分かる方がいらっしゃいましたら、教えてください。 どの値が、なぜ、初期化されていないのでしょうか? Use of uninitialized value in dbmopen at b.pl line 4. Use of uninitialized value in null operation at b.pl line 4. オープン処理をするスクリプトは以下です。 #!/usr/bin/perl -w use strict; my %all = (); dbmopen %all, "all", undef or die "DBM open error"; foreach (keys %all) { print $_, "?t", $all{$_}, "?n"; } dbmclose %all; exit;

    • ベストアンサー
    • Perl
  • 一つのテキストファイルと複数のファイルの結合

    よろしくお願いします.ディレクトリ内の一つのテキストファイル(joint.txt)と複数のファイルの結合を行ごとに隣へ結合するプログラムを作成しています.ここで以下のプログラムを作成したのですが,うまくいかないため,誤っている部分をご指摘願えないでしょうか. my $dirname = '.'; opendir(DIR, $dirname) or die "$dirname: $!"; while (my $dir = readdir(DIR)) { next unless (-f $dir); next unless ($dir =~ /\.txt$/); open(FILE, $dir) or die "$dir: $!"; open(FILE2,"joint.txt"); my @file = <FILE>; my @file2 = <FILE2>; close(FILE); close(FILE2); foreach my $line (@file) { foreach my $line2 (@file2) { chomp $line2; $line = "$line2.",".$line"; } } open(NEWFILE, "> $dir") or die "$dir: $!"; print NEWFILE @file; print NEWFILE @file2; close(NEWFILE); } closedir(DIR);

    • ベストアンサー
    • Perl
  • ディレクトリ内のテキストファイルに対する同一処理

    よろしくお願いします。現在Linuxの環境でテキスト処理をしております。 ディレクトリ内にファイル名の異なった以下のような大量ファイルがあります。 a.txt 0,1,2,3,4,5,6,7 1,2,3,4,5,6,7,8 b.txt 2,3,4,5,6,7,8,9 3,4,5,6,7,8,9,10 これらのファイルをカンマでsplitし、左から2番目の数にだけ1を引き,下のディレクトリであるoutに出力させます。出力は以下のようになります。 ./out/a.txt 0,0,2,3,4,5,6,7 1,2,3,4,5,6,7,8 ./out/b.txt 2,2,4,5,6,7,8,9 3,4,5,6,7,8,9,10 そこで以下のようなPerlのプログラムを作成しました。 use strict; use warnings; my $dirname = '.'; opendir(DIR, $dirname) or die "$dirname: $!"; while (my $dir = readdir(DIR)) { next unless (-f $dir); next unless ($dir =~ /\.txt$/); print $dir, "\n"; open(FILE, $dir) or die "$dir: $!"; my @file = <FILE>; foreach $line (@file) { my ($a,$b,$c,$d,$e,$f,$g,$h) = split(/,/, $line);      my $b = $b - 1; close(FILE); } open(NEWFILE, "> ./out/$dir") or die "$dir: $!"; print NEWFILE @file; close(NEWFILE); } closedir(DIR); ですが、出力は完了するのですが、元のファイルから計算がされていません。どこがどう間違えているのかご指摘よろしくお願い申し上げます。

    • ベストアンサー
    • Perl
  • 今更ながらdbmopenでutf8環境にハマってます。

    今更ながらdbmopenでutf8環境にハマってます。 #!/usr/bin/perl use strict; use warnings; use utf8; use open ":utf8"; use open ":std"; my $key1='test'; my $key2='test2'; my $val1 = 'あああ'; my $val2 = 'いいい'; print "$key1 $val1\n"; print "$key2 $val2\n"; open(my$fh,">./utf8DB2"); print $fh "$val1\n"; print $fh "$val2\n"; close $fh; my %HASH; dbmopen(%HASH, 'utf8DB', 0666); #utf8::decode($val1) if utf8::is_utf8($val1); #utf8::decode($val2) if utf8::is_utf8($val2); #下記2行が無いとWide character in null operation at ... utf8::encode($val1)if utf8::is_utf8($val1); utf8::encode($val2)if utf8::is_utf8($val2); $HASH{$key1}="$val1"; $HASH{$key2}="$val2"; dbmclose(%HASH); my%DBM; dbmopen(%DBM,'utf8DB',0666); while ( my( $key , $val ) = each %DBM ){ #utf8::decode($val) if utf8::is_utf8($val); #utf8::encode($val) if utf8::is_utf8($val); print "key1 : $key 値 : $val\n" ; } foreach my$key ( keys( %DBM ) ) { #utf8::decode($DBM{$key}) if utf8::is_utf8($DBM{$key}); #utf8::encode($DBM{$key}) if utf8::is_utf8($DBM{$key}); print "key2 : $key 値 : $DBM{$key}\n"; } dbmclose(%DBM); 上記スクリプトを実行時、値を表示する際に文字化けしているのですが、これは、dbmopenで保存時既に文字化けしているのでしょうか、それとも表示する際、適切にエンコードorデコード出来てないから文字化けしているのでしょうか。 また、どのようにすれば解決可能かご教授願えませんでしょうか。 いっその事、DataDumperで保存し、読み出すように修正しようかとも思うのですが、tieに変更した場合も、同様に文字化けするのでしょうか・・ その辺りも含め、アドバイス他ご教授願えますと助かります。

    • ベストアンサー
    • Perl
  • PHPで生成したテキストファイルをダウンロード

    PHPでMySQLデータベースからデータを取得してテキストファイルに出力し、それをダウンロードさせるプログラムを制作しています。 ただ、現在のやり方ではテキストファイル出力時の確認用にprintでページ内に表示させるものが、すべてダウンロードしてきたファイルに書き込まれてしまいます。 ダウンロード処理前に出力されて残っているテキストファイルは正常なので、ダウンロードの設定が悪いのだろうと思いますが、どう設定してやればいいのか分からない状態です。 以下ソース(テキストファイル生成部分などは省略します) <?php /////////////////////////////// //データベースからデータを取得 /////////////////////////////// //ファイルを書き込み専用で開く $file = fopen("sample.txt", 'w'); /////////////////////////////// //printでデータを表示しながらファイルへ出力 /////////////////////////////// //ファイルをクローズ fclose($file); // MySQLに対する処理 $close_flag = mysql_close($link); if ($close_flag){ print('<p>切断に成功しました。</p>'); } download_file("sample.txt"); function download_file($tmp_file) { // ダウンロードさせるファイル名 //$tmp_file = "./sample.txt"; $j_file = "sample.txt"; $j_file = mb_convert_encoding($j_file, "SJIS", "UTF-8"); /* ファイルの存在確認 */ if (!file_exists($tmp_file)) { die("Error: File(".$tmp_file.") does not exist"); } /* オープンできるか確認 */ if (!($fp = fopen($tmp_file, "r"))) { die("Error: Cannot open the file(".$tmp_file.")"); } fclose($fp); /* ファイルサイズの確認 */ if (($content_length = filesize($tmp_file)) == 0) { die("Error: File size is 0.(".$tmp_file.")"); } // ヘッダ header("Content-Type: application/octet-stream"); // ダイアログボックスに表示するファイル名 header("Content-Disposition: attachment; filename=$j_file"); //表示するファイルサイズ header("Content-Length: ".$content_length); header('Pragma: no-cache'); header('Cache-Control: no-cache'); // 対象ファイルを出力する。 readfile($tmp_file); exit; } ?>

    • ベストアンサー
    • PHP
  • 文字列を指定して,別のファイルでその文字列が存在する行を出力する

    いつもお世話になっております. 環境はWindows XP Pro でActiveperlを用いてプログラムをしております. この度,皆様にご意見をうかがいたいのは,「文字列を指定して,別のファイルでその文字列が存在する行を出力する」という内容です. まず,以下のテキストファイルがあります. data.txt ---------------------- A BA C DA E FA G sansyo.txt ----------------------------- B D F ------------------------------- 処理として,data.txtでsansyo.txtの行が "含まれる"行数を出力する ------------------------------- output.txt ------------------------------- 2 4 6 ここで自分なりにプログラムを組んでみました. ----------------------------------- open(FILE, "sansyo.txt"); open(FILE2,"data.txt"); @file = <FILE>; close(FILE); @file2 = <FILE2>; close(FILE2); foreach $line (@file) { foreach $line2 (@file2) { if ($line =~ $line2){ $hit = $.; } open(NEWFILE, " >> output.txt") or die "$!"; print NEWFILE $hit; close(NEWFILE); } } #ここまで ------------------------------------- ですが,永久ループに入ってしまったようにファイルはできるのですが, 出力されてきません. 間違っている点をご指摘ください.

    • ベストアンサー
    • Perl
  • ファイルの書き込み2

    またまた失礼します。 やりたいことは以下です。 1.ディレクトリを開く。 2.ファイル達を読み込み。 3.追記用のデータファイルを開く。 4.「2」にデータを追記 5.別のディレクトリに書き出し。 以上です。 4番がうまくいきません。 ためしに4の工程を抜かすと問題なく書き出されます。 4の工程でやりたいことは、 1.タブ区切りのテキストファイルを読み込み。 2.書き出し用に読み込んだ(追記したいファイル)<productのpath="[ココの値!]"を取得。 3.「2」番で取得した値と「1」番で取得した値を比較。 4.「3」がtrueの行を追記して書き出し。 以上。 要するになにがしたいかというと、 仮にタブ区切りテキストには10行分のデータがあるとします。 それぞれの行には3つ分の値が入っており、先頭にIDが付いています。 一番最初に読み込んだファイル(追記したいファイル)にも同様にIDが振られており(<product path="ココ")そのIDとタブ区切りテキストのIDが一致した行だけ追記したいということです。 下記のコードだとなぜか、 10行分のデータがすべて追記されてしまいます。 コードは以下です。 #!/usr/bin/perl #既存ファイル読み込み&追記 $n_dir = "newXml/"; $b_dir = "xml/"; opendir(DIR, $b_dir); while($file = readdir(DIR)){ $bfile="$b_dir$file"; $nfile="$n_dir$file"; $dfile="data/data.txt"; if (-T $bfile) { open(IN, $bfile);#既存ファイルオープン @list = <IN>; close(IN); open(OUT,">$nfile");#書き出しファイルオープン foreach $line (@list) { if ($line =~ /\<product/){ $line =~ /path=\".*\"/;#path取得 $1; } if ($line =~ /\<\/product\>/) { open(IN, $dfile);#追加データファイルオープン while($data = <IN>){ chomp(@data = split(/\t/,$data)); $data[0] =~ s/\//_/g; if($data[0] == $1){#取得したパスとdata.txtとってきた値を比較 print OUT <<EOF; <ds path="$data[0]">$data[1]</ds> <kw>$data[2]</kw> $line EOF } else{next;} } close(IN) }else{print OUT $line;} } close(OUT); } else{next;} } closedir(DIR); 長々と申し訳ありません。 エラーなどは特にありません。 ご協力お願いします。

    • ベストアンサー
    • Perl
  • データのテキストファイル抽出

    データの抽出に困っています。 エクセルデータにてA~Lのフィールドに値が入っていて、1000レコード程のデータを ・1レコードずつ ・A列のフィールドをファイル名に ・テキストファイルに出力 ・テキストファイルには1レコードを縦に、フィールドごと改行して並べる 上記のようにして作成する必要があります。 今までは、 A B C...K L となっているデータの列と行を入れ替えて A B C . . K L としてテキストファイルを作成し、セルAのフィールドをファイル名にして、 手作業で1列ごとにコピー&ペーストして行っていました。 非常に効率が悪いので、全自動化もしくは一部自動化出来ないかと試行錯誤していますが 良い方法が見つけられません。 何か良い方法をご存知の方がおりましたら教えて下さい。

  • テキストを参照としたPerlによる名前の変更

    よろしくお願いします。ディレクトリ内のファイル名をテキストデータを参照として変更したいと思っております。まず、以下の参照テキストがあります。 sansyo.txt 1,2,1 2,3,1 3,4,2 4,5,3 6,7,9 ・ ・ ・ ・ このファイルを利用してディレクトリ内のファイルを以下のようにリネームします。 1,2.txt → 1,2,1.txt 2,3.txt → 2,3,1.txt 3,4.txt → 3,4,2.txt 4,5.txt → 4,5,3.txt 6,7.txt → 6,7,9.txt ・ ・ ・ ・ ・ ここで私は以下のプログラムを作成しました。 sansyo.pl ------------------------------ use strict; use warnings; my $dirname = '.'; opendir(DIR, $dirname) or die "$dirname: $!"; while (my $dir = readdir(DIR)) { next unless (-f $dir); next unless ($dir =~ /\.txt$/); print $dir, "\n"; open(FILE, $dir) or die "$dir: $!"; open(FILE2,"sansyo.txt"); my @file = <FILE>; my @file2 = <FILE2>; close(FILE); close(FILE2); foreach my $line2 (@file2) { my ($a,$b,$c) = split(/,/, $line2); if ($dir == $a.",".$b.".txt"){ rename ($dir, $a.",".$b.",".$c.".txt"); } } } closedir(DIR); 内容は、FILE2にsansyo.txtをforeachで1行ずつ読み込んでいき、 $a,$b.txtというファイルが$dirに読み込んだファイルにあったら、 $a,$b,$c.txtというファイルにリネームするという内容です。 ですが、いろいろ試行錯誤したもののうまくいきません。 具体的には、 Argument "1,2.txt" isn't numeric in numeric eq (==) at sansyo.pl line 23. とエラーがでて if ($dir == $a.",".$b.".txt"){ この部分でエラーが発生しているようです。 どなたか解決方法をよろしくお願いします。

    • ベストアンサー
    • Perl

専門家に質問してみよう