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

■ナビゲータ

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

  1. [Windows版Rubyの細道・けもの道]
    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.スクリプトの中で漢字を使う]
      2. [2-2.キーブレイク処理]
      3. [2-3.マッチング(照合)処理]
      4. [2-4.ソート(並べ替え)処理]
      5. [2-5.パターンマッチ処理]
    3. [3.応用編]
    4. [スクリプトと入力データのサンプル]
Perlではどう処理する?
同じことをPerlではこうしています。

2.基本編

2-1.基本処理

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

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

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

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

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

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

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

【スクリプト】
# inout.rb  
# 内容 : 基本プログラム(1件入力し、1件出力するプログラム)    
# Copyright (c) 2002-2015 Mitsuo Minagawa, All rights reserved. 
# (minagawa@fb3.so-net.ne.jp)   
# 使用方法 : c:\>ruby inout.rb  
#   
#入力ファイルをオープンする。(1)    
in1_file    =   open("input.txt","r")   

#出力ファイルをオープンする。(2)    
out1_file   =   open("output.txt","w")  

#1行単位で入力し、入力データが終了するまで繰り返す。(3) 
while   (line1  =   in1_file.gets)  

#行末の改行文字をカットする。(4)    
    line1.chomp!    

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

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

#1行出力する。(7)   
    out1_file.print out1,"\n"   
end     

#入力ファイルをクローズする。(8)    
in1_file.close  

#出力ファイルをクローズする。(9)    
out1_file.close 
   
【スクリプトとデータのサンプル】

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

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

【スクリプトの解説】

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

コメントについて

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

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

また、Perlとは異なり、各行の最後に「;」(セミコロン)をつけないようにします。

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

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

# inout.rb  
# 内容 : 基本プログラム(1件入力し、1件出力するプログラム)    
# Copyright (c) 2002-2015 Mitsuo Minagawa, All rights reserved. 
# (minagawa@fb3.so-net.ne.jp)   
# 使用方法 : c:\>ruby inout.rb  
#   
#入力ファイルをオープンする。(1)    
in1_file    =   open("input.txt","r")   

#出力ファイルをオープンする。(2)    
out1_file   =   open("output.txt","w")  
   

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

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

「ファイルのモード」は、ファイルをどのように利用するかを指定するものです。ここでは、

であるというだけにとどめます。

「open」メソッドは「=」によってオブジェクトにセットされますが、上記の例では、式の左辺にある「in1_file」や「out1_file」がそのオブジェクトに該当します。スクリプトの中では、このオブジェクトを利用してファイルの入出力を行います。どのような名称を指定してもいいのですが、私は以下のようにしています。。

「ファイル名」は、スクリプトと同じフォルダ内にある場合は、ドライブ名やフォルダ名は不要です。スクリプトと異なるフォルダに入力ファイルや出力ファイルを置く場合については、[2-1-4.スクリプトと入出力ファイルを異なるフォルダに入れる]を参照してください。上書きする出力ファイルは、スクリプトの実行が開始されると削除されてしまいますので、「ファイルのモード」を間違えないようにしてください。

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

#1行単位で入力し、入力データが終了するまで繰り返す。(3) 
while   (line1  =   in1_file.gets)  

#行末の改行文字をカットする。(4)    
    line1.chomp!    

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

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

#1行出力する。(7)   
    out1_file.print out1,"\n"   
end     
   

(3)の「while (line1 = in1_file.gets)」とありますが、これは(7)の下の「end」と対応しており、whileの中にある「line1 = in1_file.gets」を実行したあと、(4)から(7)までの命令を繰り返すものです。

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

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

繰り返し処理について

#1行単位で入力し、入力データが終了するまで繰り返す。(3) 
while   (line1  =   in1_file.gets)  

#行末の改行文字をカットする。(4)    
    line1.chomp!    

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

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

#1行出力する。(7)   
    out1_file.print out1,"\n"   
end     
   

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

改行文字の削除

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

chomp!はオブジェクト(ここではline1)で指定した文字列の最後(行末)に改行文字があれば、削除するメソッドです。入力ファイルや出力ファイルの中では改行文字は欠かせないものですが、スクリプトの中で処理する際にはじゃまになる(CSVファイルなどをカンマで分解した際には、最後の要素についてしまうことになります)ので削除します。改行文字を削除したあとの結果は、オブジェクトである「line1」に返されます。「xxx = line1.chomp」のようにすれば、「line1」の行末から改行文字を削除したものが「xxx」にセットされます。

Perlではこのような式にした場合、左辺には、削除した文字数がセットされるだけですが、Rubyでは削除した結果が返されます。この点はPerlと異なるので注意が必要です。

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

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

配列だからと言って、Perlのように先頭に「@」をつけないように注意します。配列とは、複数の値を管理するための変数です。変数全体ではなく、複数の値から特定の値を1つ取り出すためには、変数に0から始まる添字をつけます。

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

splitメソッドの使い方は以下の通りです。

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

splitメソッドでは、2つの引数がセットできます。オブジェクトにはデリミタ(区切り文字)の付いた「分解前の文字列」を指定しますが、1番目の引数には区切り文字を指定し、2番目の引数で分解する項目数の最大値を指定します。

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

配列をタブでまとめる

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

   

(6)の「out1 = in1.join("\t")」は、(5)のsplitメソッドとは逆にjoinメソッドを使って、デリミタ(区切り文字)をつけて、配列を1つの文字列にまとめるものです。オブジェクトは配列を指定し、引数には、デリミタをダブルクォーテーションで囲んで指定します。

ファイルの出力

#1行出力する。(7)   
    out1_file.print out1,"\n"   
   

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

printメソッドは、そのあとにスペースを置いて、出力する項目を指定します。項目が複数あれば、カンマで区切りますが、通常は1行の最後で改行させるために最後にカンマと改行文字を示す「\n」をつけます。改行文字をつけないと、改行をされず、連続して出力されてしまいます。変数「out1」は、配列「in1」を1つにまとめたものですが、(4)で改行文字を削除しているので、出力時に改めてつけているのです。

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

ファイルのクローズ

#入力ファイルをクローズする。(8)    
in1_file.close  

#出力ファイルをクローズする。(9)    
out1_file.close 
   

(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.rb」をダブルクリックしたり、Comwinなどのシェルプログラムやコマンドプロンプトで「ruby inout.rb」と入力して、[Enter]キーを押すと、同じフォルダに「output.txt」という以下のような出力データができていると思います(うまく行かない場合は、Comwinなどのシェルプログラム起動し、「ruby -cw inout.rb」と入力して、[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-2015 Mitsuo Minagawa, All rights reserved.