プロが教える店舗&オフィスのセキュリティ対策術

Data::Dumper(Data::Dumperにこだわるわけではないのですが)を使って無名配列or無名ハッシュの値(40万行程度あると仮定。
例)
$VAR1 = {
'aaa' => {'get' => '1','all' => '0'},
'bbb' => {'get' => '1','all' => '1'},
}
といった形)を変更後、保存する場合、その配列orハッシュ全体を読み込んで保存する形になるのでしょうか、それとも、配列orハッシュ全体を読み込まず各値に対するアドレスから実体(リファレンス/シンボリックリファレンスの概念が明確にわかってないのですが)へアクセスして値を変更=そのまま保存するようになのでしょうか。
諸先輩or先生方、お手数ですがこの辺りの仕組みについてご教授願えますと幸いです。宜しくお願い致します。

A 回答 (6件)

結局やっていることは、



・以前にダンプしておいた情報を復元する
・処理する
・新たにダンプする

ということであり、ファイルの読み込みと書き込みは抱えている
ハッシュのデータ丸ごとです。40万行あるファイルならそれだけの量を
読み書きしています。

データベースを使う場合は、データベースに必要な情報だけをもらったり
与えたりするので、ファイルの読み書きという点で言えば格段に
少なくなるはずです。

もし現状のファイル丸ごと読み書きによる時間のロスが気になるということなら
データベースを使うことを検討してもいいと思います。

また、普通のテキストファイルで読み書きするにしても、Data::Dumperの
出力書式はこの種の目的には冗長ですから、自分で適当にフォーマットを決めた上で
読み書きすれば多少は改善できるかもしれません。

詳細な処理を訊くよりは、どういうデータをどのように処理したいのか
(他の質問とあわせて考えるとURLの参照記録?)を書いたほうが
良いアドバイスがもらえるでしょう。
    • good
    • 0
この回答へのお礼

貴重なアドバイス有難うございます。

やはりDBを使う事で必要な情報のみの読み書きになり、軽い処理になるんですね。

後、Data:Dumperでの出力処理は、冗長なんですか。その為、よくフリーのCGIでは<>でデータを挟んで処理しているケースが多いんでしょうかね・・・。

Data::Dumperが冗長という事で、ソースを見てみたいのですが、適当に検索してみたのですが、思うようにソースの味方がわかりませんでした。どのようにすれば見れるかなど、恐縮ですがご教授願えませんでしょうか。

お礼日時:2007/12/16 15:04

Data::Dumperをいじってその書式を変えようってんですか?


そりゃあいくらなんでも努力の方向を間違えているような気がします。

とりあえず、簡単に ',' で区切ったリストで表現した場合と
Data::Dumper の出力を使った場合でどのくらい違うのか試してみました。
面倒なので、読み出してそのまま出力するだけのものです。
が、データの更新はどちらの方法でも同じなのでやらないでもいいでしょう。
#urlには ',' が含まれる可能性があるのであまりよろしくありませんが
#あくまで目安を得るためということで。

テストスクリプトは以下の通りです。
40万レコードでは試してませんが、数字をいじればすぐ試せます。

#!/usr/bin/perl
# -*- coding: utf8 -*
use strict;
use warnings;
use Fatal qw(:void open close);
use Data::Dumper;

my $csv_file = 'basedata.txt';
my $dumped_file = 'hashdata.txt';


sub make_randomstr_generator {
my @basechars = split q{}, 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';

return sub {
my @t;
foreach (0 .. (rand()*10 + 7)) {
push @t, $basechars[int(rand() * scalar(@basechars))];
}
return 'http://' . (join q{}, @t) . '.com';
}
}

sub make_data_file {
my $item_count = shift || 10000;
my $gen = make_randomstr_generator;

open my $fh, '>', $csv_file;
printf {$fh} "%s,%s,%d,%s,%d\n", $gen->(), 'all', 0, 'cate', 0 for (1..$item_count);
close $fh;

open my $ifh, '<', $csv_file;
my $h_ref = {};
while (defined(my $l = <$ifh>)) {
chomp $l;
my @f = split q{,}, $l;
my $url = shift @f;
$h_ref->{$url} = { @f };
}
close $ifh;

open my $ofh, '>', $dumped_file;
print {$ofh} Dumper($h_ref);
close $ofh;
}

##########

