■Windows版Perlの細道・けもの道

■ナビゲータ

[南北館(最初のメニュー)]

  1. [Windows版Perlの細道・けもの道]
    1. [1.準備編]
    2. [2.基本編]
      1. [2-1.基本処理]
        1. [2-1-1.スクリプトの基本形]
        2. [2-1-2.必要な項目だけを出力する]
        3. [2-1-3.条件をつけて出力する]
        4. [2-1-4.スクリプトと入出力ファイルを異なるフォルダに入れる]
        5. [2-1-5.PPM(Perl Package Manager)によるモジュールのインストール]
      2. [2-2.キーブレイク処理]
      3. [2-3.マッチング(照合)処理]
      4. [2-4.ソート(並べ替え)処理]
      5. [2-5.パターンマッチ処理]
    3. [3.応用編]
    4. [スクリプトと入力データのサンプル]
rubyではどう処理する?
同じことをrubyではこうしています。

2.基本編

2-1.基本処理

2-1-1.スクリプトの基本形

以下のスクリプトはCSVファイルを読み込み、タブ区切りファイルとして出力するPerlのスクリプトです。このスクリプトが基本的なCSVファイルの入出力スクリプトになります。

実際に実行して確認することをお勧めします。

まず、FileVisorなどのファイラーを起動し、適当なフォルダ(以下c:\workを作成したものとして解説する)を作成した上で、以下のような作業を行います。

1.こちらからスクリプトをダウンロードして、c:\workの下に「inout.pl」と名前を付けて保存します(フォルダ名は変更してもかまいません)。

2.こちらから入力データをダウンロードし、同じフォルダに「input.txt」と名前を付けて保存します。

3.「inout.pl」をダブルクリックして、実行します。

【スクリプト】
# inout.pl    
# 内容 : 基本プログラム(1件入力し、1件出力するプログラム)
# Copyright (c) 2002 Mitsuo Minagawa, All rights reserved.
# (minagawa@fb3.so-net.ne.jp)
# 使用方法 : c:\>perl inout.pl
#
open(IN1,"input.txt");                    #(1)入力ファイルをオープンする。
open(OUT1,">output.txt");                 #(2)出力ファイルをオープンする。
while   ($line1 =    <IN1>)    {          #(3)1行単位で入力し、
                                          #入力データが終了するまで繰り返す。
        chomp($line1);                    #(4)行末の改行文字をカットする。
        @in1    =       split(",",$line1,-1); #(5)カンマで項目を分割する。
        $out1   =       join("\t",@in1);      #(6)タブで項目をまとめる。
        print   OUT1    "$out1\n";            #(7)1行出力する。
}
close(IN1);                                  #(8)入力ファイルをクローズする。
close(OUT1);                                 #(9)出力ファイルをクローズする。
   
【スクリプトとデータのサンプル】

スクリプトはこちらにあります。

入力データのサンプルはこちらにあります。

【スクリプトの解説】

それでは、スクリプトの解説を個別に行っていきましょう。

コメントについて

「#」が行の左端や行の途中にありますが、これは、「コメント」といって、スクリプトの実行そのものには何の影響も与えずに、スクリプトの内容があとで見たときにわかるように記入するものです。「#」から改行までの部分がコメントになります。

スクリプトは1回作成したまま、再利用しない場合もありますが、再利用することも少なくありません。再利用する場合には、修正がともないますが、見にくいスクリプトでは、修正することは困難であり、結果として再利用もできなくなります。このようなことを防ぐために、コメントには、後で見たときに内容がわかるような記述をする必要があります。コメントに何を記入してもスクリプトの実行には関係ないので、できる限りわかりやすい表現にしてください。

また、わかりにくいかもしれませんが、各行の最後が「;」(セミコロン)で終わっていることにも注意しましょう(ただし、while文については、「;」(セミコロン)をつけません)。Perlでは、それぞれの文の最後に「;」(セミコロン)をつけることになっています。この点は忘れやすいので注意が必要です。

次に、1行ずつ解説していきます。スクリプトの各行の右端にある(1)、(2)などの記号を使って説明します。

ファイルのオープンについて

open(IN1,"input.txt");              #(1)入力ファイルをオープンする。
open(OUT1,">output.txt");           #(2)出力ファイルをオープンする。   
   

まず、(1)の「open(IN1,"input.txt");」と(2)の「open(OUT1,">output.txt");」は、ファイルをオープンする処理を行っています。

