C言語関係掲示板

過去ログ

No.903 2G以上のファイルへのアクセス方法について

[戻る] [ホームページ]
No.645

2G以上のファイルへのアクセス方法について
投稿者---ケイ(2003/11/13 11:27:56)


はじめまして、ケイと申します。 よろしくお願いします。

2Gバイト以上のファイルにアクセスする場合、ファイルポインタの読み出し、設定が上手くできず困っております。 以下が詳細です。

目的:
5Gバイト以上のデータファイルの中から、特定のデータを検索し抜き出す。
・ ファイルのサイズを取得
・ ファイルの中の特定の位置にファイルポインタを移動
・ 移動後 指定バイト数の読み出しを行い、別ファイルに書き出す。

まず、ファイルサイズの取得を行いたいのですが、以下のソースの fgetpos関数にて、file_pos に返る値が、扱うファイルが2Gバイト以下であれば問題ないのですが、それ以上になりますと「0」になってしまいます。 MSDNを見ても fgetpos は2Gバイト以上のファイルも対応していると書かれてあるのですが、何か使い方が悪いのでしょうか? 助言よろしくお願いします。

以下ソース

//グローバル変数
FILE *in_file, *out_file;
fpos_t file_start_pos = 0;
fpos_t file_end_pos = 0;

/******************************************************************/
/* function : check_file_size()                                   */
/* ファイルサイズをチェックし、表示する。          */
/******************************************************************/

void CTS_CUT32Dlg::check_file_size()
{
	fpos_t file_pos = 0;
	char temp_string[32] = "\0";
	char* temp_string_p;
	int error_flag_in = 0 ; // 1 = error
	int error_flag_out = 0 ; // 1 = error
	int packet_size = analized_packet_size; // 204、188のどちらかが入る
	_int64 temp_int64 = 0;
	long double temp_long_double = 0;
	int int_infile;

	temp_string_p = temp_string;

    // バイナリ モードでファイルを開く
    // m_edit_box_input_file_name はユーザーが入力したファイル名
    
    if ((in_file = fopen( m_edit_box_input_file_name, "r+b" )) == NULL )
 	   error_flag_in = 1;

   // 以下 _open で試してみたが、同様の結果...
	//int_infile = _open(m_edit_box_input_file_name, _O_RDWR | _O_CREAT, _S_IREAD | _S_IWRITE );
	//temp_long_double = _filelength(int_infile);



	if ((error_flag_in != 1) && (error_flag_out != 1))
	{
		fseek(in_file, 0, SEEK_END);
		fgetpos(in_file, &file_pos);
		temp_int64 = file_pos;
		//_gcvt((double(file_pos)), 16, temp_string);
		//MessageBox(temp_string, "file_pos");
		//MessageBox(_i64toa(file_pos, temp_string, 10), "file_pos at end");
		file_end_pos =
			double(file_pos/(double(packet_size)))*double(packet_size);
		// ↑ この辺りは、いろいろ試してみた名残です。 ↑
			
		//MessageBox(_i64toa(temp_int64, temp_string, 10), "file_end_pos2");
		file_pos = 0;
		m_FileSize = _i64toa(file_end_pos, temp_string, 10);
		last_packet_num = file_end_pos/analized_packet_size;
		m_NumOfPackets = _i64toa(last_packet_num, temp_string, 10);
		UpdateData(FALSE);
	}

	   /* ファイルを閉じる */
   if (error_flag_in != 1)
      fclose( in_file );

   return ;

}


No.648

Re:2G以上のファイルへのアクセス方法について
投稿者---YuO(2003/11/13 12:55:01)


・処理系として,Visual C++を仮定しています。
・回答中に,処理系依存の動作を前提とした記述が含まれます。


>まず、ファイルサイズの取得を行いたいのですが、以下のソースの fgetpos関数にて、file_pos に返る値が、扱うファイルが2Gバイト以下であれば問題ないのですが、それ以上になりますと「0」になってしまいます。

どのようにして調べましたか?

Visual C++ 5.0のfgetpos実装を調べてみましたが,
OSさえ対応していれば2Gバイトを越えるファイルであっても問題なく対応しているはずですが。


また,fgetposは成功していますか?


No.650

Re:2G以上のファイルへのアクセス方法について
投稿者---ケイ(2003/11/13 14:40:38)


失礼いたしました。 言葉足らずでした。 環境はVC++6.0を使用しており、実際にはWFCを使用したダイアローグベースのアプリを作っております。 また、データファイルは、NTFSフォーマットのHDDに入っているため、NT、2K、XPにて動作することを目的としています。
>
>>まず、ファイルサイズの取得を行いたいのですが、以下のソースの
>>fgetpos関数にて、file_pos に返る値が、扱うファイルが2Gバイト
>>以下であれば問題ないのですが、それ以上になりますと「0」に
>>なってしまいます。
>
>どのようにして調べましたか?

