第5章 多次元データ構造とJSONデータ入出力
Perlで多次元データ構造を扱う方法について学んでいくよ。データを自由に表現できるようになると、プログラミングの幅が広がるよ。応用として、Webでよく使われるデータ表現であるJSONの入出力をしてみるよ。

JSONデータを入出力するサンプル

Perlで多次元データ構造を扱う方法を、JSONを入出力するプログラムを使って解説していきます。JSONは、多次元データ構造を表現できるWebでよく使われるデータ形式です。

複数の書籍の情報を表現するJSONデータ

書籍データのCSVでの表現

前の章で学んだ、書籍のCSVデータは次のようなものでした。

ID,書名,著者名,価格,発売日
1,Perlテキスト処理プログラミング入門,木本裕紀,2900,2021-01-03
2,Web開発をやるぜPerl,木本裕紀,2000,2021-01-03
3,データベース入門,田中太郎,1900,2019-03-06

書籍データのJSONでの表現

これを、JSONとして表現したものが以下です。

[
  {
    "id" : 1,
    "name" : "Perlテキスト処理プログラミング入門",
    "author_name" : "木本裕紀",
    "price" : 2900,
    "issued_date" : "2021-01-03"
  },
  {
    "id" : 2,
    "name" : "Web開発をやるぜPerl",
    "author_name" : "木本裕紀",
    "price" : 2000,
    "issued_date" : "2021-01-03"
  },
  {
    "id" : 3,
    "name" : "データベース入門",
    "author_name" : "田中太郎",
    "price" : 1900,
    "issued_date" : "2019-03-06"
  }
]

「ID」「書名」「著者名」「価格」「発売日」という項目名は、「id」「name」「author_name」「price」「issued_date」という英語にしてあります。連想配列の配列というデータ構造で表現しています。

JSONデータフォーマットの解説

JSONのデータフォーマットについて、簡単に解説しておきます。

数値

数値は「1」や「1.5」のように表現します。

1
1.5

文字列

文字列はダブルクォート「"」で囲んで表現します。

"name"
"Perlテキスト処理プログラミング入門"

配列

配列は「[」と「]」で囲んで「,」で複数の要素を並べます。

[3, 5, 7]
["apple", "orange", "banana"]

空白や改行は、見やすくするために、自由に使うことができます。

[
  3,
  5,
  7
]

連想配列

連想配列は「{」と「}」で囲んで、キーと値のペアを「,」で複数並べます。キーと値は「:」で繋げます。キーは、文字列である必要があります。連想配列は、Perlのハッシュに該当します。

{"id" : 1, "name" : "モジガエル"}

連想配列を配列の要素にすることもできます。

[
  {"id" : 1, "name" : "モジガエル"},
  {"id" : 2, "name" : "サーバー太郎"}
]

連想配列の配列で表現された書籍のJSONデータ

もう一度、書籍のJSONデータを見てみましょう。連想配列が、配列の要素になっていますね。

[
  {
    "id" : 1,
    "name" : "Perlテキスト処理プログラミング入門",
    "author_name" : "木本裕紀",
    "price" : 2900,
    "issued_date" : "2021-01-03"
  },
  {
    "id" : 2,
    "name" : "Web開発をやるぜPerl",
    "author_name" : "木本裕紀",
    "price" : 2000,
    "issued_date" : "2021-01-03"
  },
  {
    "id" : 3,
    "name" : "データベース入門",
    "author_name" : "田中太郎",
    "price" : 1900,
    "issued_date" : "2019-03-06"
  }
]

JSONデータを読み込んで、データを加工して、出力する

JSONデータを読み込んで、データを加工して、出力するサンプルコードを書いてみます。

価格のデータに10%の消費税を追加して、保存するという処理にしてみます。

JSONデータを「book1_in.json」という名前で保存します。文字コードはUTF-8にしてください。

[
  {
    "id" : 1,
    "name" : "Perlテキスト処理プログラミング入門",
    "author_name" : "木本裕紀",
    "price" : 2900,
    "issued_date" : "2021-01-03"
  },
  {
    "id" : 2,
    "name" : "Web開発をやるぜPerl",
    "author_name" : "木本裕紀",
    "price" : 2000,
    "issued_date" : "2021-01-03"
  },
  {
    "id" : 3,
    "name" : "データベース入門",
    "author_name" : "田中太郎",
    "price" : 1900,
    "issued_date" : "2019-03-06"
  }
]

次にJSONを読み込むプログラム「edit_json_add_price_zei.pl」を作成します。

use strict;
use warnings;
use utf8;

