■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-3.CSVファイルの文字列型・数値型/昇順・降順ソート(その1)

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

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

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

実際に応用する場合は、入出力データのファイル名を変更し、「$a[2] <=> $b[2]」の部分などを変更します。2つ以上のキーで並べ替えを行うには、「「$a[2] <=> $b[2]」の後に「or」をつけて指定していきます。この例では、2番目の項目を第1キーに、1番目の項目を第2キーとして並べ替えるように指定しています。Perlでは、配列のインデックスは「0」から開始されますので、1番目の項目を「0」、2番目の項目を「1」として指定することに注意してください。

文字列型を昇順に並べ替えるのであれば、「$a[1] cmp $b[1]」のように、文字列型を降順に並べ替えるのであれば、$aと$bを入れ替えて「$b[1] cmp $a[1]」のように指定します。[]の中には何番目の項目を並べ替えるのかを指定します。

また、数値型を昇順に並べ替えるのであれば、「$a[1] <=> $b[1]」のように、数値型を降順に並べ替えるのであれば、$aと$bを入れ替えて「$b[1] <=> $a[1]」のように指定します。文字列型と異なる点は「cmp」とする部分を「<=>」とする点です。

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

10万件以上のデータをソート(sort:並べ替え)する場合には、[2-4-5.大量のCSVファイルのソートする場合の考慮事項][2-4-7.大量のCSVファイルの文字列型・数値型/昇順・降順ソート]の両方を参考にして、スクリプトを作成してください(100万件程度のデータであれば、ソート(sort:並べ替え)できます)。

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

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

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

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

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

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

  # CSVファイルの入力   
@record     =   (); 
$in1_ctr    =   0;  
while   ($line1 =   <IN1>)  {   

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

    $record[$in1_ctr]   =   join("\t",@in1); 
    $in1_ctr++; 

}   

  # CSVファイルの並べ替え   
# 文字列タイプの昇順:$a[1]  cmp $b[1] #2番目の項目の昇順    
# 文字列タイプの降順:$b[0]  cmp $a[0] #1番目の項目の降順    
# 数値タイプの昇順:$a[1]    <=> $b[1] #2番目の項目の昇順    
# 数値タイプの降順:$b[0]    <=> $a[0] #1番目の項目の降順    

    @sort_rec   =   sort    {       
                                my @a   =   split("\t", $a); 
                                my @b   =   split("\t", $b); 
                                $a[1]   <=> $b[1]   
                            or  $a[0]   cmp $b[0];  
                    }   @record;    

  # CSVファイルの出力   
foreach $record (@sort_rec) {   
    print   OUT1    "$record\n";    
}   

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

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

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

【スクリプトの解説】

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

ソート(並べ替え)処理

    @sort_rec   =   sort    {       
                                my @a   =   split("\t", $a); 
                                my @b   =   split("\t", $b); 
                                $a[1]   <=> $b[1]   
                            or  $a[0]   cmp $b[0];  
                    }   @record;    
   

上記の「@record」には、並べ替えをする前のレコードが入っています。各項目をタブ区切りしたレコードを要素とする配列となっています。このレコードを「my @a = split("\t", $a);」でタブ区切りして、必要な項目で並べ替えられるようにします。「$a」と「$b」にはそれぞれ隣り合うレコードが自動的に入ります。この「$a」と「$b」の名前は変えることができません。

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

また、数値型を昇順に並べ替えるのであれば、「$a[1] <=> $b[1]」のように、数値型を降順に並べ替えるのであれば、$aと$bを入れ替えて「$b[1] <=> $a[1]」のように指定します。文字列型と異なる点は「cmp」とする部分を「<=>」とする点です。

ソート(並べ替え)のキーが1つの場合

なお、並べ替えの項目を指定するキーが1つであれば、以下のようになり、

    @sort_rec   =   sort    {       
                                my @a   =   split("\t", $a); 
                                my @b   =   split("\t", $b); 
                                $a[1]   <=> $b[1];
                    }   @record;    
   

ソート(並べ替え)キーを指定する部分では「$a[1] <=> $b[1];」のように最後にセミコロンをつけます。

ソート(並べ替え)のキーが3つの場合

また、並べ替えの項目を指定するキーが3つであれば、

    @sort_rec   =   sort    {       
                                my @a   =   split("\t", $a); 
                                my @b   =   split("\t", $b); 
                                $a[1]   <=> $b[1]   
                            or  $a[0]   cmp $b[0]
                            or  $a[2]   cmp $b[2];  
                    }   @record;    
   

となります。上記では、ソート(並べ替え)キーを指定する部分では「or $a[2] cmp $b[2];」のように最後にセミコロンをつけ、それ以外の「or $a[0] cmp $b[0]」には最後にセミコロンをつけませんので、この点に注意しください。

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

foreach $record (@sort_rec) {   
    print   OUT1    "$record\n";    
}   
   

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

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

【入力データ:ソート(並べ替え)前のデータ(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-2013 Mitsuo Minagawa, All rights reserved.