PR

入出力の基本は<STDIO>とprint

リスト7●標準入力を利用してデータを読み込む
図7●リスト7の実行結果
リスト8●数値データをソートする
図8●リスト8 実行結果
リスト9●文字列のパターン・マッチを調べる
図9●リスト9の実行結果
リスト10●文字列を置換する
リスト11●文字列を分割,結合する
図10●リスト11の実行結果

 変数,配列,ハッシュが扱えて,分岐と繰り返しができるようになりました。あとはファイルにアクセスして,データを加工できるようになれば,一通りのプログラムを組み立てることができますね。次は入出力です。

 Perlを含むスクリプト言語でもっともよく利用される入出力のインタフェースは,標準入力と標準出力でしょう。標準入力は,Windows,MS-DOS,UNIXなどのOSが,アプリケーションの標準的な入力のために用意している論理的なデバイスです。デフォルトはキーボードです。標準入力を利用する例を見てみましょう(リスト7[拡大表示])。

 (26)は単なる表示ですね。(27)は変数$lineに,標準入力から1行の文字列を読み込みます。<STDIN>はSTanDard INの略で,一般に標準入力を意味します。標準入力のデフォルトはキーボードですから,このプログラムは,ユーザーがキーボードからなにか文字列を入力して,Enterキーが押されるのを待ちます。Enterが押されると,その行が$lineに格納されます。

 (28)のchompは,末尾の改行を取り去る関数です。変数$lineの内容が「shokunin(改行)」だったら,chompすると「shokunin」に変わります。(29)は出力です(図7[拡大表示])。これまで何度も登場しているprintは,標準出力に値を出力する関数だったのです。

 ここで,リスト7を「list7.pl」という名前で保存したディレクトリに,「in.txt」というファイルを作ったとしましょう。in.txtの内容は1行だけ,

shokunin

です。行の終わりには改行コードがあります。この状態で

perl list7.pl <in.txt >out.txt

と実行すると,図7にあるような出力結果がout.txtに保存されます。これを「リダイレクト」と呼びます。標準入出力とリダイレクトさえ覚えておけば,最低限のファイル入出力ができてしまいます。もちろんPerlにはファイル名を指定して読み書きする機能もきちんと用意されています。誌面の都合でここでは解説できませんが一つのファイルを読み込み,一つのファイルを出力するだけのようなプログラムなら,標準入出力を利用するのが簡単です。

 また,

@lines = <STDIN>;
とすれば,標準入力から読み込んだ値を配列に1行ずつ順番に格納することもできます。この場合,1行目が$lines[0],2行目が$lines[1]です。さらに,
while($line = <STDIN>){
 #何か処理をする
}

と書けば1行ずつ処理をすることもできます。

正規表現の使いこなしがカギ

 残りはデータを加工処理する方法です。計算はこれまで見てきたように普通に数式を利用できます。各種関数も用意されているので,別段困ることもないでしょう。ここではPerlの強力な文字列処理を中心に見ていきます。

 よく利用される処理は,ソート(並べ替え),パターン・マッチ,置換,文字列の分解,結合などでしょうか。この順番で紹介していきます。

 ソートは簡単です。「sort」というそのままな名前の関数があります。

@sushi = sort @sushi;