「ファイルをオープンする」とは、スクリプトと外部のファイルを結びつけて、外部のファイルを利用できるようにすることです。上記の例では同じフォルダにある「input.txt」を入力ファイルに指定し、「output.txt」を出力ファイルに指定しています。open関数は、通常、2つの引数(ひきすう)を指定して使用します。引数とは、かっこ内の項目であり、この引数を変えることによって、さまざまな指示を行うことができます。open関数の引数は、「ファイルハンドル」と「ファイル名」から成り立っています。

「ファイルハンドル」は、スクリプトの中でデータを扱うための記号です。先頭が英大文字でその後に英大文字か数字を続ければ、どのような名称を指定してもいいのですが、私は以下のようにしています。

「ファイル名」は、スクリプトと同じフォルダ内にある場合は、ドライブ名やフォルダ名は不要です。スクリプトと異なるフォルダに入力ファイルや出力ファイルを置く場合については、[2-1-4.スクリプトと入出力ファイルを異なるフォルダに入れる]を参照してください。

該当するファイルが入力ファイルか、出力ファイルかは、ファイル名の前に「>」を置くかどうかで決まります。

出力ファイルは、スクリプトの実行が開始されると削除されてしまいますので、入力ファイルの前に間違えて、「>」を入れないようにしてください。また、出力ファイルに「>」をつけないと出力結果が得られないので、注意が必要です。

ファイルの入力処理について

while   ($line1 =    <IN1>)    {          #(3)1行単位で入力し、
                                          #入力データが終了するまで繰り返す。
        chomp($line1);                    #(4)行末の改行文字をカットする。
        @in1    =       split(",",$line1,-1);  #(5)カンマで項目を分割する。
        $out1   =       join("\t",@in1);       #(6)タブで項目をまとめる。
        print   OUT1    "$out1\n";             #(7)1行出力する。
}
   