調べたのは、file_pos の中身をウォッチにて確認するという方法です。 例えば、テスト的に 20,400,000 バイトの ファイルを読み込み、fgetpos関数の処理結果をウォッチにて確認すると、20400000 と入ります。 しかし、当方が処理したい実際のファイル(実際に確認してみたら6Gでした。6,043,205,632 byte)を使って、同一の処理を通した後にウォッチにて確認すると 0 となってしまいます。

fgetpos関数の戻り値(0で成功)を調べようと思ったのですが、なぜか本日ビルドすると、fgetpos のラインで不明なエラーが出て調べることが出来ません。(ようは、関数が失敗しているということでしょうか?)

エラーメッセージは次のように出ています。

ダイアログ名:Microsoft Visual C++ Debug Library
Debug Assertion Failed!
Program:c:\TS_CUT32\Debug\TS_CUT32.exe
File:fseeki64.c
Line : 61

と出ます。 このfseeki64.c と言うファイルは見当たらないので、fgetpos関数が呼ばれた時に使用されるものだと思いますが、初めてのエラーなのでなんともわかりませんが、昨晩いろいろ いじってしまった結果 変になってしまったのかと思います。

> Visual C++ 5.0のfgetpos実装を調べてみましたが,
> OSさえ対応していれば2Gバイトを越えるファイルであっても
> 問題なく対応しているはずですが。

調べていただき ありがとうございます。 やはり、私の書き方が悪いかと思います。 何か気付くことがあれば、助言よろしくお願いします。

No.651

Re:2G以上のファイルへのアクセス方法について
投稿者---nop(2003/11/13 14:46:18)


>環境はVC++6.0を使用しており、実際にはWFCを使用したダイアローグベースのアプリを作っております。

それなら、Windows API でファイルアクセスした方がいいのでは?
GetFileSize()等を使えば 4G 以上のファイルにも対応しています。

No.655

Re:2G以上のファイルへのアクセス方法について
投稿者---YuO(2003/11/13 17:13:56)


>fgetpos関数の戻り値(0で成功)を調べようと思ったのですが、なぜか本日ビルドすると、fgetpos のラインで不明なエラーが出て調べることが出来ません。(ようは、関数が失敗しているということでしょうか?)
>エラーメッセージは次のように出ています。
>ダイアログ名:Microsoft Visual C++ Debug Library
>Debug Assertion Failed!
>Program:c:\TS_CUT32\Debug\TS_CUT32.exe
>File:fseeki64.c
>Line : 61
>と出ます。 このfseeki64.c と言うファイルは見当たらないので、fgetpos関数が呼ばれた時に使用されるものだと思いますが、初めてのエラーなのでなんともわかりませんが、昨晩いろいろ いじってしまった結果 変になってしまったのかと思います。

fseeki64.cというのは,CRTのソースをインストールしていると見つかります。

VC++ 5.0において,fseeki64.cの61行目は,
>_ASSERTE(stream != NULL);
でした。

つまりは,第一引数にNULLポインタが渡されたのが原因,ということになります。

って,Assertion DialogにExpression: stream != NULLとかの表示は出ていませんか?


No.656

Re:2G以上のファイルへのアクセス方法について
投稿者---ケイ(2003/11/13 18:47:55)


レスありがとうございます。 ご察しの通り、エラーメッセージに Expression: stream != NULL と出ています。 ファイルが正しく開かれていないということでしょうか? 2Gバイト以下のサイズを選びこの関数を通すとエラーが出ないことから、fopenは正しくされているように思います。 なぜこんなことになったのか不明です。 何か対処方法はありますでしょうか?

> それなら、Windows API でファイルアクセスした方がいいのでは?
> GetFileSize()等を使えば 4G 以上のファイルにも対応しています。
確かにそうなのですが、WinAPIの知識が乏しく、handleの概念がいまだ良くわかっておらず、使いこなせていません。(避けていました...未熟) 正しく使えば、fopen で2G以上のファイルも扱いたいので、とりあえず このまま頑張ってみようかと思います。(近々、頑張って勉強してみます。)

助言等ありましたらよろしくお願いします。

No.657

Re:2G以上のファイルへのアクセス方法について
投稿者---ひっこしさかい(2003/11/13 22:30:34)


>助言等ありましたらよろしくお願いします。

サイズの取得なら
64ビット対応statの _stati64() を使う手もある。


No.658

Re:2G以上のファイルへのアクセス方法について
投稿者---ケイ(2003/11/13 23:23:36)


まず、お詫び申し上げます。 「Debug Assertion Failed!」エラーの件ですが、開こうとしていたファイルが破壊されていたことが原因でした。 何度もfileopen しながらDebugを中止したりしていたことが原因かも知れません。 別途、コピーしてきたテスト用ファイルを使用しましたところ、エラーは出なくなりました。 お騒がせ致しました。

