DOBON.NET DOBON.NETプログラミング掲示板過去ログ

巨大なファイルの読み込み

環境/言語:[WindowsXP ProSP2 VisualC# 2005 Express]
分類:[.NET]

こんにちは。
巨大なファイルを読み込みたいのですが、どうしても解決しません。
ご教授ください。

FileStream fs = new FileStream(@"c:\test.txt", FileMode.Open, FileAccess.Read);

byte[] bs = new byte[fs.Length];

とした場合、c:\text.txtがあまりに巨大だと(当方の環境では655,710,878 バイトでかならず再現します)、
「System.MemoryOutOfExceptionの例外がスローされました。」
と、なってしまいます。
こういう場合どうすればよろしいですか?
> FileStream fs = new FileStream(@"c:\test.txt", FileMode.Open, FileAccess.Read);
>
> byte[] bs = new byte[fs.Length];
>
> とした場合、c:\text.txtがあまりに巨大だと(当方の環境では655,710,878 バイトでかならず再現します)、

例外が発生しないためには、
約655MB分の連続した空きメモリが必要だと思いますが、
ごく一般的環境でそれは無茶というものではないでしょうか。

例えば、数MB分くらいのバイト配列を確保するに留めて、
その単位でファイルからデータの読み込みを行うとか。

> 「System.MemoryOutOfExceptionの例外がスローされました。」

OutOfMemoryExceptionですよね?(^^;
メモリ不足ということで、メモリを増やすか、
1度に消費するメモリが大きくならないようにプログラミングするかどちらかですね。
返信ありがとうございます。
大変参考になったのですが、解決に結びつきません。

> 例外が発生しないためには、
> 約655MB分の連続した空きメモリが必要だと思いますが、
> ごく一般的環境でそれは無茶というものではないでしょうか。
>
> 例えば、数MB分くらいのバイト配列を確保するに留めて、
> その単位でファイルからデータの読み込みを行うとか。

現在自分のコードはTipsで紹介されていたようなコードになっているのですが、
FileStream fs = new FileStream(@"c:\test.txt", FileMode.Open, FileAccess.Read);
byte[] bs = new byte[fs.Length];
fs.Read(bs, 0, bs.Length);
fs.Close();
stringA = function(bs); //読み込んだファイルを処理してstringを返す関数

例えば
byte[] bs = new byte[1024];
としたした場合、ループ文など使って1024づつ読んでいくとして、
function()にfsの内容をすべてbyte型で渡したいので、
ループするたびに今読みこんだbsが無くなってしまうわけにはいけない。
1024づつ読み込んで、すべて読み込むまで今までの内容を保持し、関数に渡す
、という処理をしたいのですがわからなくなってしまいました。

何を伝えたいかよくわからない文章になってしまいましたが、
上のコードを数百Mbあるような巨大なファイルにも対応できるように書き直したいのです。


> OutOfMemoryExceptionですよね?(^^;
そうですね^^;写し間違えてしまいました。

よろしくお願いします。
> 現在自分のコードはTipsで紹介されていたようなコードになっているのですが、
> FileStream fs = new FileStream(@"c:\test.txt", FileMode.Open, FileAccess.Read);
> byte[] bs = new byte[fs.Length];
> fs.Read(bs, 0, bs.Length);
> fs.Close();
> stringA = function(bs); //読み込んだファイルを処理してstringを返す関数
>
> 例えば
> byte[] bs = new byte[1024];
> としたした場合、ループ文など使って1024づつ読んでいくとして、

私の前回の投稿での案がこれですね。
この方法を取るのは、ファイルが大きすぎるために、
全データを一度にメモリ上に置けないからですね。

> function()にfsの内容をすべてbyte型で渡したいので、
> ループするたびに今読みこんだbsが無くなってしまうわけにはいけない。

全データをメモリ上に置けない以上、処理の対象データを一度に全部、
配列に読み込むことを前提とした方法をあきらめる必要があると思います。

> 1024づつ読み込んで、すべて読み込むまで今までの内容を保持し、関数に渡す
> 、という処理をしたいのですがわからなくなってしまいました。

1つ目の1024バイト
2つ目の1024バイト
3つ目の1024バイト
 :

のようにデータを読み込むとすると
1つ目のデータ読み込み→1つ目のデータに対する処理→1つ目のデータを破棄
2つ目のデータ読み込み→2つ目のデータに対する処理→2つ目のデータを破棄
3つ目のデータ読み込み→3つ目のデータに対する処理→3つ目のデータを破棄

のように処理していけばよいと思います。
2つ目のデータを処理するときに1つ目のデータが必要になることがあるかもしれませんが、
果たして全データないと処理できないでしょうか?
それとも1つ目〜3つ目まであれば処理できるのでしょうか?
その辺は、具体的なやりたいことに依存するでしょう。

> 何を伝えたいかよくわからない文章になってしまいましたが、
> 上のコードを数百Mbあるような巨大なファイルにも対応できるように書き直したいのです。

整理すると以下のような手順ですね。

1.ある程度のデータはバイト配列に読み込む。
2.ファイルのどこからどこまでを読んだデータかも覚えておく。
3.ファイルのデータに何かの処理をする際に、処理対象のデータが、
バイト配列にあれば、そちらを使って処理する。
4.なければ、ファイルから新たなデータをバイト配列に読み込む。
このとき前回のバイト配列データはメモリからは破棄。

こういった手順で必要な処理が終わるまで続ける。
> 上のコードを数百Mbあるような巨大なファイルにも対応できるように書き直したいのです。

既に指摘されているように、「一度に」数百 MB のデータを byte 配列で保持するのは無茶な設計です。

どうしてもということなら、64bit 環境に移行しましょう。

そうすれば、数 GB のデータでも byte 配列で扱うことが(理論上は)可能です。
よねKENさん、渋木宏明さん、ご指摘ありがとうございます。
全体的にコードを書き直しまし、解決しました。

ありがとうざいました。
解決済み!

DOBON.NET | プログラミング道 | プログラミング掲示板