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

■ナビゲータ

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

  1. [Windows版Perlの細道・けもの道]
    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. [スクリプトと入力データのサンプル]
rubyではどう処理する?
同じことをrubyではこうしています。

2.基本編

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

2-4-6.大量のCSVファイルの文字列型/昇順ソート(並べ替え)

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

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

    if      ($in1[0]    le      "AAJJ")             {   
            $sort_record01{$key}    =   $line1;    
    }   
   

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

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

    $tmp    =   $line1;
    $tmp    =~  s/(?:\x0D\x0A|[\x0D\x0A])?$/,/; 
    @in1    =   map {/^"(.*)"$/ ? scalar($_ = $1, s/""/"/g, $_) : $_}   
                ($tmp =~ /("[^"]*(?:""[^"]*)*"|[^,]*),/g);  
   

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

    @in1    =   split(",",$line1,-1);
   

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

    @in1    =   split("\t",$line1,-1);
   
【スクリプト】
# csvsort_many.pl   
# 内容 :大量のCSVファイルを文字列型で昇順にソート(並べ替え)する   
# Copyright (c) 2011 Mitsuo Minagawa, All rights reserved.
# (minagawa@fb3.so-net.ne.jp)  
# 使用方法 : c:\>perl csvsort_many.pl   
#   
open(IN1,"input.txt");  
open(OUT1,">output.txt");   

# ソート(並べ替え)キーの入っている項目番号    
@sortkey        =   (0,1);  
# ソート(並べ替え)用のキー    
$key            =   undef;  
# ソート(並べ替え)するキーとレコードをセットするハッシュ  
%sort_record01  =   (); 
%sort_record02  =   (); 
%sort_record03  =   (); 
%sort_record04  =   (); 
%sort_record05  =   (); 
%sort_record06  =   (); 
%sort_record07  =   (); 
%sort_record08  =   (); 
%sort_record09  =   (); 
%sort_record10  =   (); 
%sort_record11  =   (); 
%sort_record12  =   (); 

# 入力件数  
$in1_ctr    =   0;  
# ファイル入力  
while   ($line1 =   <IN1>)  {   
# 対象ファイルをカンマで分解する    
    chomp($line1);  
    $tmp    =   $line1;     
    $tmp    =~  s/(?:\x0D\x0A|[\x0D\x0A])?$/,/; 
    @in1    =   map {/^"(.*)"$/ ? scalar($_ = $1, s/""/"/g, $_) : $_}   
                ($tmp =~ /("[^"]*(?:""[^"]*)*"|[^,]*),/g);  
# 対象ファイルをタブで分解する  
#   @in1    =   split("\t",$line1,-1);  
    # ソート(並べ替え)キーとなる項目内容をタブをはさんで連結してキーとする    
    foreach $sortno (@sortkey)  {   
        $key    .=  $in1[$sortno] . "\t";   
    }   
    #レコードキーが同一でレコードが集約されてしまう場合にセット 
    $key    .=  sprintf("%08d",$recno); 
    # ソート(並べ替え)キーとソート(並べ替え)キーに対応する
    # レコード(CSVファイルの各行)をハッシュにセットする 
    if      ($in1[0]    le      "AAJJ")             {   
            $sort_record01{$key}    =   $line1; 
    }   
    elsif   ($in1[0]    le      "AAVV")             {   
            $sort_record02{$key}    =   $line1; 
    }   
    elsif   ($in1[0]    le      "BBJJ")             {   
            $sort_record03{$key}    =   $line1; 
    }   
    elsif   ($in1[0]    le      "BBVV")             {   
            $sort_record04{$key}    =   $line1; 
    }   
    elsif   ($in1[0]    le      "CCJJ")             {   
            $sort_record05{$key}    =   $line1; 
    }   
    elsif   ($in1[0]    le      "CCVV")             {   
            $sort_record06{$key}    =   $line1; 
    }   
    elsif   ($in1[0]    le      "XXJJ")             {   
            $sort_record07{$key}    =   $line1; 
    }   
    elsif   ($in1[0]    le      "XXVV")             {   
            $sort_record08{$key}    =   $line1; 
    }   
    elsif   ($in1[0]    le      "YYJJ")             {   
            $sort_record09{$key}    =   $line1; 
    }   
    elsif   ($in1[0]    le      "YYVV")             {   
            $sort_record10{$key}    =   $line1; 
    }   
    elsif   ($in1[0]    le      "ZZJJ")             {   
            $sort_record11{$key}    =   $line1; 
    }   
    elsif   ($in1[0]    le      "ZZVV")             {   
            $sort_record12{$key}    =   $line1; 
    }   
    $in1_ctr++; 
    $key    =   undef;  
}   
# ハッシュのキーをソート(並べ替え)し、キー順にレコードを出力する  
foreach $recno  (sort   keys    %sort_record01) {   
        print OUT1 "$sort_record01{$recno}\n";  
}   
foreach $recno  (sort   keys    %sort_record02) {   
        print OUT1 "$sort_record02{$recno}\n";  
}   
foreach $recno  (sort   keys    %sort_record03) {   
        print OUT1 "$sort_record03{$recno}\n";  
}   
foreach $recno  (sort   keys    %sort_record04) {   
        print OUT1 "$sort_record04{$recno}\n";  
}   
foreach $recno  (sort   keys    %sort_record05) {   
        print OUT1 "$sort_record05{$recno}\n";  
}   
foreach $recno  (sort   keys    %sort_record06) {   
        print OUT1 "$sort_record06{$recno}\n";  
}   
foreach $recno  (sort   keys    %sort_record07) {   
        print OUT1 "$sort_record07{$recno}\n";  
}   
foreach $recno  (sort   keys    %sort_record08) {   
        print OUT1 "$sort_record08{$recno}\n";  
}   
foreach $recno  (sort   keys    %sort_record09) {   
        print OUT1 "$sort_record09{$recno}\n";  
}   
foreach $recno  (sort   keys    %sort_record10) {   
        print OUT1 "$sort_record10{$recno}\n";  
}   
foreach $recno  (sort   keys    %sort_record11) {   
        print OUT1 "$sort_record11{$recno}\n";  
}   
foreach $recno  (sort   keys    %sort_record12) {   
        print OUT1 "$sort_record12{$recno}\n";  
}   

close(IN1); 
close(OUT1);    
   
【スクリプトとデータのサンプル】

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

【スクリプトの解説】

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

入力データの振り分け

    # ソート(並べ替え)キーとソート(並べ替え)キーに対応する
    # レコード(CSVファイルの各行)をハッシュにセットする 
    if      ($in1[0]    le      "AAJJ")             {   
            $sort_record01{$key}    =   $line1; 
    }   
    elsif   ($in1[0]    le      "AAVV")             {   
            $sort_record02{$key}    =   $line1; 
    }   
    elsif   ($in1[0]    le      "BBJJ")             {   
            $sort_record03{$key}    =   $line1; 
    }   
    elsif   ($in1[0]    le      "BBVV")             {   
            $sort_record04{$key}    =   $line1; 
    }   
    elsif   ($in1[0]    le      "CCJJ")             {   
            $sort_record05{$key}    =   $line1; 
    }   
    elsif   ($in1[0]    le      "CCVV")             {   
            $sort_record06{$key}    =   $line1; 
    }   
    elsif   ($in1[0]    le      "XXJJ")             {   
            $sort_record07{$key}    =   $line1; 
    }   
    elsif   ($in1[0]    le      "XXVV")             {   
            $sort_record08{$key}    =   $line1; 
    }   
    elsif   ($in1[0]    le      "YYJJ")             {   
            $sort_record09{$key}    =   $line1; 
    }   
    elsif   ($in1[0]    le      "YYVV")             {   
            $sort_record10{$key}    =   $line1; 
    }   
    elsif   ($in1[0]    le      "ZZJJ")             {   
            $sort_record11{$key}    =   $line1; 
    }   
    elsif   ($in1[0]    le      "ZZVV")             {   
            $sort_record12{$key}    =   $line1; 
    }   
   

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

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

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

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

    if      ($in1[0]    le      "AAJJ")             {
            $sort_record01{$key}    =   $line1;    
    }   
   

上記の箇所については、ハッシュにセットするのをやめ、以下のように通常の配列「@sort_record01a」にセットします。

    if      ($in1[0]    le      "AAJJ")             {
            $sort_record01a[$in1_ctr]   =   $line1;
    }   
   

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

    foreach $recno  (sort   keys    %sort_record01) {   
            print   OUT1    "$sort_record01{$recno}\n";  
    }   
   

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

    foreach $i (0..$#sort_record01a) {
            print   OUT1    "$sort_record01a[$i]\n";
    }
   


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