[Top] [CGI研究室]
ファイルソートについて

ファイルソートの説明とサンプル

  1. ファイルソートとは
    ファイルソートとは、 ファイル内のレコードをソートする処理の事です。

    小さいファイルであれば、全レコードを一括してメモリに読み込み、 ソートした後に書き戻せば済みますが、大きなファイルの場合では メモリ消費量が大きすぎて向きません。
    その場合にファイルソートを使用します。

    ※CGIでそこまで大きなファイルをソートする必要が あるかどうかは疑問ですが、アクセス解析ログなどをローカルでソートしたい 場合等には便利かもしれません。

  2. ファイルソートの手法
    ファイルソートの手法はいろいろありますが、ここではメモリ上のソートと マージを組み合わせた方法を説明します。

    1. レコードをブロック分けしてソート
      ・ソート元のファイルから一定のレコード数ごとにブロック分けしてメモリ上へ読み込み、メモリ上でソートしてから、テンポラリファイル1へ書き出します。
    2. 各ブロックをマージ
      ・テンポラリファイル1の各ブロックをマージしてテンポラリファイル2へ書き出します。
    3. ソート元ファイルとの入替
      ・ソート元のファイルを削除し、テンポラリファイル2をソート元のファイル名にリネームします。
      ・使用済みのテンポラリファイル1も削除します。

    ※上記の方法では、処理途中で何らかの異常があった場合でも 元ファイルを壊さないようにする為に、ソート元のファイルと同じサイズのテンポラリ ファイルを2つ作成する処理にしています。

  3. ファイルソートのサンプル
    ファイルソートのサンプルを以下に示します。

    $rtndata = &FileSort( 'sort.dat', ',', 1000, 1, 0 ) ;
    		・
    		・
    		・
    		・
    
    
    #-----------------------------------------------------------------------#
    #	ファイルソート
    #	 パラメータ:	$fn		…ファイル名
    #			$sep		…レコード内の項目の区切り文字
    #			$blocksize	…1ブロック内のレコード数
    #			@keytbl		…ソートキーテーブル(カンマ区切りのキー位置/0〜)
    #	 リターン :			…1:正常
    #					  0:エラー
    #	 出力用変数:	$FS_Sep		…レコード内の項目の区切り文字(ソートサブルーチンへの受け渡し用)
    #			@FS_KeyTbl	…キーテーブル(ソートサブルーチンへの受け渡し用)
    #-----------------------------------------------------------------------#
    sub FileSort
    {
    	local( $fn, $sep, $blocksize, @keytbl ) = @_ ;
    	local( $i, $tmpfn1, $tmpfn2, @tmpbuf, @seektbl, @cnttbl, $tmprec, @tmptbl ) ;
    	local( @tmp0, @tmp1, $cmp01 ) ;
    	local( $rtn ) = 0 ;
    
    	$FS_Sep    = $sep ;
    	@FS_KeyTbl = @keytbl ;
    
    	$tmpfn1 = $fn . '.tmp1' ;
    	$tmpfn2 = $fn . '.tmp2' ;
    
    	@seektbl = () ;
    	@cnttbl = () ;
    
    	while (1)
    	{
    		if ( ! open( FSIN  , $fn ) )		{ last ; }
    		if ( ! open( FSOUT1, ">$tmpfn1" ) )	{ last ; }
    		if ( ! open( FSOUT2, ">$tmpfn2" ) )	{ last ; }
    
    		while (1)
    		{
    			if ( eof(FSIN) )	{ last ; }
    			push( @seektbl, tell( FSOUT1 ) ) ;
    			@tmpbuf = () ;
    			for ( $i=0; $i<$blocksize; $i++ )
    			{
    				$tmpbuf[$i] =  ;
    				if ( eof(FSIN) )	{ last ; }
    			}
    			@tmpbuf = sort FSortSub @tmpbuf ;
    			print FSOUT1 @tmpbuf ;
    			push( @cnttbl, $#tmpbuf + 1 ) ;
    		}
    		close( FSIN ) ;
    
    		if ( $#cnttbl == 0 )
    		{
    			print FSOUT2 @tmpbuf ;
    		}
    		else
    		{
    			if ( ! open( FSOUT1, "$tmpfn1" ) )	{ last ; }
    			@tmptbl = () ;
    			@tmpbuf = () ;
    			for ( $i=0; $i<=$#cnttbl; $i++ )
    			{
    				if ( $cnttbl[$i] > 0 )
    				{
    					seek( FSOUT1, $seektbl[$i], 0 ) ;
    					$tmprec =  ;
    					$tmprec =~ s/\n/$sep$i\n/ ;
    					push( @tmptbl, $tmprec ) ;
    					$seektbl[$i] = tell( FSOUT1 ) ;
    					$cnttbl[$i] -- ;
    				}
    			}
    
    			@tmptbl = sort FSortSub @tmptbl ;
    			while ( $#tmptbl >= 0 )
    			{
    				$tmprec = shift( @tmptbl ) ;
    				$tmprec =~ s/$sep(\d+)\n/\n/ ;
    				$i = $1 ;
    				push( @tmpbuf, $tmprec ) ;
    				if ( $#tmpbuf + 1 >= $blocksize )
    				{
    					print FSOUT2 @tmpbuf ;
    					@tmpbuf = () ;
    				}
    				if ( $cnttbl[$i] > 0 )
    				{
    					seek( FSOUT1, $seektbl[$i], 0 ) ;
    					$tmprec =  ;
    					$tmprec =~ s/\n/$sep$i\n/ ;
    					unshift( @tmptbl, $tmprec ) ;
    					$seektbl[$i] = tell( FSOUT1 ) ;
    					$cnttbl[$i] -- ;
    				}
    				if ( $#tmptbl >= 1 )
    				{
    					@tmp0 = split( /$sep/, $tmptbl[0] ) ;
    					@tmp1 = split( /$sep/, $tmptbl[1] ) ;
    					for ( $i=0; $i<=$#FS_KeyTbl; $i++ )
    					{
    						$cmp01 = $tmp0[ $keytbl[$i] ] cmp $tmp1[ $keytbl[$i] ] ;
    						if ( $cmp01 != 0 )	{ last; }
    					}
    
    					if ( $cmp01 > 0  )
    					{
    						@tmptbl = sort FSortSub @tmptbl ;
    					}
    				}
    			}
    			close( FSOUT1 ) ;
    			if ( $#tmpbuf >= 0 )	{ print FSOUT2 @tmpbuf ; }
    		}
    		close( FSOUT2 ) ;
    
    		unlink( $fn ) ;
    		rename( $tmpfn2, $fn ) ;
    
    		$rtn = 1 ;
    		last ;
    	}
    	close( FSIN ) ;
    	close( FSOUT1 ) ;
    	close( FSOUT2 ) ;
    	unlink( $tmpfn1 ) ;
    	unlink( $tmpfn2 ) ;
    
    	$rtn ;
    }
    
    #-----------------------------------------------------------------------#
    #	ファイルソート用ソートサブルーチン
    #	 パラメータ:	$a,$b		…比較レコード
    #	 入力用変数:	$FS_Sep		…レコード内の項目の区切り文字(ソートサブルーチンへの受け渡し用)
    #			@FS_KeyTbl	…キーテーブル(ソートサブルーチンへの受け渡し用)
    #	 リターン :			… 0: $a=$b
    #					   1: $a>$b
    #					  -1: $a<$b
    #-----------------------------------------------------------------------#
    sub FSortSub
    {
    	local( @tmpA ) = split( /$FS_Sep/, $a ) ;
    	local( @tmpB ) = split( /$FS_Sep/, $b ) ;
    	local( $i ) ;
    	local( $rtn ) = 0 ;
    
    	for ( $i=0; $i<=$#FS_KeyTbl; $i++ )
    	{
    		$rtn = $tmpA[ $FS_KeyTbl[$i] ] cmp $tmpB[ $FS_KeyTbl[$i] ] ;
    		if ( $rtn != 0 )	{ last; }
    	}
    
    	$rtn ;
    }
    


CGI Saloon