4GBを超えるファイルの読込

[戻る]
ももたろう 2005/05/14(土) 11:29:33
みなさん

こんにちは。
あるファイル内からfseekを用いて中身のデータを取り出すプログラムを
Visual C++ 6.0、コンソールアプリケーションで作りました。

fseekだと読み出せるファイルの大きさは、offsetバイト(long型)で
決まってしまい、2GB程度のファイルまでしか指定出来ません。4GBを超える
ファイルで同様の操作をしたいと思っています。

プログラムとしては
if((fp=fopen(fname_in,"rb"))==NULL)
{
 fprintf(stderr, "Input File Open Error!\n");
 exit(0);
}
else
 printf("Open input file %s\n",fname_in);
}

fseek(fp, HEADER_SIZE, SEEK_SET);
fread(data, sizeof(RAW_Data_info), 1, fin); //以下データ読込部分

HEADER_SIZEが、4GBを超えるような大容量ファイルを読込たい場合には
具体的にはどのように記述をしたらよいのでしょうか?
よろしくお願い致します。

ori 2005/05/14(土) 11:47:21
標準ライブラリではどうしようもないので
WinAPIのCreateFile, Read/WriteFile等を使うことになります。

デフォルトの名無しさん 2005/05/14(土) 14:27:15
> 標準ライブラリではどうしようもないので
> WinAPIのCreateFile, Read/WriteFile等を使うことになります。

え!?標準の範囲でもfseekの現在位置から
相対ジャンプを繰り返す手順でいいと思ったけど。
VC6はそれで動かないの?

シャノン 2005/05/14(土) 20:46:05
> え!?標準の範囲でもfseekの現在位置から
> 相対ジャンプを繰り返す手順でいいと思ったけど。
> VC6はそれで動かないの?

VC6 ではどうだか知りませんが、VC7 では動きません。
現在の実装では、内部でオフセット値を 32bit で管理していたり、SetFilePointer の
第3引数を NULL で呼んだりしていますので。
7 で動かないのが 6 で動くってのも考えにくいので、たぶん 6 でも動きません。

シャノン 2005/05/14(土) 21:04:47
ついでに言うと、標準ライブラリでは 2GB を超えるファイルは扱えません。

ももたろう 2005/05/16(月) 08:38:10
みなさま

いろいろとコメントありがとうございます。
コンソールアプリで十分なのですが、WinAPIのCreateFile
, SetFilePointerを使ってダイアログベースかSDIにして
プログラムを作り直してみようと思います。

NowNow 2005/05/16(月) 08:48:07
> コンソールアプリで十分なのですが、WinAPIのCreateFile
> , SetFilePointerを使ってダイアログベースかSDIにして
> プログラムを作り直してみようと思います。

コンソールアプリでも、Win32のAPI使えませんでしたっけ?
ファイルアクセス関連のCreateFileとWriteFileを使ってコンソールアプリを書いた記憶
があります。

ももたろう 2005/05/16(月) 09:00:33
NowNowさん、はじめまして

本当ならうれしいのですが...。CreateFileでHADNLE hFile=::CreateFile()
と書くと思うのですが、HADNLE hFileの定義は具体的には、どこで定義したら
よいのでしょうか?

Blue 2005/05/16(月) 09:17:23
>HADNLE hFileの定義は具体的には、どこで定義したらよいのでしょうか?
ダイアログやSDIでつくったばあいですか?
どこでファイル処理をさせたいのかわかりませんが、ウィンドウを表示したら
その結果が表示されるというならば、OnCreate(SDI)やOnInitDialog(ダイアログ)に記述
するのかな。

#include<windows.h>を宣言すればコンソールアプリでも使えます。>Win32API

NowNow 2005/05/16(月) 09:34:51
VC6でコンソールアプリで作成してみました。

#include <stdio.h>
#include <windows.h>
int main(void)
{
    char data[] = {1,2,3,4};
    HANDLE hFile = CreateFile("Test.dat", GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL, NULL);
    if(hFile == INVALID_HANDLE_VALUE)
    {
        puts("CreateFile Error");
        return 0;
    }
    DWORD write = 0;
    WriteFile(hFile, data, 4, &write, NULL);
    CloseHandle(hFile);
    puts("end");
    return 0;
}

これで、コンソール画面にはendと表示され、Test.datファイルにはバイナリエディタで
見ると01 02 03 04と格納されたファイルが作成されていました。
BlueさんのResにもありますが、#include <windows.h>を追加すればOKのはずです。
VC6のコンソールアプリの標準設定では、libファイルの追加とかは行わなくて上のプログ
ラムであればリンクエラーも発生しませんでした。

ももたろう 2005/05/16(月) 09:52:34
NowNowさん、Blueさん

ありがとうございます。NowNowさん、ソースまでご教示頂きありがとうございます。
NowNowさんのご教示の前にわたしもとりあえず、現存のプログラムを変更しつつ、
コンパイルが通る、通らないをみながらやってみました。SetFilePointerExの
ところで引っかかってしまい、ヘルプで何でだろうかと考えているところです。

//TEST_pro.cpp
#include <stdio.h>
#include "TEST_pro.h"
#include <windows.h>

HANDLE hfile; //どこが適当かわからなかったので、こんなところにかいてしまいました。

int main(void)
{
if((fp=fopen(fname_in,"rb"))==NULL)
{
 fprintf(stderr, "Input File Open Error!\n");
 exit(0);
}
else
{
hfile=::CreateFile(fname_in, GENERIC_READ, FILE_SHARE_READ, NULL, PEN_EXISTING, 
FILE_ATTRIBUTE_NORMAL, NULL);
 printf("Open input file %s\n",fname_in);
}

}