# JSON::PPを読み込み
# encode_json関数とdecode_json関数をインポートする
use JSON::PP 'encode_json', 'decode_json';

# ファイル全部を読み込む
my $in_json = do { local $/; <> };

# deocde_json関数でJSONデータをPerlのデータ構造に変換
my $books = decode_json $in_json;

for my $book (@$books) {
  # 書籍の情報を取得
  my $id = $book->{id};
  my $name = $book->{name};
  my $price = $book->{price};
  my $issued_date = $book->{issued_date};
  
  # 税込み価格
  my $price_zeikomi = $price * 1.1;
  
  # 税込み価格で書籍の情報を更新
  
  $book->{price} = $price_zeikomi
}

# encode_json関数でPerlのデータ構造をJSONデータに変換
my $out_json = encode_json $books;

# JSONを出力
print $out_json;

次のように実行してみましょう。

perl edit_json_add_price_zei.pl book1_in.json > book1_out.json

出力結果です。出力データには、空白文字と改行が含まれていないので、見づらいですが、価格が税込み価格に更新されているのを確認できます。

[{"name":"Perlテキスト処理プログラミング入門","author_name":"木本裕紀","price":3190,"issued_date":"2021-01-03","id":1},{"id":2,"issued_date":"2021-01-03","name":"Web開発をやるぜPerl","author_name":"木本裕紀","price":2200},{"id":3,"issued_date":"2019-03-06","author_name":"田中太郎","name":"データベース入門","price":2090}]

JSONを読み込むソースコードの解説

JSONを読み込むソースコードを解説していきます。

JSON::PPモジュールの読み込みと関数のインポート

JSONデータを扱うために、JSON::PPモジュールを使います。JSON::PPモジュールは、Perl 5.14からPerlに含まれています。

use文を使って、JSON::PPモジュールを読み込みます。モジュールを読み込むと同時に、利用する関数をインポートすることができます。

# JSON::PPを読み込み
# encode_json関数とdecode_json関数をインポートする
use JSON::PP 'encode_json', 'decode_json';

Perlのデータ構造からJSONデータを生成するencode_json関数をインポートしています。

JSONデータをPerlのデータ構造にするdecode_json関数をインポートしています。

JSONデータをファイルから読み込みPerlのデータ構造に変換する

JSONデータをファイルから読み込みます。

# ファイル全部を読み込む
my $in_json = do { local $/; <> };

次に、deocde_json関数でJSONデータをPerlのデータ構造に変換します。JSONの文字列は、Perlの内部文字列に変換されます。

# deocde_json関数でJSONデータをPerlのデータ構造に変換
my $books = decode_json $in_json;

$booksは、以下のようなPerlのデータ構造になっています。

[
  {
    "id" => 1,
    "name" => "Perlテキスト処理プログラミング入門",
    "author_name" => "木本裕紀",
    "price" => 2900,
    "issued_date" => "2021-01-03"
  },
  {
    "id" => 2,
    "name" => "Web開発をやるぜPerl",
    "author_name" => "木本裕紀",
    "price" => 2000,
    "issued_date" => "2021-01-03"
  },
  {
    "id" => 3,
    "name" => "データベース入門",
    "author_name" => "田中太郎",
    "price" => 1900,
    "issued_date" => "2019-03-06"
  }
]

Perlでは、上記のデータ構造を理解するのにリファレンスの知識が必要になります。ハッシュの配列と呼ばれるデータ構造ですが、Perlでは、多次元データ構造を表現するのに、リファレンスとういう機能を使います。リファレンスについては、この後、詳しく解説します。

Perlのデータ構造をJSONデータに変換する

Perlのデータ構造をJSONデータに変換するにはJSON::PPモジュールのencode_json関数を使用します。

# encode_json関数でPerlデータ構造をJSONデータに変換
my $out_json = encode_json $books;

print文で出力して、リダイレクトを使ってファイルに書き込みます。

# JSONを出力
print $out_json;

以下のようにプログラムを実行します。

perl edit_json_add_price_zei.pl book1_in.json > book1_out.json

出力されたファイルの内容は、消費税が10%加算されたJSONデータです。

[{"name":"Perlテキスト処理プログラミング入門","author_name":"木本裕紀","price":3190,"issued_date":"2021-01-03","id":1},{"id":2,"issued_date":"2021-01-03","name":"Web開発をやるぜPerl","author_name":"木本裕紀","price":2200},{"id":3,"issued_date":"2019-03-06","author_name":"田中太郎","name":"データベース入門","price":2090}]

リファレンス

Perlにはリファレンスという機能があります。リファレンスは日本語では「参照」という意味です。リファレンスは、実体データへの参照です。

