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

■ナビゲータ

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

  1. [Windows版Rubyの細道・けもの道]
    1. [1.準備編]
    2. [2.基本編]
      1. [2-1.基本処理]
      2. [2-2.キーブレイク処理]
      3. [2-3.マッチング(照合)処理]
      4. [2-4.ソート(並べ替え)処理]
        1. [2-4-1.レコード全体のソート(並べ替え)]
        2. [2-4-2.CSVファイルの文字列型昇順ソート(並べ替え)]
        3. [2-4-3.CSVファイルの文字列型・数値型/昇順・降順ソート(その1)]
        4. [2-4-4.CSVファイルの文字列型・数値型/昇順・降順ソート(その2)]
        5. [2-4-5.大量のCSVファイルのソート(並べ替え)する場合の考慮事項]
        6. [2-4-6.大量のCSVファイルの文字列型/昇順ソート(並べ替え)]
        7. [2-4-7.大量のCSVファイルの文字列型・数値型/昇順・降順ソート(並べ替え)]
      5. [2-5.パターンマッチ処理]
    3. [3.応用編]
    4. [スクリプトと入力データのサンプル]
Perlではどう処理する?
同じことをPerlではこうしています。

2.基本編

2-4.ソート(並べ替え)処理

2-4-3.CSVファイルの文字列型・数値型/昇順・降順ソート(その1)

CSVファイルを文字列型や数値型に区別して、昇順にも降順にもソート(sort:並べ替え)することのできるスクリプトです。

数値型というのは、数字を計算するための数値として扱うということで、たとえば、昇順に並べ替えると「1,2,3,10,20,30」のような順番になりますが、文字列型では「1,10,2,20,3,30」のような順番になります。表計算ソフトなどでは、誤解しているケースが多いようですが、文字列型・数値型というのは、データそのものによって決まるのではなく、データをどのように扱う(認識する)かによって決まりますので、データが数字だからといって数値型と即断するのは誤りです(文字列型の可能性もあります)。たとえば、郵便番号や電話番号は(ハイフンは除くものとすると)すべて数字から成り立っていますが、通常は文字列型として扱います。一方、点数や長さ、重さなどは通常、数値型として扱います。また、ISBNコードやJANコードなどは通常は文字列型として扱いますが、チェックディジットを計算する場合は、各桁の数字を数値として扱います。

並べ替える項目が文字列型か数値型かによって指定する方法が変わります。

実際に応用する場合は、入出力データのファイル名を変更し、下記の「[w_array[1].to_f,w_array[0]]」の部分などを変更します

sort_rec    =   in1_rec.sort_by{|data|  
                w_array =   data.split("\t",-1) 
                [w_array[1].to_f,w_array[0]]    
            }   
   

この例では、2番目の項目を第1キーで数値項目として、1番目の項目を第2キーで文字列項目として並べ替えるように指定しています。Rubyでは、配列のインデックスは「0」から開始されますので、1番目の項目を「0」、2番目の項目を「1」として指定することに注意してください。

文字列型を昇順に並べ替えるのであれば、「w_array[0]」のように、文字列型を降順に並べ替えるのであれば、マイナスをつけて「-w_array[0]」のように指定します。[]の中には何番目の項目を並べ替えるのかを指定します。

また、数値型を昇順に並べ替えるのであれば、整数として並べ替える場合、「w_array[0].to_i」のように、整数として降順に並べ替えるのであれば、マイナスをつけて「-w_array[0].to_i」のように指定します。実数として並べ替えるのであれば、それぞれ「to_i」の部分を「to_f」に変更します。

なお、実用的な入力データの件数は指定するキーの数にもよりますが、ソート(並べ替え)キーを2つ指定したときで、10万件程度までです。それ以上の入力件数がある場合は、実用にはなりません。

