日本語CSVファイル処理のプログラム
CSVとは、カンマで区切られたデータ形式のことです。
ID,書名,著者名,価格 1,Perlテキスト処理プログラミング入門,木本裕紀,2900 2,Perl Web開発入門,木本裕紀,2000 3,データベース入門,田中太郎,1900 4,正規表現テクニック,田中太郎,3000
CSV形式は、実務でもよく使われ、基本的な学習にぴったりなデータ形式です。ファイルに記述されたこのようなカンマ区切りのCSVデータを、プログラムで処理できるようになることが、この章の目標です。
ではさっそく、日本語CSVファイル処理を行うプログラムをPerlで書いて実行してみましょう。
CSV形式の入力データファイル
まず最初に、CSV形式の入力データファイルを作成してみましょう。ファイル名は「tut_input.csv」にします。
ID,書名,著者名,価格 1,Perlテキスト処理プログラミング入門,木本裕紀,2900 2,Perl Web開発入門,木本裕紀,2000 3,データベース入門,田中太郎,1900 4,正規表現テクニック,田中太郎,3000
文字コードはUTF-8で保存します。エディタによって若干違いがあるのですが、ファイル保存するダイアログの下の方に「文字コード」という項目があると思います。ファイルを保存する前に、この部分で「UTF-8」を指定すると、UTF-8で保存できます。
UTF-8は、ユニコードと呼ばれる文字体系のひとつの符号化方式で、Webにおけるデータ交換用の文字コードとして広く使われています。
CSVファイル処理の内容
CSVファイルを入力で受け取って、プログラムで処理をします。どんな処理をするかを、ここで書き出してみます。
・入力データ「tut_input.csv」を読み込む
・田中さんの書籍だけを取り出す
・書名に含まれる「入門」という言葉を「基礎」に変換する
・「tut_output.csv」という名前のファイルに出力
上記の処理を行ったら出力ファイル「tut_output.csv」には、どのような内容が出力されるか、想像してみてください。
予想される出力データ
予想される出力データは以下のようになります。
3,データベース基礎,田中次郎,1900 4,正規表現テクニック,田中次郎,3000
出力ファイルには、田中さんの書籍だけが含まれています。「データベース入門」は「データベース基礎」に変換されています。
日本語CSVファイル処理するPerlプログラム
日本語CSVファイル処理するPerlプログラムです。ファイル名は「tut_example1.pl」です。Perlプログラムの文字コードはUTF-8で保存します。
少し長いですが、頑張って書いてみましょう。基本的な内容が詰まったプログラムです。
use strict; use warnings; use utf8; use Encode 'encode', 'decode'; # 引数に指定したファイルから行を一行ずつ読み込む while (my $line = <>) { # PerlのUTF-8を内部文字列へデコード $line = decode('UTF-8', $line); # 改行を削除 chomp $line; # カンマで分割して配列へ my @items = split(/,/, $line); # 本の情報をハッシュへ保存 my %book; $book{id} = $items[0]; $book{name} = $items[1]; $book{author} = $items[2]; $book{price} = $items[3]; # 著者名に田中が含まれていたら if ($book{author} =~ /田中/) { # 書名の入門を基礎に置換 $book{name} =~ s/入門/基礎/; # 出力するための配列を作成 my @output_items = ( $book{id}, $book{name}, $book{author}, $book{price} ); # 出力行を作成 my $output_line = join(',', @output_items); # Perlの内部文字列をUTF-8にエンコードして出力 print encode('UTF-8', $output_line) . "\n"; } }
Perlのプログラムを次のように実行してみましょう。perlコマンドの第一引数に「プログラム名」、第二引数に「入力ファイル名」を指定します。出力ファイルは、リダイレクト記号「>」の後ろに記述します。
perl tut_example1.pl tut_input.csv > tut_output.csv
うまくプログラムが実行できましたか? 出力ファイルが、予想した出力と合っているか確認してみましょう。慣れてくるまで、何度かプログラムを書いてみましょう。
では、ここから、このプログラムを理解するための、解説を行っていきます。
画面に文字列を表示する
まず最初に、プログラミングのABCである「Hello World!」を画面に表示してみましょう。
画面に文字を表示するには、print文を使います。「Hello World」という文字列を表現するにはダブルクォート「"」で文字「Hello World」を囲みます。文字の並びをプログラミングでは、文字列といいます。Perlプログラムの文の最後はセミコロン「;」で終わります。
print "Hello World";
これを「hello.pl」という名前で保存してください。
プログラミングを実行するには、perlコマンドを使用します。
perl hello.pl
画面に以下のように表示されます。
Hello World
「Hello World!」の後ろで改行されずに、何か文字が続いていると思います。これは、文字列の最後に改行がないためです。改行については、この後で、解説します。
Perlの文法チェック
「-c」オプションを使うと、Perlの文法チェックだけを行うことができます。プログラムは実行されません。Perlの文法チェックだけを先に行いたいときに便利です。
perl -c hello.pl
「syntax OK」と表示されれば、Perlの文法が正しいことが確認できます。
hello.pl syntax OK
改行
Perlプログラムで改行をするには「/n」を使います。「/」はバックスラッシュですが、Windows環境であれば円マーク「¥」になっています。
print "Hello World\n";
このようなバックスラッシュで始まる特殊な文字をエスケープシーケンスといいます。ダブルクォート「"」で囲まれた文字列の中で「\n」と書くと改行を意味し、これはエスケープシーケンスのひとつです。
これを「hello_newline.pl」という名前で保存してください。
プログラミングを実行するには、perlコマンドを使用します。
perl hello_newline.pl
画面に以下のように表示されます。文字列の末尾で改行されています。
Hello World
コメント
Perlのコメントについて解説します。
1行コメント
Perlのコメントは「#」で始まります。コメントは、プログラムとして意味をもちません。
# コメント
プログラムには、他の人が読むことを想定して、意図を表現した、わかりやすく、短いコメントをつけるのがお勧めです。以下は、コメントの例です。
# foo.comのアクセス回数をカウントする my $accesss_count = 0; if ($line =~ /foo\.com/) { $access_count++; }
複数行コメント
Perlには複数行コメントの文法が存在しませんが、ドキュメントを記述するPODと呼ばれる記法を使うと、複数行コメントが書けます。
=pod コメントにしたい文章 コメントにしたい文章 =cut
一時的に、プログラムの一部をコメントしたい場合に便利です。
Perlの約束事
Perlの約束事です。Perlプログラムの先頭には、文法チェックを厳格にし、警告を表示するように、以下の二行を追加します。
use strict; use warnings;
「use strict」の代表的な効果としては、変数宣言がされていない場合に、コンパイルが失敗します。
「use warnings」の代表的な効果としては、未定義値が意図しない場所で利用されていた場合に、警告が発生します。
変数
変数について解説します。
変数宣言
変数とは、値を保存しておく箱のようなものです。Perlの変数はmyを使って宣言します。変数名の先頭はドル「$」で始まります。
# 変数宣言 my $num;
「$」で始まる変数名は、正式にはスカラ変数と呼ばれます。スカラ変数は、一つの値を代入できる変数のことです。Perlには、配列変数とハッシュ変数と呼ばれるものがありますが、それは後で解説します。
有効な変数名
変数名は、小文字、大文字、数字、アンダースコア「_」で構成することができます。変数名の先頭は数字以外で始まる必要があります。
# 有効な変数名 my $book_name; my $BOOK; my $author2;
推奨される変数名
Perlの慣習として、推奨される変数名は、小文字とアンダースコア「_」で構成されるスネークケースと呼ばれる表現です。慣習的に広く利用されている書き方と同じ書き方をしておくと、他の人が読みやすくなるというメリットがあります。
# Perlの慣習として推奨される変数名 my $num; my $book_name; my $access_count; # Perlの慣習として推奨されない変数名 my $Num; my $BookName; my $AccessCount;
変数名の付け方のコツは「他の変数と区別ができること」と「わかりやすく意味のある名前を付けること」です。変数がプログラムを解説してくれて、簡単なコメントがあれば、プログラムが読めるというのが、良いですね。
変数の宣言と初期化を同時に行う
変数の宣言と初期化を同時に行うことができます。
# 変数の宣言と初期化を同時に行う my $num = 1;
数値
数値の表現について解説します。
整数リテラル
整数リテラルを使って整数を表現できます。整数リテラルは、ソースコード上での整数の表現です。
# 整数リテラル 234 3987
整数リテラルでは、アンダースコアを桁区切りとして利用することもできます。
# 整数リテラル(桁区切りあり) 100_000_000 4_432_000
整数リテラルは、スカラ変数に代入できます。
# 数値リテラルを変数に代入 my $num = 234; my $num = 100_000_000;
浮動小数点リテラル
浮動小数点リテラルを使って浮動小数点を表現できます。浮動小数点リテラルは、ソースコード上での浮動小数点の表現です。
# 浮動小数点リテラル 1.234 12.65
浮動小数点リテラルは、スカラ変数に代入できます。
# 浮動小数点リテラルを変数に代入 my $num = 12.65;
Perlの数値は、プログラムの表現上は、64bit浮動小数点として扱われると考えてください。整数の場合は内部的には最適化のために整数として保持されるという場合があるのですが、プログラムからみた場合は、Perlの数値は、64bit浮動小数点と考えてください。
数値の計算
Perlで数値の計算を行ってみます。足し算「+」、引き算「-」、掛け算「*」、割り算「/」の計算です。
# 足し算 結果は2 my $add = 1 + 1; # 割り算 結果は2 my $subtract = 3 - 1; # 掛け算 結果は6 my $multiply = 3 * 2; # 割り算 結果は0.5 my $division = 1 / 2;
余りと商の求め方を紹介しておきます。商を求めるには普通の割り算を行った後に、int関数で整数部を取り出します。
# 商 結果は2 my $div = int(5/2); # 余り 結果は1 my $mod = 5 % 2;
演算子の優先順位
演算子の優先順位は「掛け算、割り算」が「足し算、引き算」に優先します。
# 掛け算が優先 結果は17 my $num = 2 + 3 * 5;
優先順位を変えたい場合や、はっきりさせたい場合は、丸かっこを使います。
# 丸かっこが優先 結果は25 my $num = (2 + 3) * 5;
数値計算のサンプルプログラム
数値計算を使ったサンプルプログラム「calculate.pl」です。数値を変数に保存して、四則演算を行っています。Perlでプログラムを書く場合は、「use strict;」「use warnings;」を先頭に書きましょう。
use strict; use warnings; # 数値を計算する my $num1 = 1 + 2 * 3; my $num2 = 4 * 5; my $num3 = $num1 + $num2; print "$num3\n";
実行してみましょう。
perl calculate.pl
出力結果です。計算結果が出力されます。
27
文字列
文字列について解説します。文字列とは、文字の並びを意味します。文字列は、シングルクォート「'」で囲む方法と、ダブルクォート「"」で囲む方法の二種類の表現方法があります。
シングルクォート文字列
シングルクォート文字列は、文字列を単なる文字列として、扱いたい場合に使います。
# シングルクォートで囲った文字列 'Hello';
シングルクォート文字列は、スカラ変数に代入できます。
my $message = 'Hello';
ダブルクォート文字列
ダブルクォートを使って、文字列を表現できます。
# ダブルクォートで囲った文字列 "Hello";
ダブルクォート文字列は、スカラ変数に代入できます。
my $message = "Hello";
ダブルクォートで囲った文字列の中では、エスケープシーケンスと変数展開を使うことができます。
エスケープシーケンス
エスケープシーケンスとは、改行「\n」やタブ「\t」などの特別な文字のことです。
# タブと改行をエスケープシーケンスで表現 my $message = "Foo\tBar\n";
変数展開
変数展開とは、変数の内容が文字列として展開される機能のことです。
my $name = 'mojigaeru'; # 変数展開が行われる「I am mojigaeru」となる my $message = "I am $name";
文字列の連結
文字列を連結するには、文字列連結演算子「.」を使用します。
# 「abcdef」になる my $concat = 'abc' . 'def';
文字列のサンプルプログラム
文字列を使ったサンプルプログラムです。
数値計算を使ったサンプルプログラム「string.pl」です。文字列を連結して表示するサンプルです。Perlでプログラムを書く場合は、「use strict;」「use warnings;」を先頭に書きましょう。
use strict; use warnings; # 変数展開を使ってメッセージを作成 my $fruit0 = 'orange'; my $fruit1 = 'banana'; my $fruit2 = 'apple'; my $message = "I like $fruit0, $fruit1, and $fruit2."; # メッセージの末尾に新しいメッセージを追加 $message = $message . "Hello!"; # 改行をつけて出力 print $message . "\n";
実行してみましょう。
perl string.pl
出力結果です。メッセージが出力されます。
I like orange, banana, and apple.Hello!
配列
Perlの配列について解説します。
配列とは
配列とは、複数の値を代入できる変数のことです。スカラ変数が「$」で始まったのに対して、配列の先頭は「@」で始まります。「$」や「@」のことをシジルと呼びます。
# 配列の宣言 my @nums;
配列は、リストを使って初期化できます。リストとは「(」と「)」で囲まれた、カンマで区切られた値の並びのことです。
# 配列の宣言 my @nums = (5, 4, 3);
配列の要素には、文字列を代入することもできます。
# 文字列の配列 my @strs = ('foo', 'bar', 'baz');
文字列と数値を混在させることもできます。
# 数値と文字列が混在した配列 my @items = (1, 2, 'foo', 'bar');
Perlは動的型言語なので、配列の要素の型を意識する必要はありません。
配列の要素の取得と設定
配列の要素の取得と設定を行うには、以下の構文を使用します。インデックスは0から始まることに注意してください。配列の要素を取得・設定するときは「@」ではなく「$」を使うことに注意してください。
# 配列の要素の取得 $配列名[インデックス] # 配列の要素の設定 $配列名[インデックス] = 値
配列の要素の取得と設定を行うサンプルです。
# 配列の宣言 インデックスは0から数える my @nums = (5, 4, 3); # 配列の要素を取得 my $elem = $nums[2]; # 3 print "$elem\n"; # 配列の要素の設定 $nums[1] = 7; # 7 print "$nums[1]\n";
配列の要素の画面への出力
配列の要素を出力してみましょう。変数展開を使うと便利です。配列は変数展開すると、要素が空白で区切られた値に変換されます。
# 配列の宣言 my @nums = (5, 4, 3); print "@nums\n";
出力結果は以下のようになります。
5 4 3
ハッシュ
Perlのハッシュについて解説します。Perlのハッシュとは、キーと値の複数のペアを保存できる変数のことです。ソフトウェア用語では、連想配列と呼ばれているものです。
ハッシュの宣言
ハッシュの宣言です。ハッシュ名は「%」で始まります。
# ハッシュの宣言 my %book;
ハッシュへの代入
ハッシュにはキーと値のペアが入ったリストを代入することができます。この例では、本という名前のハッシュを宣言して「ID」「書名」「価格」という情報を持たせています。
# ハッシュへの代入 %book = (id => 1, name => 'Perl', price => '900');
ハッシュの宣言と同時に初期化することもできます。
# ハッシュの宣言と同時に初期化 my %book = (id => 1, name => 'Perl', price => '900');
Perlでは、「=>」は「,」と同じ意味です。「=>」はファットカンマと呼ばれます。「=>」の左側の文字列は「a-zA-Z0-9_」で構成されている場合は、シングルクォートやダブルクォートを省略できます。
# ファットカンマを使ってハッシュの中身を表現 %book = (id => 1, name => 'Perl', price => '900'); # 上記は、以下と同じ意味 %book = ('id', 1, 'name', 'Perl', 'price', '900');
ハッシュの値の取得と設定
ハッシュの値の取得と設定を行うには、以下の構文を使用します。キー名は文字列です。ハッシュの要素を取得・設定するときは「%」ではなく「$」を使うことに注意してください。
# ハッシュの要素の取得 $ハッシュ名{キー名} # ハッシュの要素の設定 $ハッシュ名{キー名} = 値
ハッシュの値の取得と設定のサンプルです。
# ハッシュの値の取得 $book{'name'}; $book{'price'}; # ハッシュの値の設定 $book{'name'} = 'Database'; $book{'price'} = 2000;
ハッシュのキー名は文字列ですが「a-zA-Z0-9_」で構成されている場合は、シングルクォートやダブルクォートで囲む必要はありません。
# ハッシュのキーを、シングルクォートやダブルクォートで囲まない記法 $book{name}; $book{price}; $book{name} = 'Database'; $book{price} = 2000;
慣習としては、シングルクォートやダブルクォートで囲まない記法が、多く利用されています。
if文を使った条件分岐
条件を指定して、実行する処理を切り替えることを、条件分岐といいます。条件分岐はif文を使って行うことができます。
本の価格が1000円以下だったら
本の価格が、1000円以下だったら「Low Price」と画面に出力するというサンプル(if_price_under_1000.pl)を書いてみましょう。
use strict; use warnings; # 本の価格が1000円以下だったら「Low Price」と画面に出力 my $book_price = 900; if ($book_price <= 900) { print "Low Price\n"; }
ifの丸かっこ「()」の中に条件を書くことができます。「<=」は、○○以下を表す数値比較演算子です。条件が真になった場合は、ブロック「{}」の中の処理が実行されます。条件が真というのは、条件が満たされたという意味だと考えてください。
実行してみましょう。
perl if_price_under_1000.pl
出力結果です。
Low Price
数値比較演算子
数値比較演算子の一覧です。
演算子 | 意味 |
A == B | AとBは等しい |
A != B | AとBは等しくない |
A > B | AはBより大きい |
A >= B | AはB以上 |
A < B | AはBより小さい |
A <= B | AはB以下 |
本の名前がPerlだったら
本の名前が「Perl」だったら「Name is Perl」と画面に出力するというサンプル(if_name_eq_perl.pl)を書いてみましょう。
use strict; use warnings; # 本の名前が「Perl」だったら「Name is Perl」と画面に出力 my $book_name = 'Perl'; if ($book_name eq 'Perl') { print "Name is Perl\n"; }
「eq」は、文字列が○○と等しいを表す文字列比較演算子です。Perlでは数値比較演算子と文字列比較演算子が区別されているので、注意してください。条件が真になった場合は、ブロック「{}」の中の処理が実行されます。
実行してみましょう。
perl if_name_eq_perl.pl
出力結果です。
Name is Perl
文字列比較演算子
文字列比較演算子の一覧です。文字列比較演算子において、文字列の大小は、辞書順比較になります。
演算子 | 意味 |
A eq B | AとBは等しい |
A ne B | AとBは等しくない |
A gt B | AはBより大きい |
A ge B | AはB以上 |
A lt B | AはBより小さい |
A le B | AはB以下 |
本の名前にPerlを含んでいたら
本の名前にPerlを含んでいたら「This is Perl Book.」と出力するif文を使ったサンプルです。正規表現と呼ばれる文法を使います。
use strict; use warnings; # 本の価格が1000円以下だったら「Low Price」と画面に出力 my $book_name = 'Perl Tutorial'; if ($book_name =~ /Perl/) { print "This is Perl Book.\n"; }
「=~」はパターンマッチ演算子と呼ばれ、正規表現にマッチした場合に真になります。
繰り返し
Perlの繰り返し処理の解説です。Perlでは、繰り返しを行うときに、for文あるいはwhile文を使います。
for文
Perlのfor文は、二種類の構文があります。
C言語風のfor文
C言語風のfor文です。繰り返しを行う基本的な構文です。ループ変数の初期化、繰り返し条件、ループ変数の更新を記述します。
# for文 - C言語風for文 for (ループ変数の初期化; 繰り返し条件; ループ変数の更新) { # 処理 }
C言語風のfor文を使ったサンプル「loop_for_c.pl」です。ループ変数を0から始めて、5より小さい間ループします。ループ変数はインクリメントの構文「$i++」を使って、ループが実行されるたびに、1づつ増加させています。
use strict; use warnings; # for文 - C言語風for文 for (my $i = 0; $i < 5; $i++) { print "$i\n"; }
実行してみましょう。
perl loop_for_c.pl
出力結果です。0~4が順番に出力されています。
0 1 2 3 4
配列の要素を順番に処理するfor文
配列を順番に処理するfor文です。
# for文 - 配列の要素を順番に処理するfor文 for 要素を代入する変数 (配列) { # 処理 }
配列の要素を順番に処理するfor文のサンプル「loop_for_iterate.pl」です。
use strict; use warnings; # for文 - 配列の要素を順番に処理するfor文 my @nums = (4, 6, 9); for my $num (@nums) { print "$num\n"; }
実行してみましょう。
perl loop_for_iterate.pl
出力結果です。配列の要素が順番に出力されています。
4 6 9
foreach文
Perlにはforeach文というものがあるのですが、for文とまったく同じ意味です。
while文
while文を使って、ループすることもできます。while文はループの原始的な構文です。while文の丸かっこ「()」の中には条件を書くことができます。while文の条件が真の場合にブロック「{}」の中の処理が実行されます。
while (繰り返し条件) { # 処理 }
while文を使ったループのサンプル「loop_while.pl」です。
use strict; use warnings; # while文 my $i = 0; while ($i < 10) { # 処理 print "$i\n"; $i++; }
実行してみましょう。
perl loop_while.pl
出力結果です。0~4の値が順番に出力されました。
0 1 2 3 4
Perl標準関数
関数の使い方について学びましょう。関数は、特定の機能を便利に使える道具のようなものだと考えてください。Perlには、標準関数と呼ばれる、デフォルトで使える関数が用意されています。
chomp関数 - 改行削除
chomp関数は、改行を削除するための関数です。Perlプログラミングでは、ファイルから行を読み込んだときに、まず改行を削除するということを行います。
# 改行を削除 chomp $line;
Windows、Unix/Linux/Macの場合は、chompで削除される改行コードは「\n」です。
OSに依存しないで改行コードを削除する方法
OSに依存しないで改行コードを削除する方法について解説しておきます。この部分は、読み飛ばしてもかまいません。
WindowsとUNIXの改行コードという二つのOSに絞って話をします。LinuxとMacはUNIXをベースにしているので、UNIXであると考えてください。
OSによって異なる改行コードを、Perlは抽象化して扱おうとします。
Windowsの改行コードとUNIXの改行コード
Windowsの改行コードはASCIIコードにおける「CRLF」です。
UNIXの改行コードはASCIIコードにおける「LF」です。
テキストファイルを保存した場合は、一般的には、上記の改行コードで保存されます。ただし、異なる改行コードで保存することもできます。UNIXからWindowsへファイルを移動する場合、WindowsからUNIXへファイルを移動する場合は、OSの改行コードと、テキストファイルに含まれる改行コードは、異なってしまいます。
このような場合に、実務として問題になってくるのは、どのようにして改行コードを削除するかということです。
OSに依存しないで改行コードを削除するコード
WindowsとUNIXの改行コードの違いに依存しないで、改行コードを削除する正しいコードをここに書きます。正規表現の知識が必要になるので、正規表現の章を読んでから、ここに戻ってきてもよいでしょう。
# WindowsとUNIXの改行コードの違いに依存しないで改行コードを削除する $line =~ s/\x0D?\x0A$//;
CRは、ASCIIコードの16進数で「0D」です。LFは、ASCIIコードの16進数で「0A」です。Perlでは「\x0D」のようにエスケープシーケンスを使ってASCIIコードを16進数で表現できます。
正規表現の意味は「末尾のCRLF、または、末尾のLFを削除する」です。この正規表現でWindowsとUNIXの改行コードの違いに依存しないで改行コードを削除することができます。
CRまたはCRLFを出力する
print関数で、出力する場合に、改行コードを指定したい場合があります。このような場合は、次のようにします。
# LFを出力したい場合 print "$line\x0A"; # CRLFを出力したい場合 print "$line\x0D\x0A";
split関数 - 区切り文字を指定して配列に変換する
split関数は、指定した区切り文字で対象の文字列を分割し、配列に変換する関数です。split関数の第一引数には、区切り文字を正規表現「//」を指定できます。第二引数には、対象の文字列を指定します。
split 区切り文字の正規表現, 文字列
split関数のサンプル「split.pl」です。
use strict; use warnings; # カンマ区切りのデータ my $csv = '1,Perl,Mojigaeru,2900'; # カンマ区切りのデータを配列に変換 my @items = split(/,/, $csv); # 配列の内容を出力 for my $item (@items) { print "$item\n"; }
実行してみましょう。
perl split.pl
出力結果です。
1 Perl Mojigaeru 2900
join関数 - 区切り文字で連結された文字列を取得
join関数を使うと、指定した区切り文字で、配列の各要素を連結した文字列を取得できます。join関数の第一引数は、区切り文字です。第二引数以降はリストです。
join 区切り文字, リスト
join関数のサンプル「join.pl」です。
use strict; use warnings; # 配列 my @items = (1, 'Perl', 'Mojigaeru', 2900); # join関数で、カンマで連結してCSVデータに変換 my $csv = join(',', @items); print "$csv\n";
実行してみましょう。
perl join.pl
出力結果です。
1,Perl,Mojigaeru,2900
日本語処理
Perlで日本語を扱う方法について解説します。
ソースコードはUTF-8で保存
Perlで正しく日本語を扱う場合は、ソースコードをUTF-8で保存する必要があります。UTF-8というのは、ユニコードの符号化方式のひとつです。
Windowsのメモ帳の場合は、名前を付けて保存を行うダイアログで「文字コード」を選択できるので「UTF-8」を選択します。他のエディタを使っている場合も、名前を付けて保存のダイアログの下の方に「文字コード」の選択があるので、「UTF-8」を選択します。
「use utf8;」の記述
「use utf8;」を記述します。「use utf8;」は、ソースコードがUTF-8で書かれていることをPerlに知らせるためのものです。これを記述することで、Perlは、ソースコードに書かれた文字列を内部的な文字列として扱ってくれます。内部的な文字列を、ここでは、内部文字列と呼ぶことにします。公式には、テキスト文字列と呼ばれ、デコードされた文字列と呼ばれることもあります。
use utf8;
Encodeモジュール
Encodeモジュールは、Perlで内部文字列とバイト文字列の変換を行うためのモジュールです。バイト文字列とは、実際の文字コードで表現されたバイト列のことです。モジュールというのは、プログラムの機能をまとめた部品のようなものだと考えてください。
内部文字列とバイト文字列の変換を行うために、Encodeモジュールのencode関数とdecode関数をインポートしましょう。インポートとは、モジュールで定義された関数を、関数名だけで呼び出せるようにする機能のことです。
# Encodeモジュールのencode関数とdecode関数をインポート use Encode 'encode', 'decode';
ファイルから入力するときはdecode関数
ファイルから入力するときはdecode関数を使って、実際の文字コードから、Perlの内部文字列へデコードします。たとえば、読み込む対象のファイルがUTF-8で書かれていたら、UTF-8を指定、cp932で書かれていたらcp932を指定します。
# 引数に指定したファイルから行を一行ずつ読み込む while (my $line = <>) { # PerlのUTF-8を内部文字列へデコード $line = decode('UTF-8', $line); # ... }
Perlでは、日本語は、プログラムの中においては、Perlの内部文字列に変換して扱います。Perlの内部文字列の表現はlatin-1あるいはUTF-8ですが、ここでは、詳細については、忘れてください。decode関数は、実際の文字コードをを、Perlが内部で扱う形式に変換する関数なのだということだけ、理解してください。
内部文字列への変換が行われると、文字列関数や正規表現で、日本語を正しく扱えるようになります。
ファイルへ出力するときはencode関数
ファイルへ出力するときは、encode関数を使って、Perlの内部文字列から、実際の文字コードへエンコードします。ファイルへ出力する直前でエンコードします。encode関数は、decode関数と反対の操作です。
# Perlの内部文字列をUTF-8にエンコードして出力 print encode('UTF-8', $output_line) . "\n";
Perlの日本語処理に対する混乱は、encode関数とdecode関数が、実際の文字コードから、実際の文字コードの変換を行っているように誤解してしまうことにあると思っています。これは、以前に頻繁に利用されていたjcode.plやJcode.pmが、実際の文字コードを、実際の文字コードに変換するライブラリだったからだと思います。
Perl 5.8以降の新しい文字列の扱いは、実際の文字コードを、Perlの内部文字列に変換するという仕組みであることを、しっかりと覚えておいてください。
日本語を出力するサンプル
著者名に木本を含んでいたら。「これは木本の著作です」と出力するサンプルです。
Windowsの場合のサンプル
Windowsの場合のサンプル「contain_jp_string_win.pl」です。ソースコードは、UTF-8で保存します。
use strict; use warnings; use utf8; use Encode 'encode'; # 著者の名前に木本を含んでいたら「これは木本の著作です。」と画面に出力 my $author_name = '木本裕紀'; if ($author_name =~ /木本/) { my $message = "これは木本の著作です"; print encode('cp932', $message) . "\n"; }
実行してみましょう。
perl contain_jp_string_win.pl
出力結果です。
これは木本の著作です
Linux/Unix/Macの場合のサンプル
Linux/Unix/Macの場合のサンプル「contain_jp_string_unix.pl」です。ソースコードは、UTF-8で保存します。
use strict; use warnings; use utf8; use Encode 'encode'; # 著者の名前に木本を含んでいたら「これは木本の著作です。」と画面に出力 my $author_name = '木本裕紀'; if ($author_name =~ /木本/) { my $message = "これは木本の著作です"; print encode('UTF-8', $message) . "\n"; }
実行してみましょう。
perl contain_jp_string_unix.pl
出力結果です。
これは木本の著作です
ファイル入出力
ファイル入出力を行う最も簡単な方法を紹介します。
リダイレクトを使ったファイルへの出力
ファイルへ出力するには、リダイレクトと呼ばれる方法を使います。リダイレクトを使うと、画面への出力をファイルへの出力に切り替えることができます。リダイレクトは、Perlの機能ではなくコマンドプロンプトやシェルの機能です。
「>」がリダイレクトの記号です。リダイレクトを使って、画面への出力を「tut_output.csv」というファイルへの出力に切り替えています。
perl tut_example1.pl tut_input.csv > tut_output.csv
ダイヤモンド演算子を使ったファイルからの入力
ダイヤモンド演算子「<>」を使うと、コマンドライン引数で指定したファイルから、1行づつ読み込むことができます。ダイヤモンド演算子は通称です。本当の名前は「行入力演算子」です。
# 引数に指定したファイルから行を一行ずつ読み込む while (my $line = <>) { }
一行づつ読み込んで、ファイルの末尾に到達すると、ダイヤモンド演算子は偽を返し、whileループは終了します。while文の条件部に「my」を使った変数宣言を書いていますが、Perlの変数宣言は値を返す式なので、このように書けます。
Windowsの場合は、行入力演算子で読み込むと、Windowsの改行コードであるCRLFからPerlでの改行を表現する文字である「\n」に変換されることに注意してください。
コマンドライン引数とは、プログラム名の後ろに続く部分のことです。ここにファイル名を指定します。「tut_input.csv」が、コマンドライン引数です。
perl tut_example1.pl tut_input.csv
ダイヤモンド演算子を使うと「tut_input.csv」のファイルの内容を一行づつ読み込むことができます。
一般的なファイル入出力
Perlには、open関数を使って、ファイル入出力を行う一般的な方法があります。興味のある方は「Perl ファイル入出力」などの用語で検索してみてください。Perlゼミのサイトでも解説しています。
最初のCSVファイル処理のサンプルをもう一度
これで最初のサンプル「tut_example1.pl」を理解するためのすべての解説が終わりました。最初のサンプルのソースコードをもう一度読んでみましょう。この章の内容を理解できていれば、読めるようになっていると思います。
次の章では、Perlでテキストを自由に扱えるようになるために、正規表現を使った文字列の検索について解説します。