電子書籍の厳選無料作品が豊富!

下記、perlスクリプトで通常は逆順に並べ替えることができると思うのですが、ファイルの行数が1000万行程度あり、実行すると、PCのメモリを食いつぶしてしまいまともに動作しません。
どのようにすればよいでしょうか?
もしくは、これに相当するようなフリーソフトはありますか?
ご存知の方教えてください。
宜しくお願い致します。


#!/usr/local/bin/perl

open(OUT, "1all.txt");
open (FH , ">out_all.txt");
print FH foreach( reverse <OUT> );
close(FH);
close(OUT);

A 回答 (4件)

サンプルファイルとかがない上に、そんなに巨大ファイルを扱ったことがないので、上手くいくかどうか分かりませんが、試行錯誤を含めたサンプルプログラムを書いてみました。



@data=(1..100);
$name="file.txt";#完成ファイル名
$line=10;#一度に処理する行数

my($dir)=&makedir;#作業ディレクトリ作成

my($fn)=(0);
while(@data){
my(@save);
for($n=$line;$n>0;--$n){#$line行読み込んで、逆順に配列に入れる
unshift(@save,shift(@data));
}
++$fn;
&fsave($fn,@save);#ファイル一時保存
sleep 1;#1秒休憩。休憩できたら嬉しいな。
print "$fn完了\n";#待ってる間が怖いので、終了するたびにコメント。本当はいらない
}

&makefile($dir,$name);#ファイル結合
rmdir($dir);#一時ディレクトリ削除

sub makedir{#作業用ディレクトリ作成
my($fd)=(0);
while(-d "tmp$fd"){++$fd;}
mkdir("tmp$fd",0755);
return "tmp$fd";
}

sub fsave{#一時ファイルの作成
my($f)=shift(@_);
open(FILE,">$dir/$f")||die "Open Error";
print FILE @_;
close(FILE);
}

sub makefile{#ファイル結合
opendir(DIR,$_[0]);
my(@list)=readdir(DIR);
closedir(DIR);

shift(@list);#.削除。ここら辺かっこ悪いけど
shift(@list);#..削除。まぁ、動けばいいということで。

#ファイルの並び替え。数字大……小へ
my($i,$j);
for($i=$#list;$i>0;--$i){
for($j=0;$j<$i;++$j){
if($list[$j]<$list[$j+1]){
my($wk)=$list[$j];
$list[$j]=$list[$j+1];
$list[$j+1]=$wk;
}else{}
}
}

my($name)=$_[1];

while(@list){
my($file)=shift(@list);

#データ読み込み
open(FILE,"<$dir/$file")||die "Open Error";
my(@tmp)=<FILE>;
close(FILE);

#データ追加書き込み
open(FILE,">>$name")||die "Open Error";
print FILE @tmp;
close(FILE);

unlink("$dir/$file");

}
}

kumozさんの小分けにファイルに分けて、後で結合方式を使ってみたサンプルプログラムです。数を多くしたら待つ時間が嫌になったので、設定された数は少なめですが、そこら辺はいじって試して見てください。
処理が多くなるので、sleepで一定処理ごとに休ませてみたのですが、本当に休めるのかどうか分かりません。ただ、$lineの数だけファイルにまとめたら、一秒動作を止めてみました。意味がないならsleep関数はいりませんね。
工夫したところは、一時フォルダに一時ファイルをまとめて入れて、後で削除するようにしたところ。それとファイルの開き方を追加書き込みにして、どんどんとデータを追加していく方法を取ったことくらいでしょうか。できる限り、メモリに大量のデータを溜め込まないようにしてみた……つもりです。
なるべく流れをすっきりさせることを意識したので、脂肪がたくさんついている上に、何となく幼いプログラムになってしまいましたが、役に立てば嬉しいです。
    • good
    • 0
この回答へのお礼

皆様へ
たくさんの回答いただきありがとうございます。
perl初心者でしっかり、コードを研究して実行しようと思います。
回答いただき、本当に、ありがとうございました!!

お礼日時:2007/08/14 22:04

ディスクスペースに余裕があれば、分割して処理する方法があると思います。


以下のプログラムは、10 万行ずつ逆順に並べ替えたものをファイルに保存し
ておいて、後から結合しています。

use strict;
open IN, "all.txt" or die "Can't open all.txt: $!";
my @line_100000; my $file_no = 0;

while (my $line = <IN>) {
unshift @line_100000, $line;
if (@line_100000 == 100000 or eof(IN)) {
$file_no++;
my $part = "part$file_no";
open OUT, ">$part" or die "Can't open $part: $!";
print OUT @line_100000;
close OUT or die "Can't close $part: $!";
@line_100000 = ();
}
}

close IN or die "Can't close all.txt: $!";
open OUT, ">out_all.txt" or die "Can't open out_all.txt: $!";

foreach my $i (reverse(1 .. $file_no)) {
my $part = "part$i";
open IN, "$part" or die "Can't open $part: $!";
print OUT <IN>;
close IN or die "Can't close $part: $!";
unlink $part or die "Can't delete $part: $!";
}

close OUT or die "Can't close out_all.txt: $!";
    • good
    • 0

こんにちは、


標準モジュールじゃなくても良いなら、
File::ReadBackwards というモジュールがあるみたいですよ。

本で見て、試しに使ってみた程度ですけど、
ダウンロードしたモジュールを適当なディレクトリに置いて
use lib qw(適当なディレクトリ);
したら使えました。

参考URL:http://search.cpan.org/~uri/File-ReadBackwards-1 …
    • good
    • 0

use Tie::File;


my @lines;
tie @lines, 'Tie::File', '1all.txt' or die $!;

して、@linesを逆から読むとか。

for (my $i=@lines; $i>0; $i--) {
print $lines[$i];
}


詳しい使い方は perldoc Tie::File で。
Perlのバージョンが5.8.0以降ならTie::Fileは標準で
ついているはず。
    • good
    • 0

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!