下記の例では、入力ファイルの各フィールドを「"」(ダブルクォーテーションつき)で囲み、それぞれのデータを「,」(カンマ)で区切っている形式を前提としているため、

    in1 =   (line1 + ',')   
            .scan(/"([^"\\]*(?:\\.[^"\\]*)*)",|([^,]*),/)   
            .collect{|x,y| y || x.gsub(/(.)/, '\1')}    
   

としていますが、入力ファイルの各フィールドを「"」(ダブルクォーテーションつき)で囲まずに、それぞれのデータを「,」(カンマ)で区切っている、いわゆる「CSV2形式」であれば、以下のようにすることができます。

    in1 =   line1.split(",",-1)   
   

また、タブ区切りであれば、以下のようにすることができます。

   in1 =   line1.split("\t",-1)   
   
【スクリプト】
# csvsort2.rb   
# 内容 : 対象ファイルをCSV形式としてソートする 
#     文字列型の昇順・降順、数値型の昇順・降順の並べ替えを行う。    
#     (通貨記号や","つきの数字は数値型としての並べ替えはできない)   
# Copyright (c) 2009-2015 Mitsuo Minagawa, All rights reserved. 
# (minagawa@fb3.so-net.ne.jp)   
# 使用方法 : c:\>ruby csvsort2.rb   
#   
# 入力ファイル  
in1_file    =   open("input.txt","r")   
# 出力ファイル  
out1_file   =   open("output.txt","w")  

in1_ctr =   0       #入力件数   
in1_rec =   Array.new   #入力ファイルを格納する配列 
sort_rec    =   Array.new   #ソート済ファイルを格納する配列 

# CSVファイルの入力 

while   (line1  =   in1_file.gets)  
    line1.chomp!    
    in1 =   (line1 + ',')   
            .scan(/"([^"\\]*(?:\\.[^"\\]*)*)",|([^,]*),/)   
            .collect{|x,y| y || x.gsub(/(.)/, '\1')}    
    in1_rec[in1_ctr]    =   in1.join("\t")  
#入力件数加算   
    in1_ctr +=  1   
end 

# タブ区切りファイルのときは、以下のようにする  
# 後でタブ区切りで配列に入れるので、この時点でCSVファイルのように配列にする必要はない   
#while  (line1  =   in1_file.gets)  
#   line1.chomp!    
#   in1_rec[in1_ctr]    =   line1   
#入力件数加算   
#   in1_ctr +=  1   
#end    
# CSVファイルの並べ替え 
# 文字列型の昇順(1番目の項目):w_array[0]    
# 文字列型の降順(2番目の項目):-w_array[1]   
# 整数型の昇順(3番目の項目):w_array[2].to_i 
# 整数型の降順(4番目の項目):-w_array[3].to_i    
# 実数型の昇順(3番目の項目):w_array[2].to_f 
# 実数型の降順(4番目の項目):-w_array[3].to_f    
# 複数項目の並べ替えは並べ替えをするキーの順番にカンマで区切って並べていく。    

sort_rec    =   in1_rec.sort_by{|data|  
                w_array =   data.split("\t",-1) 
                [w_array[1].to_f,w_array[0]]    
            }   

sort_rec.each   {|data|     
                out1_file.print data,"\n"   
            }   

# ファイルのクローズ    
in1_file.close  
out1_file.close 
    
【スクリプトとデータのサンプル】

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

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

【スクリプトの解説】

それでは、1つずつ解説していきましょう。

ソート(並べ替え)処理

sort_rec    =   in1_rec.sort_by{|data|  
                w_array =   data.split("\t",-1) 
                [w_array[1].to_f,w_array[0]]    
            }   
   

「in1_rec」には、並べ替えをする前のレコードが入っています。各項目をタブ区切りした文字列となっています。この文字列を「w_array = data.split("\t",-1)」でタブ区切りして、配列にし、必要な項目で並べ替えられるようにします。この「w_array」の名前は変更可能です。

文字列型を昇順に並べ替えるのであれば、「w_array[0]」のように、文字列型を降順に並べ替えるのであれば、マイナスをつけて「-w_array[0]」のように指定します。[]の中には何番目の項目を並べ替えるのかを指定します。Rubyでは、配列のインデックスは「0」から開始されますので、1番目の項目を「0」、2番目の項目を「1」として指定することに注意してください。

また、数値型を昇順に並べ替えるのであれば、整数として並べ替える場合、「w_array[0].to_i」のように、整数として降順に並べ替えるのであれば、マイナスをつけて「-w_array[0].to_i」のように指定します。実数として並べ替えるのであれば、それぞれ「to_i」の部分を「to_f」に変更します。

ソート(並べ替え)した結果の出力

sort_rec.each   {|data|     
                out1_file.print data,"\n"   
            }   
   

並べ替えが終わった後のレコードを出力しています。

「sort_rec」には、並べ替えが終わった後のレコードが入っていますので、そのレコードをそのまま「each」で先頭からの順番に出力します。

(注)なお、この方法でソート(並べ替え)すると、安定したソート(ソート(並べ替え)したキーが同じ場合、入力順に並ぶこと)にはなりません。安定したソートにしたい場合は、入力順に連番をつけて、その連番もソート(並べ替え)キーに含めることにより、行いますが、具体的には該当部分を以下のように変更します。

安定したソート(並べ替え)にするための方法

sort_rec    =   in1_rec.sort_by{|data|  
                 i   =   i   +   1
                w_array =   data.split("\t",-1) 
                [w_array[1].to_f,w_array[0],i]    
            }   

sort_rec.each   {|data|     
                w_array =   data.split("\t",-1)
                w_array.pop
                out1    =   w_array.join("\t")
                out1_file.print	out1,"\n"
            }   
    
【入力データ:ソート(並べ替え)前のデータ(input.txt)】
ccc,5555   
aaa,7777
bbb,9999
aaa,1111
bbb,3333
bbb,4444
aaa,5555
ccc,1111
    
【出力データ:ソート(並べ替え)後のデータ(output.txt)】
aaa,1111   
ccc,1111
bbb,3333
bbb,4444
aaa,5555
ccc,5555
aaa,7777
bbb,9999
    



Copyright (c) 2009-2015 Mitsuo Minagawa, All rights reserved.