リファレンスを作成するコード「create_scalar_ref.pl」を書いてみます。リファレンスは、リファレンス生成演算子「\」で作成できます。Windowsの場合は「¥」マークです。

use strict;
use warnings;

# 実際のデータ
my $name = "kimoto";

# リファレンスを作成
my $name_ref = \$name;

# 中身を表示してみるとどうなる?
print "$name_ref\n";

実行してみましょう。

perl create_scalar_ref.pl

私の実行環境での出力結果です。

SCALAR(0x55beb1acea08)

リファレンスは、実際のデータの指す矢印のようなものです。「$name_ref」は「$name」というデータを指しています。リファレンスという言葉で、データを指す矢印を想像してみてください。

# リファレンスのイメージ
$name_ref -> $name

内部実装では、「$name_ref」には「$name」のメモリ上の位置を示す数値(アドレス)が代入されています。出力結果に表れた16進数の数字はアドレスです。

「$name」は一つの値を表すスカラ変数ですので、このようなリファレンスをスカラリファレンスと呼びます。

配列リファレンスの作成

配列のリファレンスも「\」を使って同じように作成できます。

# 配列のリファレンスを作成
my $nums_ref = \@nums;

「$nums_ref」は「@nums」という配列の実体を指しています。

# 配列のリファレンスのイメージ
$nums_ref -> @nums

配列のリファレンスを作成するサンプル「create_array_ref.pl」です。

use strict;
use warnings;

# 実際のデータ
my @nums = (3, 5, 9);

# 配列リファレンスを作成
my $nums_ref = \@nums;

# 中身を表示してみるとどうなる?
print "$nums_ref\n";

実行してみましょう。

perl create_array_ref.pl

私の実行環境での出力結果です。

ARRAY(0x560d5ff19a08)

配列リファレンスを簡単に作成する

配列リファレンスは、無名配列生成子「[]」と呼ばれる文法を使って、簡単に作成できます。

# 無名配列生成子を使って配列リファレンスを簡単に作成
my $nums_ref = [3, 5, 9];

これは、以下の記述と同じになります。配列を作成している部分が省略できてきますね。

# 実際のデータ
my @nums = (3, 5, 9);

# 配列リファレンスを作成
my $nums_ref = \@nums;

Perlでは「()」と「[]」を区別して読めるようになることが、大切です。

無名配列生成子を使って、配列リファレンスを簡単に作成するサンプル「create_array_ref_simple.pl」です。

use strict;
use warnings;

# 無名配列生成子を使って配列リファレンスを簡単に作成
my $nums_ref = [3, 5, 9];

# 中身を表示してみるとどうなる?
print "$nums_ref\n";

実行してみましょう。

perl create_array_ref_simple.pl

私の実行環境での出力結果です。

ARRAY(0x55e35929b398)

ハッシュのリファレンスの作成

ハッシュのリファレンスも「\」を使って同じように作成できます。

# ハッシュのリファレンスを作成
my $book_ref = \%book;

リファレンスは、実際のデータの指す矢印のようなものです。「$book_ref」は「%book」というハッシュの実体を指しています。

# ハッシュのリファレンスのイメージ
$book_ref -> %book

ハッシュのリファレンスを作成するサンプル「create_hash_ref.pl」です。

use strict;
use warnings;

# 実際のデータ
my %book = (id => 1, name => 'Perl Book');

# ハッシュリファレンスを作成
my $book_ref = \%book;

# 中身を表示してみるとどうなる?
print "$book_ref\n";

実行してみましょう。

perl create_hash_ref.pl

私の実行環境での出力結果です。

HASH(0x560d5ff19a08)

ハッシュリファレンスを簡単に作成する

ハッシュリファレンスは、無名ハッシュ生成子「{}」と呼ばれる文法を使って、簡単に作成できます。

# 無名ハッシュ生成子を使ってハッシュリファレンスを簡単に作成
my $book_ref = {id => 1, name => 'Perl Book'};

これは、以下の記述と同じになります。ハッシュを作成している部分が省略できてきますね。

# 実際のデータ
my %book = (id => 1, name => 'Perl Book');

# ハッシュリファレンスを作成
my $book_ref = \%book;

Perlでは「()」と「{}」を区別して読めるようになることが、大切です。

無名ハッシュ生成子を使って、ハッシュリファレンスを簡単に作成するサンプル「create_hash_ref_simple.pl」です。

use strict;
use warnings;

# 無名ハッシュ生成子を使ってハッシュリファレンスを簡単に作成
my $book_ref = {id => 1, name => 'Perl Book'};

# 中身を表示してみるとどうなる?
print "$book_ref\n";