と書くと配列@sushiの各要素をASCIIコード*10の順番でソートして,配列@sushiに再び格納します。

 数値データをソートするときはリスト8[拡大表示]のように書きます。(30)がソートの対象になる配列です。(31)で昇順(小さい値から大きい値)にソートします。(32)は出力です。ちゃんとソートされています(図8[拡大表示])。(31)の行はずいぶん変わった書き方だと思われるかもしれません。なぜそうなのかの説明は省きますが,だれもが納得できるだけの理由はちゃんとあるのでご安心ください。ちなみに<=>は比較演算子で,左辺が大きければ「1」,右辺が大きければ「-1」,等しい場合は0を返します。「$a <=> $b」のaとbを入れ替えて「$b <=> $a」と書けば逆順でソートできます(33)。

 次は文字列の中に,あるパターンにマッチする部分があるかどうかを調べる方法を見てみましょう(リスト9[拡大表示])。(34)のifの条件は,デフォルト変数$_が,「ni」という文字列を含むかどうかをチェックしています。配列@sushiの要素で,「ni」を含むのは「uni」だけなので,(34)で画面に出力するのはuniだけです(図9[拡大表示] )。「/ni/」の部分をパターンと呼びます。

 (35)では正規表現*11を利用しています。正規表現の「.」は任意の1文字を意味します。なので「/ma..ro/」は,最初の2文字が「ma」,次の2文字が任意の文字,最後の2文字が「ro」のものにマッチします。配列@sushiでマッチするのは「maguro」だけです(図9)。ほかにもし「makuro」があればマッチします。「makkuro」は間に3文字あるのでマッチしません。

 この正規表現をうまく使いこなせるかどうかが,Perlによる文字列処理をうまく利用できるかどうかの分かれ目になります。複雑な正規表現を読むのは大変です。でも,ちょっとだけでも知っていればうまくパターンを記述できますし,無駄なコードを書く必要も減ります。

 正規表現は組み合わせて利用できます。たとえば「*」は,直前のものに0回以上マッチすることを意味するので,「.」と「*」を組み合わせた「.*」は任意文字の0回以上の繰り返しです。ここで「/.*ur.*/」と書くと,「ur」を任意文字の0回以上の繰り返しではさんだ文字列にマッチします(36)。配列@sushiでマッチするのは,「maguro」と「ikura」ですね(図9)。

 ほかにいくつか,基本的なものを紹介しておきましょう。「^」は文字列の先頭からマッチさせる場合に利用します。「^tama」という正規表現は,「tamago」,「tamagawa」,「tamachan」などにマッチします。文字列の最後からマッチさせる場合は「$」を利用します。「go$」というパターンは「tamago」,「eigo」,「sango」などにマッチします。「[0-9]」は,0から9までの1文字にマッチします。「^[a-c].*」と書くと,a,b,cのいずれかで始まる文字列にマッチします。

 ひとまずこの程度覚えておけば,かなりのパターンを作り,マッチさせることができるはずです。正規表現のすべての表記法を一度に全部覚えるのはかなり大変です。ここに示した基本を使いながら,ちょっとずつ覚えていくのがよいでしょう。

 最後に,文字列置換,分割,結合の方法を紹介しておきましょう。Perlの強力なテキスト処理能力は,これらの方法と正規表現の組み合わせで実現されています。

 まず置換です。以下のように書くと,

s/maguro/toro/;

文字列「maguro」を「toro」に置換します。「s」はsubstitute(置換)の略です。リスト10[拡大表示]は「toro is good taste.」と画面に表示します。「maguro」の部分を「ma..ro」のように正規表現に置き換えることも可能です。

 文字列の分割はsplit関数を利用して次のようにします(リスト11[拡大表示])。

@配列 = split /正規表現/, $変数;

$変数で与えられた文字列を,正規表現で分割し,@配列に順番に格納します。(37)の行で,「:」で文字列$netaを分割し,配列@netaに順番に格納しています。(38)でforeach制御構造を利用して,配列@netaの全要素を順番に表示しています。

 ちゃんと分割して格納されたことが確認できます(図10[拡大表示])。その逆の結合はjoin関数です。書式は以下のようになっています。

$変数 = join "つなぐ文字", @配列;

@配列の要素をつなぐ文字でつないだ文字列を変数に代入します。リスト11では(39)の部分です。「/」でつないでみました。

☆          ☆          ☆

 最初「記号ばっかり」と思っていたPerlのソースコードも,だいぶすんなり読めるようになったのではないでしょうか。Perlはとても自由度の高い言語です。ここで紹介したサンプルは,もっと読みやすく書けることもありますし,記号を多用するなどしてもっと短く書くこともできます。世の中の“Perlらしい書き方”は,どちらかというと短く,読みにくいものが多いように思います。でも,最初からPerlらしい書き方を実践しよう,と意気込む必要はないと思います。

 もしPerlを気に入ったなら,自分が読みやすく,書きやすい書き方でプログラミングしてください。そのうち,「もっと楽な」,「もっとスマートな」書き方がないのかと思うことがあるでしょう。人に聞いてもいいですし,書籍で学ぶのもよいです。そうして,新しいPerlの言葉遣いを次々と覚えながら,“あなたらしい”Perlの書き方ができるようになるころにはもう,Perlを手放せなくなっているでしょう。

矢崎 茂明