sub use_csv_version {
open my $ifh, '<', $csv_file;
my $h_ref = {};
while (defined(my $l = <$ifh>)) {
chomp $l;
my @f = split q{,}, $l;
my $url = shift @f;
$h_ref->{$url} = { @f };
}
close $ifh;

open my $ofh, '>', "$csv_file.1";
foreach my $key (keys %{$h_ref}) {
printf {$ofh} "%s,%s,%d,%s,%d\n",
$key, 'all', $h_ref->{$key}{all}, 'cate', $h_ref->{$key}{cate};
}
close $ofh;
}

sub do_dump_version {
my $h_ref = {};

#require $dumped_file;
our $VAR1 = "";
do $dumped_file;
$h_ref = $VAR1;

open my $ofh, '>', "$dumped_file.1";
print {$ofh} Dumper($h_ref);
close $ofh;
}

#####
use Benchmark qw(:all);

make_data_file(100000);

my $count = 10;
timethese($count, {
'Non-Dump version.' => sub { use_csv_version},
'Dump version.' => sub { do_dump_version },
});

実行結果:
Benchmark: timing 10 iterations of Dump version., Non-Dump version....
Dump version.: 998 wallclock secs (616.11 usr + 4.51 sys = 620.62 CPU) @ 0.02/s (n=10)
Non-Dump version.: 286 wallclock secs (135.16 usr + 0.58 sys = 135.73 CPU) @ 0.07/s (n=10)

カッコの中身の数字が大体の処理時間の目安と見てください。
出力ファイルのサイズは次の通り

2007/12/19 01:51 3,850,858 basedata.txt.1
2007/12/19 01:46 19,903,454 hashdata.txt.1

どんだけ冗長かはわかっていただけると思います。
    • good
    • 0

私の理解力不足で、力になれないな感じがしてきました、申し訳ないです…。


どなたかフォローして頂けるとありがたいんですけど…。

一応、思う事を書きます。

>open(F,"> $dumpfile") ;
>print F Dumper(\%hash);
>close(F);

上記のコードでファイルに書き込まれたものは、
%hash のデータを Perl で実行可能なコードで表現した文字列であり、
メモリにある %hash とは何の関係もありませんよね?
で、そのコードをディスクに書き込んだわけですよね?

その後、
>my$hash_ref = require 'dump2.txt';で読み込み
これで、dump2.txt 内のコードが実行されて、新たに $hash_ref に全てが復元されて、新しいメモリが割り当てられます。
(一部って言うのは無理なんじゃないかと思います、dump2.txt をテキストとして直接いじれば別でしょうけど)。

>リファレンスを使って実体を直接変更するという事は、毎回ハッシュ全体
>(foreachかwhillによっても読み込む行か全体かが違うようですが、変更時には既にハッシュ全体を読み込んでいる状態?)
>を保存するのか
リファレンスと言うのは、
配列等のメモリアドレスの様な物を格納した単なるスカラー値ですので、
リファレンスを使ったからって特別何かが変わるわけではありません。
そもそも、

>my %hash;

>foreach my $a ( @allurl ) {
>foreach my $b ( @cate ) {
>$hash{$a}{$b} = '0';
>}
>}

$hash{$a} は無名ハッシュのリファレンスなんで、リファレンス以外に参照できません。
で、データが復元されているのは require の時点であって、
呼び出されたときにメモリが割り当てられるわけではないと思います。

最後にしつこいようですが、
●Data::Dumper が出力する物は単なるPerlのコード。

●require はコードを実行してコードの最後に評価された値を返すので、
 Data::Dumperで出力したテキストを require した場合、
 他の何物とも無縁の新しいデータが生成される。

あと、余計なお世話かも知れませんが、
use strict;
した方が良いでしょう、また
$a と $b は使うのは止めといた方がよいです。

*Perl の内部までは私も良く分かっていませんので、間違いがあるかも知れません、すみません。
間違っていたらどなたかツッコミよろしくお願いします。
    • good
    • 0

>リファレンスを使っての保存時でも、結局配列全体を保存し直し・・と言う事になるのでしょうか。



書かれている「保存」の意味が良く理解できません。

私が思っている、Data::Dumper を使ったデータの保存って言ったら、
ハッシュやら配列やらオブジェクトを文字列化したものを、テキストとして、
ファイルやらデータベースに格納するって事だと考えていますが、
そうではないのでしょうか?