実行してみましょう。

perl create_hash_ref_simple.pl

私の実行環境での出力結果です。

HASH(0x55e35929b398)

デリファレンス

デリファレンスは、リファレンスの作成と反対になる操作で、リファレンスから実体を取得する操作のことです。

デリファレンスには、スカラのデリファレンス、配列のデリファレンス、ハッシュのデリファレンスがあります。

  • スカラのデリファレンス
  • 配列のデリファレンス
  • ハッシュのデリファレンス

スカラのデリファレンス

スカラのデリファレンスの構文です。

${スカラリファレンス}

スカラリファレンスをデリファレンスして文字列の実体を取り出すサンプル「deref_scalar.pl」です。

use strict;
use warnings;

# 実際のデータ
my $name = "kimoto";

# リファレンスを作成
my $name_ref = \$name;

# スカラのデリファレンスして実体を取り出す
my $name_deref = ${$name_ref};

# 中身を表示してみるとどうなる? 元に戻っていますね。
print "$name_deref\n";

実行してみましょう。

perl deref_scalar.pl

出力結果です。「kimoto」という文字列の内容が出力されています。

kimoto

スカラデリファレンスの構文は、対象が単なる変数の場合は「{}」を省略できるので、次のように書かれることも多いので覚えておきましょう。

# スカラのデリファレンスして実体を取り出す
my $name_deref = $$name_ref;

配列のデリファレンス

配列のデリファレンスの構文です。

@{配列リファレンス}

配列リファレンスをデリファレンスして配列の実体を取り出すサンプル「deref_array.pl」です。

use strict;
use warnings;

# 実際のデータ
my @nums = (3, 5, 9);

# 配列リファレンスを作成
my $nums_ref = \@nums;

# 配列をデリファレンスして実体を取り出す
my @nums_deref = @{$nums_ref};

# 中身を表示してみるとどうなる? 元に戻っていますね。
print "@nums_deref\n";

実行してみましょう。

perl deref_array.pl

出力結果です。「3」「5」「9」という配列の内容が出力されています。

3 5 9

配列デリファレンスの構文は、対象が単なる変数の場合は「{}」を省略できるので、次のように書かれることも多いので覚えておきましょう。

# 配列のデリファレンス
my @nums_deref = @$nums_ref;

ハッシュのデリファレンス

ハッシュのデリファレンスの構文です。

%{ハッシュリファレンス}

ハッシュリファレンスをデリファレンスしてハッシュの実体を取り出すサンプル「deref_hash.pl」です。

use strict;
use warnings;

# 実際のデータ
my %book = (id => 1, name => 'Perl Book');

# ハッシュリファレンスを作成
my $book_ref = \%book;

# ハッシュのデリファレンスを行って実体を取り出す
my %book_deref = %{$book_ref};

# 中身を表示してみるとどうなる? 元に戻っていますね。
for my $name (sort keys %book_deref) {
  my $value = $book_deref{$name};
  print "$name : $value\n";
}

実行してみましょう。

perl deref_hash.pl

出力結果です。ハッシュの内容が出力されています。

id : 1
name : Perl Book

ハッシュデリファレンスの構文は、対象が単なる変数の場合は「{}」を省略できるので、次のように書かれることも多いので覚えておきましょう。

# ハッシュのデリファレンスを行って実体を取り出す
my %book_deref = %$book_ref;

リファレンスから要素にアクセスする

配列の要素やハッシュの値をリファレンスから取り出すための、簡単な記法がPerlでは準備されています。

配列のリファレンスから要素を取得・設定する

配列のリファレンスから要素にアクセスするにはアロー演算子「->」を使用します。配列のリファレンスの場合と比較するために、配列の要素を取得・設定する方法も記述します。

# 配列のリファレンスから要素を取得
$array_ref->[$index]

# 配列のリファレンスから要素を設定
$array_ref->[$index] = 値

# 参考情報

# 配列の要素を取得
$array[$index]

# 配列の要素を設定
$array[$index] = 値

配列で要素にアクセスする場合の違いは「->」があるかないかだけですので、Perlのプログラムを読むときは、注意して読んでください。

配列のリファレンスから要素の設定・取得を行うサンプル「access_array_ref.pl」です。

use strict;
use warnings;

# 配列のリファレンスを作成
my $nums = [3, 5, 7];

# 配列のリファレンスから要素を取得
my $num = $nums->[2];

print "$num\n";

# 配列のリファレンスから要素を設定
$nums->[1] = 10;

# 配列のリファレンスをデリファレンスして出力
print "@$nums\n";

プログラムを実行してみましょう。

