■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-2.CSVファイルの文字列型昇順ソート(並べ替え)

大量のCSVファイルを文字列型のキーで昇順にソート(sort:並べ替え)するスクリプトです。

[2-4-5.大量のCSVファイルのソート(sort:並べ替え)する場合の考慮事項]でソート(sort:並べ替え)キーごとの累計件数から割り出したソート(sort:並べ替え)キーをもとに大量のCSVファイルを分割しながら並べ替えていきます。下記の例で"AAJJ"の箇所に分割して並べ替える際の基準になるソート(並べ替え)キーを指定していきます。このとき、「<=」や「<」で比較する場合には、ソート(sort:並べ替え)キーを小さい順に指定し、「>=」や「>」で比較する場合には、ソート(sort:並べ替え)キーを大きい順に指定します。

    if      (in1[0]     <=      "AAJJ") 
            sort_rec01[hash_key]    =   record  
   

実際に応用する場合は、入出力データのファイル名を変更し、「sortitem = [0,1]」の部分を変更します。この例では、1番目の項目を第1キーに、2番目の項目を第2キーとして並べ替えるように指定しています。rubyでは、配列のインデックスは「0」から開始されますので、1番目の項目を「0」、2番目の項目を「1」として指定することに注意してください。

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

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

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

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

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

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


【スクリプト】
# csvsort_many.rb   
# 内容 :大量のCSVファイルを文字列型で昇順にソートする   
# Copyright (c) 2011-2015 Mitsuo Minagawa, All rights reserved. 
# (minagawa@fb3.so-net.ne.jp)   
# 使用方法 : c:\>ruby csvsort_many.rb   
#   
# 入力ファイルを全部読み込む。  
in1_file    =   IO.readlines("input.txt")   
# 出力ファイル  
out1_file   =   open("output.txt","w")  

# 整列の基準となる項目番号      
# ソートキー    
sortitem    =   [0,1]   
# ソート用のキー    
hash_key    =   nil 
# ソートするキーとレコードをセットするハッシュ  
sort_rec01  =   Hash.new    
sort_rec02  =   Hash.new    
sort_rec03  =   Hash.new    
sort_rec04  =   Hash.new    
sort_rec05  =   Hash.new    
sort_rec06  =   Hash.new    
sort_rec07  =   Hash.new    
sort_rec08  =   Hash.new    
sort_rec09  =   Hash.new    
sort_rec10  =   Hash.new    
sort_rec11  =   Hash.new    
sort_rec12  =   Hash.new    