全体とか部分的にといった考え方が良く理解できません。
Data::Dumper 出力された物を、テキストデータとして扱うといった意味ですか?

Data::Dumper で出力した物をどう保存していて、
どんな風に再利用しているのか簡単なコードを見せて頂けたら
何かアドバイスできそうな気がするんですが…。

この回答への補足

私もテキストとして、ファイルやらデータベースに格納すると考えております。
全体や部分的にと言う事について、ここでは重複いたしますので、下記のスクリプト内容を前提に最後に再度私の意図した質問を掲載させて頂いております。
よろしければご参照頂けましたら幸いです。

宜しくお願い致します。

・ファイルの内容
$ cat testfile1
--testfile1の内容----------
aaaa
bbbb
cccc
--ここまでで40万行あると仮定----------

・スクリプト内容表示
$ cat dump-test.cgi
--ここからdump-test.cgiの内容----------------
use Data::Dumper;
$Data::Dumper::Indent = 1;

$dumpfile='dump2.txt';

use Data::Dumper;
my @allurl;
while (chomp($_ =<STDIN>)){
push(@allurl,"$_");
}
close(STDIN);

my @cate = qw(all cate);
my %hash;

foreach my $a ( @allurl ) {
foreach my $b ( @cate ) {
$hash{$a}{$b} = '0';
}
}
open(F,"> $dumpfile") ;
print F Dumper(\%hash);
close(F);

my$all = 'all';
my$cate = 'cate';
my$hash_ref = require 'dump2.txt';
foreach my $c ( keys %{$hash_ref} ) {
if(exists($hash{$c}) ){
$hash_ref->{$c}{$cate} = '1';
}else{
$hash_ref->{$c}{$cate} = '0';
}
}

open(F,"> dump2.txt") ;
print F Dumper($hash_ref);
close(F);
--ここまで---------------------------

・実行
$ cat testfile1 | perl url-dump-test.cgi

サーバーのシェル上からパイプを使ってtestfile1の中身を渡しているのですが、一度保存したもの(ここの部分は毎回ではなく別スクリプトでもよいのですが)をmy$hash_ref = require 'dump2.txt';で読み込みそれ以降の条件分岐箇所で何かしらの判定をして
$hash_ref->{$c}{$cate} = '1';

$hash_ref->{$c}{$cate} = '0';
と変更し保存したい場合、リファレンスを使って実体を直接変更するという事は、毎回ハッシュ全体(foreachかwhillによっても読み込む行か全体かが違うようですが、変更時には既にハッシュ全体を読み込んでいる状態?)を保存するのか、値の変更箇所だけを保存(書き換え)できるのか・・の所がよくわかってないのですけれども、ここの部分についてお分かりでございますでしょうか。ご教授頂けますと幸いです。また結果的にはリファレンスを使わなくても変更時にはハッシュなり配列なりの全体を保存するという事なのでしょうか。
またDB(mysql,postgresなどを想定)での保存の場合は、この場合、
$hash_ref->{$c}{$cate} = '1';

$hash_ref->{$c}{$cate} = '0';
の値の変更箇所だけを変更できるという概念なのでございますか?お恥かしながらご存知なようでしたら合わせてご教授頂けますと幸いです。

補足日時:2007/12/09 15:22
    • good
    • 0

perlにポインターってあるんですか



静的変数に使ってるんでコピーだと解釈してます
    • good
    • 0
この回答へのお礼

ご教授いただき有難うございます。

「perl ポインター」でぐぐってみますと、かなり魅力的なサイトがヒットして参りますので、この辺り適当に一読してもう少し試行錯誤してみたいと存じます。

有難うございました。

お礼日時:2007/12/07 12:32

Data::Dumper で文字列化された物は単なるPerl のコードなんで、


元のデータのアドレスとかは関係ないですよ。

文字列化されたコードを eval すれば元のデータとは無縁の新しいデータが生成されるだけで、
特に難しく考える必要はない気がしますけどね。
    • good
    • 0
この回答へのお礼

ご教授いただき有難うございます。

リファレンスを使っての保存時でも、結局配列全体を保存し直し・・と言う事になるのでしょうか。

もう少し試行錯誤してみたいと思います。有難うございました。

お礼日時:2007/12/07 12:30

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