perl access_array_ref.pl

出力結果です。配列のリファレンスから要素の取得と設定ができています。

7
3 10 7

ハッシュのリファレンスから要素を取得・設定する

ハッシュのリファレンスから要素にアクセスするにはアロー演算子「->」を使用します。ハッシュのリファレンスの場合と比較するために、ハッシュの要素を取得・設定する方法も記述します。

# ハッシュのリファレンスから要素を取得
$hash_ref->{$key}

# ハッシュのリファレンスから要素を設定
$hash_ref->{$key} = 値

# 参考情報

# ハッシュの要素を取得
$hash{$key}

# ハッシュの要素を設定
$hash{$key} = 値

ハッシュで要素にアクセスする場合の違いは「->」があるかないかだけですので、Perlのプログラムを読むときは、注意して読んでください。

ハッシュのリファレンスから要素の設定・取得を行うサンプル「access_hash_ref.pl」です。

use strict;
use warnings;

# ハッシュのリファレンスを作成
my $book = {id => 1, name => 'Perl Book'};

# ハッシュのリファレンスから要素を取得
my $name = $book->{name};

print "$name\n";

# ハッシュのリファレンスから要素を設定
$book->{id} = 2;

# ハッシュのリファレンスをデリファレンスして出力
for my $key (sort keys %$book) {
  my $value = $book->{$key};
  print "$key : $value\n";
}

プログラムを実行してみましょう。

perl access_hash_ref.pl

出力結果です。ハッシュのリファレンスから要素の取得と設定ができています。

Perl Book
id : 2
name : Perl Book

データをダンプする

Perlのデータをダンプする方法について書きます。ダンプとは、この文脈では、データの中身を文字列に変換して表示することです。

配列のデータ、配列のリファレンスのデータ、ハッシュのデータ、ハッシュのリファレンスのデータをData::DumperというPerlのモジュールを使ってダンプしてみましょう。

配列をダンプする

配列をダンプしてみましょう。Data::Dumperモジュールを読み込み、Dumper関数を使います。

Dumper関数の引数は、リファレンスを渡すのがお勧めな方法なので「\」を使って、配列のリファレンスを作成しています。

Dumper関数は、文字列化されたデータの情報を返すので、warn関数を使って、標準エラー出力に、出力しています。

配列の情報をダンプする「dump_array.pl」です。

use strict;
use warnings;

# Data::Dumperモジュールを読み込み
use Data::Dumper;

# 配列
my @nums = (3, 5, 6);

# 配列のデータをダンプする
warn Dumper \@nums;

実行してみましょう。

perl dump_array.pl

配列の内容が出力されます。

$VAR1 = [
          3,
          5,
          6
        ];

配列のリファレンスをダンプする

配列のリファレンスをダンプしてみましょう。Data::Dumperモジュールを読み込み、Dumper関数を使います。

Dumper関数の引数は、配列のリファレンスを渡します。

Dumper関数は、文字列化されたデータの情報を返すので、warn関数を使って、標準エラー出力に、出力しています。

配列のリファレンスの情報をダンプする「dump_array_ref.pl」です。

use strict;
use warnings;

# Data::Dumperモジュールを読み込み
use Data::Dumper;

# 配列のリファレンス
my $nums = [3, 5, 6];

# 配列のリファレンスのデータをダンプする
warn Dumper $nums;

実行してみましょう。

perl dump_array_ref.pl

配列のリファレンスの内容が出力されます。

$VAR1 = [
          3,
          5,
          6
        ];

ハッシュをダンプする

ハッシュをダンプしてみましょう。Data::Dumperモジュールを読み込み、Dumper関数を使います。

Dumper関数の引数は、リファレンスを渡すのがお勧めな方法なので「\」を使って、ハッシュのリファレンスを作成しています。

Dumper関数は、文字列化されたデータの情報を返すので、warn関数を使って、標準エラー出力に、出力しています。

ハッシュの情報をダンプする「dump_hash.pl」です。

use strict;
use warnings;

# Data::Dumperモジュールを読み込み
use Data::Dumper;

# ハッシュ
my %book = (id => 1, name => 'Perl Book');

# ハッシュのデータをダンプする
warn Dumper \%book;

実行してみましょう。

perl dump_hash.pl

ハッシュの内容が出力されます。

$VAR1 = {
          'name' => 'Perl Book',
          'id' => 1
        };

ハッシュのリファレンスをダンプする

ハッシュのリファレンスをダンプしてみましょう。Data::Dumperモジュールを読み込み、Dumper関数を使います。

Dumper関数の引数は、リファレンスを渡すのがお勧めな方法なので「\」を使って、ハッシュのリファレンスのリファレンスを作成しています。