# ソートキーとなる項目番号からソートキーとなる文字列を作っておく    
in1_file.each_with_index    {|record,recno| 
    record.chomp!   
# CSVファイルを分解し、配列にセットする 
    in1 =   (record + ',')  
            .scan(/"([^"\\]*(?:\\.[^"\\]*)*)",|([^,]*),/)   
            .collect{|x,y| y || x.gsub(/(.)/, '\1')}    
# タブ区切りファイルのときは下記の通り  
#   in1 =   record.split("\t",-1)   
# 整列の基準となる項目番号の項目内容をタブ値ではさんで連結してキーとする        
    hash_key    =   ""  
    sortitem.each   {|i|    
        hash_key    +=  in1[i] + "\t"   
    }   
# 同じ値を持つキーが複数存在する場合に以下のように指定する。    
    hash_key    +=  sprintf("%08d",recno)   
# レコードをハッシュの値としてセットする。  
    if      (in1[0]     <=      "AAJJ") 
            sort_rec01[hash_key]    =   record  
    elsif   (in1[0]     <=      "AAVV") 
            sort_rec02[hash_key]    =   record  
    elsif   (in1[0]     <=      "BBJJ") 
            sort_rec03[hash_key]    =   record  
    elsif   (in1[0]     <=      "BBVV") 
            sort_rec04[hash_key]    =   record  
    elsif   (in1[0]     <=      "CCJJ") 
            sort_rec05[hash_key]    =   record  
    elsif   (in1[0]     <=      "CCVV") 
            sort_rec06[hash_key]    =   record  
    elsif   (in1[0]     <=      "XXJJ") 
            sort_rec07[hash_key]    =   record  
    elsif   (in1[0]     <=      "XXVV") 
            sort_rec08[hash_key]    =   record  
    elsif   (in1[0]     <=      "YYJJ") 
            sort_rec09[hash_key]    =   record  
    elsif   (in1[0]     <=      "YYVV") 
            sort_rec10[hash_key]    =   record  
    elsif   (in1[0]     <=      "ZZJJ") 
            sort_rec11[hash_key]    =   record  
    elsif   (in1[0]     <=      "ZZVV") 
            sort_rec12[hash_key]    =   record  
    end 
    hash_key    =   nil 
}   

# キー項目+行番号の配列を整列し、それにしたがってレコードを出力する    
sort_rec01.sort_by{|key| key }.each{|key,value| 
    out1_file.print value,"\n"  
}       

sort_rec02.sort_by{|key| key }.each{|key,value| 
    out1_file.print value,"\n"  
}       

sort_rec03.sort_by{|key| key }.each{|key,value| 
    out1_file.print value,"\n"  
}       

sort_rec04.sort_by{|key| key }.each{|key,value| 
    out1_file.print value,"\n"  
}       

sort_rec05.sort_by{|key| key }.each{|key,value| 
    out1_file.print value,"\n"  
}       

sort_rec06.sort_by{|key| key }.each{|key,value| 
    out1_file.print value,"\n"  
}       

sort_rec07.sort_by{|key| key }.each{|key,value| 
    out1_file.print value,"\n"  
}       

sort_rec08.sort_by{|key| key }.each{|key,value| 
    out1_file.print value,"\n"  
}       

sort_rec09.sort_by{|key| key }.each{|key,value| 
    out1_file.print value,"\n"  
}       

sort_rec10.sort_by{|key| key }.each{|key,value| 
    out1_file.print value,"\n"  
}       

sort_rec11.sort_by{|key| key }.each{|key,value| 
    out1_file.print value,"\n"  
}       

sort_rec12.sort_by{|key| key }.each{|key,value| 
    out1_file.print value,"\n"  
}       

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

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

【スクリプトの解説】

基本的には[2-4-2.CSVファイルの文字列型昇順ソート]を応用したスクリプトになっています。

入力データの振り分け

# レコードをハッシュの値としてセットする。  
    if      (in1[0]     <=      "AAJJ") 
            sort_rec01[hash_key]    =   record  
    elsif   (in1[0]     <=      "AAVV") 
            sort_rec02[hash_key]    =   record  
    elsif   (in1[0]     <=      "BBJJ") 
            sort_rec03[hash_key]    =   record  
    elsif   (in1[0]     <=      "BBVV") 
            sort_rec04[hash_key]    =   record  
    elsif   (in1[0]     <=      "CCJJ") 
            sort_rec05[hash_key]    =   record  
    elsif   (in1[0]     <=      "CCVV") 
            sort_rec06[hash_key]    =   record  
    elsif   (in1[0]     <=      "XXJJ") 
            sort_rec07[hash_key]    =   record  
    elsif   (in1[0]     <=      "XXVV") 
            sort_rec08[hash_key]    =   record  
    elsif   (in1[0]     <=      "YYJJ") 
            sort_rec09[hash_key]    =   record  
    elsif   (in1[0]     <=      "YYVV") 
            sort_rec10[hash_key]    =   record  
    elsif   (in1[0]     <=      "ZZJJ") 
            sort_rec11[hash_key]    =   record  
    elsif   (in1[0]     <=      "ZZVV") 
            sort_rec12[hash_key]    =   record  
    end 
   

[2-4-5.大量のCSVファイルのソート(並べ替え)する場合の考慮事項]でソート(並べ替え)キーごとの累計件数から各々100万件程度になるように割り出したソート(並べ替え)キー(原則として第1キー)を指定していきます。入力ファイルの件数によって、elsifの数を調整していきます。また、必要に応じてif文やelsif文には、第2キー以降をand条件で追加していきます。ここでセットした「sort_recxx(xxには01から始まる数字が入る)」の順に並べ替えをして出力していきます。

並べ替える件数にあわせて、上記の部分および「sort_recxx.sort_by・・・」で実際に出力している箇所を増減していきます。

同一のソート(並べ替え)キーで100万件を大幅に超える件数がある場合

また、同一のソート(並べ替え)キーで100万件を大幅に超える件数がある場合は、以下のように変更します。

まず、ハッシュの初期値設定をしている箇所ですが、

    sort_rec01  =   Hash.new    
   

上記の箇所については、下記のようにハッシュではなく、通常の配列に変更します。

    sort_rec01  =   Array.new    
   

また、条件ごとに振り分けている箇所ですが、

    if      (in1[0]     <=      "AAJJ") 
            sort_rec01[hash_key]    =   record  
   

上記の箇所については、ハッシュキーではなく、以下のようにします。

    if      (in1[0]     <=      "AAJJ") 
            sort_rec01[recno]       =   record  
   

さらに、ハッシュからソート(並べ替え)して出力する以下の箇所については、

    sort_rec01.sort_by{|key,value|  key}.each{|key,value|   
        out1_file.print value,"\n"  
    }       
   

以下のように配列「sort_rec01」から順番に出力するように変更します。

    sort_rec01.each {|record|
        out1_file.print record,"\n"  
    }
   


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