ルモーリン
ホーム 更新 Perl Sample サービス 雑談 鉄ゲタ Linux リンク 連絡先

Perlサンプル9 データの保存と復元

2019-08-31

内容

階層構造を持つデータを丸ごとファイルに保存、復元します。 なお、保存したファイルは復元以外に開けません。

コード

#!/usr/bin/env perl

use v5.26;
use utf8;
use strict;
use warnings;

use Encode::Argv;
use Encode::Locale;
use FindBin;
use Storable;

use open IO => ":utf8";
binmode STDIN, ":encoding(console_in)";
binmode STDOUT, ":encoding(console_out)";

$| = 1;

# スクリプトがある所に現在のディレクトリを移動
chdir $FindBin::Bin;

use constant STORE_FILE => "perlsample_009.store";


my $world;
if (-e Encode::encode locale_fs => STORE_FILE) {
	$world = retrieve Encode::encode locale_fs => STORE_FILE;
	say "世界を復元しました:@{[STORE_FILE]}";
} else {
	say "世界が誕生しました";
	$world = {
		たこルカぬいぐるみ_肩のりサイズ => [
			{ name => "たこルカ1号", kind => "ほほえみ顔", },
			{ name => "たこルカ2号", kind => "きゃー顔", },
			{ name => "たこルカ3号", kind => "よだれ顔", },
			{ name => "たこルカ4号", kind => "おすまし顔", },
		],
		ルカ姐さん_ぬいぐるみ => [
			{ name => "ルカ姐さん", kind => "キーホルダー", },
			{ name => "ルカ姐さん", kind => "キーホルダー", },
			{ name => "ルカ姐さん", kind => "キーホルダー", },
			{ name => "ルカ姐さん", kind => "ジャンボぬいぐるみ", },
			{ name => "ルカ姐さん", kind => "ジャンボぬいぐるみ", },
			{ name => "ルカ姐さん", kind => "ジャンボぬいぐるみ", },
			{ name => "ルカ姐さん", kind => "ジャンボぬいぐるみ", },
		],
	};
}

push @{$world->{ルカ姐さん_手ぬぐい}}, { name => "ルカ姐さん", kind => "手ぬぐい", };
say "手ぬぐい増やしました";

store $world, Encode::encode locale_fs => STORE_FILE;
say "世界を保存しました:@{[STORE_FILE]}";

say "世界の構成";
say "-----";

for (keys %{$world}) {
	say;
	say "$_->{name}, $_->{kind}" for @{$world->{$_}};
	say "-----";
}

実行結果

1回目はファイルがないので世界が誕生します。

世界が誕生しました
手ぬぐい増やしました
世界を保存しました:perlsample_009.store
世界の構成
-----
ルカ姐さん_ぬいぐるみ
ルカ姐さん, キーホルダー
ルカ姐さん, キーホルダー
ルカ姐さん, キーホルダー
ルカ姐さん, ジャンボぬいぐるみ
ルカ姐さん, ジャンボぬいぐるみ
ルカ姐さん, ジャンボぬいぐるみ
ルカ姐さん, ジャンボぬいぐるみ
-----
ルカ姐さん_手ぬぐい
ルカ姐さん, 手ぬぐい
-----
たこルカぬいぐるみ_肩のりサイズ
たこルカ1号, ほほえみ顔
たこルカ2号, きゃー顔
たこルカ3号, よだれ顔
たこルカ4号, おすまし顔
-----

2回目はファイルから復元、手ぬぐいが増えて、保存されます。

世界を復元しました:perlsample_009.store
手ぬぐい増やしました
世界を保存しました:perlsample_009.store
世界の構成
-----
ルカ姐さん_ぬいぐるみ
ルカ姐さん, キーホルダー
ルカ姐さん, キーホルダー
ルカ姐さん, キーホルダー
ルカ姐さん, ジャンボぬいぐるみ
ルカ姐さん, ジャンボぬいぐるみ
ルカ姐さん, ジャンボぬいぐるみ
ルカ姐さん, ジャンボぬいぐるみ
-----
たこルカぬいぐるみ_肩のりサイズ
たこルカ1号, ほほえみ顔
たこルカ2号, きゃー顔
たこルカ3号, よだれ顔
たこルカ4号, おすまし顔
-----
ルカ姐さん_手ぬぐい
ルカ姐さん, 手ぬぐい
ルカ姐さん, 手ぬぐい
-----

3回目も同様に手ぬぐいが増えます。 ここで注目ポイント、ぬいぐるみ/手ぬぐい/肩のリサイズの順序が実行の度に変わっています。 ハッシュの特徴なので良く覚えておきましょう。

世界を復元しました:perlsample_009.store
手ぬぐい増やしました
世界を保存しました:perlsample_009.store
世界の構成
-----
ルカ姐さん_ぬいぐるみ
ルカ姐さん, キーホルダー
ルカ姐さん, キーホルダー
ルカ姐さん, キーホルダー
ルカ姐さん, ジャンボぬいぐるみ
ルカ姐さん, ジャンボぬいぐるみ
ルカ姐さん, ジャンボぬいぐるみ
ルカ姐さん, ジャンボぬいぐるみ
-----
ルカ姐さん_手ぬぐい
ルカ姐さん, 手ぬぐい
ルカ姐さん, 手ぬぐい
ルカ姐さん, 手ぬぐい
-----
たこルカぬいぐるみ_肩のりサイズ
たこルカ1号, ほほえみ顔
たこルカ2号, きゃー顔
たこルカ3号, よだれ顔
たこルカ4号, おすまし顔
-----

解説

use Storable

リファレンスの先を丸ごとファイルに保存/復元します。 別の言語のプログラムやエディタが見ることはできませんが、お手軽に使えます。
Storable - Perlデータ構造体の永続化 - perldoc.jp

push

「ARRAY をスタックとして扱い、LIST 内の値を ARRAY の終わりに追加します。 ARRAY の大きさは、LIST の長さ分だけ大きくなります。」
Perlの組み込み関数 push の翻訳 - perldoc.jp

@{$world->{ルカ姐さん_手ぬぐい}}

$world->{ルカ姐さん_手ぬぐい}を配列のリファレンスとみなしてデリファレンスして配列にします。 ちょっと待って。 配列もなければリファレンスもないし、大体、ハッシュのキー「ルカ姐さん_手ぬぐい」がない。 これらは全て逆算して自動で生成されて収まります
「これが、先の左辺値コンテキストで用いるとリファレンスが 存在するようになるというケースの一つです。 この文以前には、$array[$x] は未定義かもしれません。 その場合自動的にハッシュリファレンスと定義されて、{"foo"} が 検索できるようになります。 同じように $array[$x]->{"foo"} が配列リファレンスで定義されるので、 [0] をそこで探すことができます。 このプロセスは 自動有効化 (autovivification) と呼ばれます。」
perlref - Perlのリファレンスとネストしたデータ構造 - perldoc.jp

store

リファレンスの先をファイルに書き出します。
Storable - Perlデータ構造体の永続化 - perldoc.jp

retrieve

ファイルを読み込んで、それを指すリファレンスを返します。
Storable - Perlデータ構造体の永続化 - perldoc.jp