Dumper関数は、文字列化されたデータの情報を返すので、warn関数を使って、標準エラー出力に、出力しています。

ハッシュのリファレンスの情報をダンプする「dump_hash_ref.pl」です。

use strict;
use warnings;

# Data::Dumperモジュールを読み込み
use Data::Dumper;

# ハッシュのリファレンス
my $book = {id => 1, name => 'Perl Book'};

# ハッシュのリファレンスのデータをダンプする
warn Dumper $book;

実行してみましょう。

perl dump_hash_ref.pl

ハッシュのリファレンスの内容が出力されます。

$VAR1 = {
          'name' => 'Perl Book',
          'id' => 1
        };

日本語のデータをダンプする

日本語のデータをダンプするには、どうすればよいのでしょうか? 実は、Perlには、日本語のデータを簡単にダンプする機能が標準関数や標準モジュールとして含まれていません。

これでは少し不便ですね。こういう場合は、その機能を実現するPerlのモジュールをCPANというサイトからダウンロードしてインストールできます。

CPANからダウンロードするコマンドは「cpanm」あるいは「cpan」です。cpanmがある場合は、cpanmでインストールするのがお勧めです。

ここでは、日本語のデータをダンプできる「D」モジュールをインストールしてみましょう。

# cpanmの場合
cpanm D

# cpanの場合
cpan D

Dモジュールのインストールが終わったら次のように、日本語データをダンプしてみましょう。

Windowsで日本語を含むデータをダンプする

Windowsの場合は「use D;」でモジュールを読み込んだ後に、「dw」というコマンドでダンプできます。Perlの内部文字列を、cp932でエンコードして出力します。ソースコードの文字コードはUTF-8で保存してください。

プログラム名は「dump_data_win.pl」です。

use strict;
use warnings;
use utf8;

# ハッシュのリファレンス
my $book = {id => 1, name => 'モジガエルのPerl入門'};

# Windowsで日本語を含んだPerlのデータをダンプする
use D;dw $book;
use strict;
use warnings;
use utf8;

# ハッシュのリファレンス
my $book = {id => 1, name => 'モジガエルのPerl入門'};

# Windowsで日本語を含んだPerlのデータをダンプする
use D;dw $book;

実行してみましょう。

perl dump_data_win.pl

次のように日本語が正しくダンプされました。

{
  'id' => '1',
  'name' => 'モジガエルのPerl入門'
} at dump_data_win.pl line 9.

Mac、Linux/UNIXで日本語を含むデータをダンプする

Mac、Linux/UNIXの場合は「use D;」でモジュールを読み込んだ後に、「du」というコマンドでダンプできます。Perlの内部文字列を、UTF-8でエンコードして出力します。ソースコードの文字コードはUTF-8で保存してください。

プログラム名は「dump_data_unix.pl」です。

use strict;
use warnings;
use utf8;

# ハッシュのリファレンス
my $book = {id => 1, name => 'モジガエルのPerl入門'};

# Mac、Linux/UNIXで日本語を含んだPerlのデータをダンプする
use D;du $book;
use strict;
use warnings;
use utf8;

# ハッシュのリファレンス
my $book = {id => 1, name => 'モジガエルのPerl入門'};

# Mac、Linux/UNIXで日本語を含んだPerlのデータをダンプする
use D;du $book;

実行してみましょう。

perl dump_data_unix.pl

次のように日本語が正しくダンプされました。

{
  'id' => '1',
  'name' => 'モジガエルのPerl入門'
} at dump_data_unix.pl line 9.

ハッシュの配列というデータ構造をPerlで表現する

ハッシュの配列というデータ構造をPerlで表現してみましょう。ついに、ここに戻ってきました!

ハッシュの配列というデータ構造の作成

Perlでは、多次元データ構造は、リファレンスを使って表現します。

ハッシュの配列を作成してダンプするサンプルコード「dump_array_of_hash.pl」を書いてみます。

use strict;
use warnings;
use Data::Dumper;

# Perlでハッシュの配列というデータ構造を表現
# 配列のリファレンスとハッシュのリファレンスを使います。
my $books = [
  {
    id => 1,
    name => 'Perl Boook',
    price => 2900,
  },
  {
    id => 2,
    name => 'Web Development',
    price => 2000,
  },
  {
    id => 3,
    name => 'Database',
    price => 1900,
  }
];

warn Dumper $books;

実行してみましょう。

perl dump_array_of_hash.pl

出力結果です。ハッシュの配列がダンプされます。