//fseek(fp, HEADER_SIZE, SEEK_SET);
// SetFilePointerEx(hfile,HEADER_SIZE, NULL, FILE_BEGIN); ->このときは、C2440エ
ラー
  SetFilePointerEx(hfile, 3221225471, NULL, FILE_BEGIN); ->このときは、CC2664エ
ラー

fread(data, sizeof(RAW_Data_info), 1, fin); //以下データ読込部分


}

NowNow 2005/05/16(月) 10:27:27
SetFilePointerExの第二引数はLARGE_INTEGERです。

LARGE_INTEGER li;
li.LowPart = 0xBFFFFFFF;
li.HighPart = 0;
SetFilePointerEx(hfile, &li, NULL, FILE_BEGIN);

試していませんが、こんな感じで動作するのではないでしょうか?
LARGE_INTEGERの詳細は、
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winprog/winprog/large_integer_str.asp
こちらにあります。

ももたろう 2005/05/16(月) 13:16:25
NowNowさん、みなさん、ありがとうございます。

SetFilePointerEx(hfile, li, NULL, FILE_BEGIN);
にしてうまくいきました。申し訳ないのですが、もう少しご教示ください。

実は、liは変数でしてfor文で回して大きな値にしていきます。

//li.LowPart = 0xBFFFFFFF;
//li.HighPart = 0;
から
li.LowPart = (6442450943)&0x00000000FFFFFFFF;
li.HighPart = (6442450943)&0xFFFF00000000;
のように変更し、4GB(li.LowPart = 4294967295)までのファイルは、デバック
モードで値が入るのを確認して、大丈夫そうだと解りました。ところが、例えば
6GB程度、値として6442450943、を入れた際、

li.LowPart = 2147483647;
li.HighPart = 0;

となってしまいました。&の取り方をいろいろと変えてみたのですが、うまくいき
ませんでした。6,8,10GBの様なファイルの場合は、どうすればよいのでしょうか?

よろしくおねがいします。

NowNow 2005/05/16(月) 13:37:37
for文でまわしているときのカウンタはどのような変数の型を使っているかわかりません
が、その値が64ビット整数型(LONGLONG)であれば、LARGE_INTEGERのQuadPartに代入す
れば、上位32ビット、下位32ビットを意識せずに代入できます。


li.LowPart =   (6442450943)&0x00000000FFFFFFFF;
li.HighPart = ((6442450943)&0xFFFFFFFF00000000) >> 32;

LowPart、HighPartともに32ビットですので、このようにすればよいのではないでしょうか?
ただし、試していません。

各無し 2005/05/17(火) 11:44:28
こんなのもあるよ。
http://www.codeproject.com/file/64-bit_fileio.asp

ももたろう 2005/05/18(水) 09:52:29
NowNowさん、各無しさん

おはようございます。
お返事が遅くなりすいません。データを入れているサーバーのHDDが調子が悪く
壊れかかっているとかで...。

li.LowPart =   (6442450943)&0x00000000FFFFFFFF;
li.HighPart = ((6442450943)&0xFFFFFFFF00000000) >> 32;

で"li"にきちんと"6442450943"入ることをデバックモードで確認できました。
ありがとうございます。

データの読込は、freadのままだったので、ReadFileにして読み込みファイルエンド
まで行くか、現在、約5GBのファイルを使って確認しています(昨日間違って、freadの
ままで実行させてしまい、2GB計算するのに6時間半かかっていました)。

int i, HEADER_SIZE, HEADER_SIZE_1;
li.LowPart =   (HEADER_SIZE+HEADER_SIZE_1*i)&0x00000000FFFFFFFF;
li.HighPart = ((HEADER_SIZE+HEADER_SIZE_1*i)&0xFFFFFFFF00000000) >> 32;
SetFilePointerEx(hFile, li, NULL, FILE_BEGIN);

//RAW_Data_infoは構造体の構造体
RAW_Data_info *data;
n = ReadFile(hFile, data, sizeof(RAW_Data_info), &dwResult, NULL) ;

また、結果が出ましたらご報告させていただきます。

NowNow 2005/05/18(水) 11:28:23
> int i, HEADER_SIZE, HEADER_SIZE_1;
> li.LowPart =   (HEADER_SIZE+HEADER_SIZE_1*i)&0x00000000FFFFFFFF;
> li.HighPart = ((HEADER_SIZE+HEADER_SIZE_1*i)&0xFFFFFFFF00000000) >> 32;
> SetFilePointerEx(hFile, li, NULL, FILE_BEGIN);

64ビットのアドレス計算が、intで行われています。
intが32ビットだとすると、正しく計算が行われていないかもしれません。

VC6で試したところ、
    LONGLONG ll = 0x7FFFFFFF * 0x00000002;
が、ll = 0xfffffffffffffffeに、
    LONGLONG ll = (LONGLONG)0x7FFFFFFF * 0x00000002;
が、ll = 0x00000000fffffffeになりました。
キャストをするべきかもしれません。

ところで、上位32ビットと下位32ビットを別々に代入しなくても、
    li.QuadPart = (LONGLONG)HEADER_SIZE+HEADER_SIZE_1*i;
このようにしても上位下位分けて代入したのと同じ値になっているようでした。

ももたろう 2005/05/19(木) 09:05:11
[[解決]]
NowNowさん、みなさん

おはようございます。
無事にデータを読み込ませることが出来ました。いろいろとご教示頂き
ありがとうございました。

古いスレッドには追加発言できません。