(3)の「while ($line1 = <IN1>) {」とありますが、この中の「{」は、(7)の下の「}」と対応しており、whileの中にある「$line1 = <IN1>」を実行したあと、(4)から(7)までの命令を繰り返すものです。

「$line1 = <IN1>」は、「IN1」というファイルハンドルで結びつけられた入力ファイル(ここでは、(1)の命令から、input.txt)から、1行読み込み、その1行をそのまま$line1という変数にセットします。したがって、最初にこの命令を実行すると、input.txtの1行目にある「aaa,100,10,20,30,40」がそのまま改行コードつきで$line1という変数にセットされることになります。

while関数は、Perlの解説書では、「『真』の間は実行を繰り返し、偽になったら終了する」となっていますが、ここでは「入力データがある間は」ぐらいに考えればいいでしょう。入データがなくなった時点で、(8)や(9)のclose関数を実行します。

繰り返し処理について

while   ($line1 =    <IN1>)    {           #(3)1行単位で入力し、
                                           #入力データが終了するまで繰り返す。
        chomp($line1);                     #(4)行末の改行文字をカットする。
        @in1    =       split(",",$line1,-1);  #(5)カンマで項目を分割する。
        $out1   =       join("\t",@in1);       #(6)タブで項目をまとめる。
        print   OUT1    "$out1\n";             #(7)1行出力する。
}
   

(4)から(7)までがwhileを使った繰り返し処理の中でおこなわれている処理です。

改行文字の削除

まず(4)の「chomp($line1);」から説明していきましょう。

chompは引数で指定した文字列の最後(行末)に改行文字があれば、削除する関数です。入力ファイルや出力ファイルの中では、改行文字は欠かせないものですが、スクリプトの中で処理する際にはじゃまになる(CSVファイルなどをカンマで分解した際には、最後の要素についてしまうことになります)ので削除します。改行文字を削除したあとの結果は、引数である、$line1にセットされます。「$xxx = chomp($line1)」などとしないでください。$xxxには、削除された文字数が入るだけで、改行文字が削除された結果が入るわけではありません。

CSVファイルをカンマ区切りして配列に入れる

        @in1    =       split(",",$line1,-1);  #(5)カンマで項目を分割する。
   

(5)の「@in1 = split(",",$line1,-1);」 は、文字列をデリミタで分解し、配列にセットするものです。デリミタとは、文字列を分解するために利用される記号のことで。通常、カンマやタブ、スペースなどが使われます。

先頭に「@」をつけた変数が配列として扱われます。配列とは、複数の値を管理するための変数です。変数全体ではなく、複数の値から特定の値を1つ取り出すためには、変数に0から始まる添字をつけます。

たとえば、入力データの1行目で「aaa,100,10,20,30,40」とあって、これをカンマで区切った内容が@in1という配列に入りますが、この結果、配列@in1の1番目の項目である$in1[0]には「aaa」が入り、2番目の項目である$in1[1]には「100」が入り、$in1[5]に「40」が入ります。

split関数の使い方は以下の通りです。

「分解後の内容をセットする配列 = split(デリミタ,分解前の文字列,分解する要素数)

1番目の引数ではデリミタ(区切り文字)を指定し、2番目の引数では分解する対象となる文字列を指定し、3番目の引数で分解する項目数の最大値を指定します。

上記の「分解する要素数」に何を指定するかによって、配列にセットされる内容が変わります。

配列をタブでまとめる

        $out1   =       join("\t",@in1);       #(6)タブで項目をまとめる。
   

(6)の「$out1 = join("\t",@in1);」は、(5)のsplit関数とは逆にjoin関数を使って、配列をデリミタ(区切り文字)をつけて、1つの文字列にまとめるものです。1番目の引数には、デリミタをダブルクォーテーションで囲んで指定します。2番目の引数には、1行にすべき配列を指定します。

ファイルの出力

        print   OUT1    "$out1\n";             #(7)1行出力する。
   

(7)の「print OUT1 "$out1\n";」で1行出力します。

print関数は、そのあとにスペースを置いて、出力ファイルを示すファイルハンドルを指定し、さらにスペースを置いて、出力する内容をダブルクォーテーションをつけて指定します。項目が複数あれば、連続して記入します。

通常は1行の最後で改行させるために最後に改行文字を示す「\n」をつけます。改行文字をつけないと、改行をされず、連続して出力されてしまいます。変数「out1」は、配列「in1」を1つにまとめたものですが、(4)で改行文字を削除しているので、出力時に改めてつけているのです。

print関数で出力する内容をダブルクォーテーションで囲むことが多いのですが、これは、ダブルクォーテーションで変数を囲むと変数の「値」がセットされるためです。これを変数展開とよびます。変数展開は、変数以外にも改行("\n")やタブ("\t")を表すエスケープシーケンスも利用されますが、変数展開されると、たとえば、"\n"という文字そのものが出力されるのではなく、改行文字を出力することになります。

ファイルのクローズ

      close(IN1);             #入力ファイルをクローズする。(8)   
      close(OUT1);            #出力ファイルをクローズする。(9)
   

(8)と(9)は入力ファイルと出力ファイルをクローズする命令です。

それぞれ引数には、ファイルハンドルを指定します。ファイルを使ったスクリプトを記述する場合には、最後にclose関数を忘れないようにしてください。

以上、スクリプトの基本形について説明しました。実質的には、10行程度のスクリプトですが、これにいくつかの関数や命令を追加することで実用的なスクリプトを作成することができます。実際には、毎回1から作成するのではなく、ある程度パターン化したスクリプトを用意しておき、一部分を変更したり、組み合わせたりすることによって、さまざまな処理を行うスクリプトが簡単に作成できるようになります。

【入力データ】
aaa,100,10,20,30,40
aaa,200,10,20,30,
aaa,300,10,20,,
bbb,100,10,,,
ccc,100,,,,
ccc,,,,,
ddd,100,,,,
ddd,200,10,,,
ddd,300,20,40,,
ddd,400,20,40,60,
eee,100,20,40,60,80
fff,100,20,40,60,
ggg,100,20,40,,
hhh,100,20,,,
hhh,200,,,,
hhh,,,,,
   

「inout.pl」をダブルクリックしたり、Comwinなどのシェルプログラムやコマンドプロンプトで「perl inout.pl」と入力して、[Enter]キーを押すと、同じフォルダに「output.txt」という以下のような出力データができていると思います(うまく行かない場合は、Comwinなどのシェルプログラム起動し、「Perl -cw inout.pl」と入力して、[Enter]キーを押してみてください。「-cw」をつけると、文法チェック(Syntax Check)を行うと同時に詳細なエラーメッセージが表示されます)。テキストエディタでは、[タブ]記号が矢印等で表示されているため、下記の出力データとは、多少異なる見え方になるかもしれません。

【出力データ】
aaa 100 10  20  30  40
aaa 200 10  20  30  
aaa 300 10  20      
bbb 100 10          
ccc 100             
ccc                 
ddd 100             
ddd 200 10          
ddd 300 20  40      
ddd 400 20  40  60  
eee 100 20  40  60  80
fff 100 20  40  60  
ggg 100 20  40      
hhh 100 20          
hhh 200             
hhh
   



Copyright (c) 2004-2013 Mitsuo Minagawa, All rights reserved.