$VAR1 = [
          {
            'id' => 1,
            'name' => 'Perl Boook',
            'price' => 2900
          },
          {
            'name' => 'Web Development',
            'id' => 2,
            'price' => 2000
          },
          {
            'name' => 'Database',
            'id' => 3,
            'price' => 1900
          }
        ];

ハッシュの配列をループする

ハッシュの配列をループしてみます。書籍の価格に消費税10%を追加してみましょう。この部分の処理は、JSONでデータを読み込んだ場合の処理と全く同じです。

ハッシュの配列をループする場合は、扱っているデータがリファレンスであることに注意します。

forループに渡すときは、配列のリファレンスをデリファレンスして配列にします。

ハッシュのリファレンスから値にアクセスする場合は、アロー演算子「->」を使います。

ハッシュの配列をループする「loop_array_of_hash.pl」です。

use strict;
use warnings;
use Data::Dumper;

# 書籍のデータ
my $books = [
  {
    id => 1,
    name => 'Perl Boook',
    price => 2900,
  },
  {
    id => 2,
    name => 'Web Development',
    price => 2000,
  },
  {
    id => 3,
    name => 'Database',
    price => 1900,
  }
];

# 配列のリファレンスをデリファレンスしてforループに渡す
for my $book (@$books) {
  # アロー演算子「->」で値を取得
  my $id = $book->{id};
  my $name = $book->{name};
  my $price = $book->{price};
  
  # 消費税10%を加算
  $book->{price} *= 1.1;
}

# データをダンプする
warn Dumper $books;

実行してみましょう。

perl loop_array_of_hash.pl

出力結果です。消費税10%が加算されていますね。

$VAR1 = [
          {
            'name' => 'Perl Boook',
            'price' => '3190',
            'id' => 1
          },
          {
            'name' => 'Web Development',
            'price' => '2200',
            'id' => 2
          },
          {
            'id' => 3,
            'price' => '2090',
            'name' => 'Database'
          }
        ];

演習問題: CSVファイル読込、正規表現で検索・置換、JSONデータ出力

さぁ、Perlテキスト処理のエッセンスの総まとめとして、CSVデータをファイルから読み込んで、正規表現で検索・置換して、JSONデータで出力するということをやってみましょう。

この処理が、自然に書けるようになれば、この書籍で学んだことが、実践できていると思います。

では、書籍情報のCSVファイルを読み込んで、JSONファイルを出力する処理を書いてみてください。

次の機能を実現するプログラムを書いてください。

1. 検索条件は「書名が『モジガエル』で始まる、あるいは『Webシステム開発』で終わる」かつ「発売日が2020年以降」

2. 価格は20%割引

3. 発売日は「2021年1年3日」というフォーマットから「2021-01-03」というフォーマットに変換

これまでに説明していませんが、回答では、sprintfというPerlの標準関数を使っています。sprintf関数の使い方のサンプルを書きますので、これを使って、発売日のフォーマット変換にチャレンジしてみてください。

# 4桁-2桁-2桁にフォーマットする
my $formatted_string = sprintf("%04d-%02d-%02d", $year, $mon, $mday);

CSVデータ形式の入力ファイル

入力のCSVファイルです。UTF-8で保存してください。

ID,書名,著者名,価格,発売日
1,モジガエルのPerlテキスト処理プログラミング入門,木本裕紀,2900, 2021年1年3日
2,PerlでWebシステム開発,木本裕紀,2000,2020年12年6日
3,データベース入門,田中太郎,1900,2020年5年6日
4,モジガエルの正規表現テクニック,田中太郎,3000,2019年7年31日

JSONデータ形式の出力ファイル

以下のJSONの出力ファイルを出力してください。改行は見やすくするために入れていますが、内容があっていればOKです。

[
  {
    "id" : 1,
    "name" : "モジガエルのPerlテキスト処理プログラミング入門",
    "author_name" : "木本裕紀",
    "price" : 2320,
    "issued_date" : "2021-01-03"
  },
  {
    "id" : 2,
    "name" : "PerlでWebシステム開発",
    "author_name" : "木本裕紀",
    "price" : 1600,
    "issued_date" : "2020-12-06"
  },
]

演習問題の答え合わせ

答え合わせです。書籍のCSVファイルを処理してJSONに変換する「csv_to_json.pl」です。上手に書けたかな。

use strict;
use warnings;
use utf8;
use Encode 'encode', 'decode';
use JSON::PP 'encode_json', 'decode_json';