YuOさんのコメントにあった「fgetposは成功しているか」という点、確認しました。

fgetpos は失敗したときに、0以外を返すとのことですので、戻り値を int変数に返して確認しました。 結果、fgetpos は成功しているようです。 しかし、同様にfgetpos は ファイルポインタに「0」を入れます。 何か、根本的に間違いがあるのでしょうか? プロジェクトの設定などの要因などあるのでしょうか????

また、include しているhファイルは以下です。

#include <stdio.h>
#include <process.h>
#include <iostream.h>
#include <windows.h>

足りないもの、間違ったもの等ありますでしょうか??

_stati64() も一度試してみます。

No.660

Re:2G以上のファイルへのアクセス方法について
投稿者---ケイ(2003/11/14 15:56:36)


皆様、いろいろありがとうございます。 まだ悩んでいますので助言お願いします。

_stati64 で 簡単にファイルサイズの取得を試して見ました。 実際のファイルサイズは 6,043,205,632 バイトあるのですが、以下のように 1,748,238,336 バイトと表示されてしまいます。 毎回 1,748,238,336 バイトとなるのですが、このサイズになにか意味があるのでしょうか??

以下出力結果

<<出力結果>>

M:\>test_console.exe
ファイルの大きさ : 1748238336
ドライブ : M:
更新時刻 : Fri Nov 14 15:15:22 2003

<<ソース>>

/* STAT.C: _stat 関数を使って、ファイル STAT.C についての
 * 情報をレポートします。
 */

#include <time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>

void main( void )
{
   struct _stati64 buf;
   int result;
   char buffer[] = "A line to output";

   /* "stat.c" に関連付けられているデータを取得します。 */
   result = _stati64( "5g.trp", &buf );

   /* ファイル状態情報が取得できたかのチェック */
   if( result != 0 )
      perror( "ファイルの情報を取得できませんでした" );
   else
   {
      /* 統計情報の一部を表示 */
      printf( "ファイルの大きさ : %ld\n", buf.st_size );
      printf( "ドライブ         : %c:\n", buf.st_dev + 'A' );
      printf( "更新時刻         : %s", ctime( &buf.st_atime ) );
   }
}


ちなみに、前回のソースで 上手く動いていないのは fgetpos 関数ではなく fseekでした。 ( fseek(in_file, 0, SEEK_END); のライン ) 手動で、正しいファイルサイズを fsetpos に渡し、その後で fgetpos をすると O.Kでした。 fseek が64ビット対応していないという理由でしょうか?? _stati64 も上手くいきませんので、何か気付くことがあればよろしくお願いします。

No.661

Re:2G以上のファイルへのアクセス方法について
投稿者---☆ら☆(2003/11/14 18:46:28)


> _stati64 で 簡単にファイルサイズの取得を試して見ました。
> 実際のファイルサイズは 6,043,205,632 バイトあるのですが、
> 以下のように 1,748,238,336 バイトと表示されてしまいます。
> 毎回 1,748,238,336 バイトとなるのですが、このサイズになにか
> 意味があるのでしょうか??

6,043,205,632 と 1,748,238,336 をそれぞれ16進数にしたら
何か気づきませんか?


No.662

Re:2G以上のファイルへのアクセス方法について
投稿者---YuO(2003/11/14 18:49:19)


>_stati64 で 簡単にファイルサイズの取得を試して見ました。 実際のファイルサイズは 6,043,205,632 バイトあるのですが、以下のように 1,748,238,336 バイトと表示されてしまいます。 毎回 1,748,238,336 バイトとなるのですが、このサイズになにか意味があるのでしょうか??
      printf( "ファイルの大きさ : %ld\n", buf.st_size );


そりゃ,buf.st_sizeは__int64型ですから,%ldで受けるのが間違いです。
%i64dを使う必要があります。


>ちなみに、前回のソースで 上手く動いていないのは fgetpos 関数ではなく fseekでした。 ( fseek(in_file, 0, SEEK_END); のライン ) 手動で、正しいファイルサイズを fsetpos に渡し、その後で fgetpos をすると O.Kでした。 fseek が64ビット対応していないという理由でしょうか?? _stati64 も上手くいきませんので、何か気付くことがあればよろしくお願いします。

fseekはいろいろ問題が起きそうです。
_fseeki64を使うか,fsetposを使うかです。
#fsetposは_fseeki64を呼び出しています。


No.663

Re:2G以上のファイルへのアクセス方法について
投稿者---ケイ(2003/11/14 23:18:17)


みなさん、ありがとうございます。

_fseeki64 を使って、書き直した結果、上手く6Gのファイルも扱うことが出来ました。 なぜ、fseekでうまくいかなかったのか、いまだ謎ですが 作成したプログラムも上手く動作しました。

ありがとうございます!!