# CSVファイルを読み込んでPerlのデータ構造を作成する
my $books = [];
while (my $line = <>) {
  # 1行目のヘッダを飛ばす $.は行番号
  if ($. == 1) {
    next;
  }
  
  # 改行の削除(Windows, Mac, Linux/UNIX対応)
  $line =~ s/\r?\n//;
  
  # Perlの内部文字列へ変換
  $line = decode('UTF-8', $line);

  # CSVデータをPerlの変数へ
  my ($id, $name, $author_name, $price, $issued_date)
    = split(/,/, $line);
  
  # 価格を20% off
  my $price_off = $price * 0.8;
  
  # 発行日の日付を
  #「2021年1年3日」のようなフォーマットから「2021-01-03」のようなフォーマットへ
  my $issued_date_hyphen;
  if ($issued_date =~ /^(\d+)年(\d+)月(\d+)日$/) {
    my $year = $1;
    my $mon = $2;
    my $mday = $3;
    
    $issued_date_hyphen = sprintf("%04d-%02d-%02d", $year, $mon, $mday);
  }
  else {
    # フォーマットが間違っている場合は、警告を出して、次の処理へ
    # $. には行番号が含まれている
    warn("Invalid issued date format at line $.");
    next;
  }
  
  # 条件にマッチしたときだけ追加
  # 検索条件は「書名が『モジガエル』で始まる、あるいは『Webシステム開発』で終わる」
  # かつ「発売日が2020年以降」
  my $match;
  if (
    ($name =~ /^モジガエル/ || $name =~ /Webシステム開発$/)
    && $issued_date_hyphen ge '2020'
  ) {
    # マッチ
    $match = 1;
  }
  
  # マッチした場合は追加
  if ($match) {
    # 書籍情報を作成
    my $book = {
      id => $id,
      name => $name,
      author_name => $author_name,
      price => $price_off,
      issued_date => $issued_date_hyphen,
    };
    
    # 書籍を配列のリファレンスの最後の要素に追加
    push @$books, $book;
  }
}

# データの中身をダンプしてみたい場合
# Windows
# use D;dw $books;
# Mac, Linux/UNIX
# use D;du $books;

# Perlのデータ構造をJSONデータへ変換
my $books_json = encode_json $books;

# JSONデータを出力
print $books_json;

次のように実行してください。

perl csv_to_json.pl csv_to_json_in.csv > csv_to_json_out.json

出力結果です。

[{"name":"モジガエルのPerlテキスト処理プログラミング入門","price":2320,"id":"1","issued_date":"2021-01-03","author_name":"木本裕紀"},{"issued_date":"2020-12-06","author_name":"木本裕紀","id":"2","price":1600,"name":"PerlでWebシステム開発"}]

JSON::PPのencode_jsonモジュールは、最小サイズになるJSON出力を行うので、見にくいですが、以下の条件で出力されていることを確認してください。

1. 検索条件は「書名が『モジガエル』で始まる、あるいは『Webシステム開発』で終わる」かつ「発売日が2020年以降」

2. 価格は20%割引

3. 発売日は「2021年1年3日」というフォーマットから「2021-01-03」というフォーマットに変換

お疲れさまでした。

Perlプログラミングの今後の目標

テキスト処理プログラミングでプログラミングの基礎を終えた後にチャレンジできる分野について書いておきます。

Linux/UNIXサーバー管理

PerlはUNIX上で誕生したプログラミング言語で、UNIXやその派生であるLinux上でのサーバー管理に適したプログラミング言語です。WindowsやMacでPerlを試した後に、Linux/UNIX上でPerlを使うことにチャレンジするのもよいでしょう。

Webシステム開発

Perlテキスト処理プログラミングの次の目標として、Webシステム開発にチャレンジしてみるのもよいでしょう。Webの中心的な処理は、HTTP上におけるテキスト処理なので、テキスト処理が得意なPerlとの相性はピッタリです。

サンプルコードのダウンロード

Perlテキスト処理のエッセンスで紹介したサンプルコードは、すべてPerlクラブのサイトからダウンロードして試すことができます。

Perlテキスト処理のエッセンス サンプルコードのダウンロード

Perlクラブで会員登録を行うと、サンプルがすべてダウンロードできます。

https://perlclub.net

Perlテキスト処理のエッセンス

 

発行日

 2021年6月1日 初版第1刷発行

 2021年7月6日 初版第2刷発行

 2022年2月1日 第2版第1刷発行

著者

 Perlクラブ(株)

 

装丁

 MNdesign

 

発行者・発行所

 Perlクラブ(株)

 〒104-0061

 東京都中央区銀座7丁目17番8号銀座松良ビル5階

 電話 03-6281-5440

 https://perlclub.net/

 

業務に役立つPerl

Perlテキスト処理のエッセンス 書籍版

関連情報