○ファイル処理

一つのファイルを読み書きモードで開いて、書く→読む する場合には
ファイルになにかしら書き込んだ後に、バッファをフラッシュ(fflush(fp))または、一度ファイルを閉じないと
環境によっては、その後の読み込み動作で正常な値が返って来ない事があります。
なぜかと言うと書き込み動作だけだと、キャッシュに書き込まれていて実際のディスクに反映されていないからです。

ファイルに書き込んだのにまともに読み出せないという
なかなか難しいバグを作りこむ事になってしまいますからね(笑


■ファイルの開き方

FILE* fopen(path,mode);

path ファイルのパス

mode	"r" : 読み出し
	"w" : 書き込み(既存のファイルがあれば0にする)
	"a" : 追記モード(既存のファイルが無ければ新規作成)
	"r+" : "w+" : "a+" : 読み書きモード
	"b" : バイナリモード("rb"、"wb"、"ab"のように使用)


■ファイルを(改行文字)により一行ずつ最後まで読み込む

//fgets(buff,size,stream)
//
//buff : 文字列バッファポインタ
//size : 読み出す最大の長さ
//stream : ストリームのポインタ

#include <stdio.h>

int main(int argc, char* argv[])
{
	FILE * fp;
	if((fp=fopen("c:/test.txt","r"))==NULL){
		//エラー処理
	}else{
		char buff[256]="";
		while(fgets(buff,256,fp)!=NULL){
			printf("%s",buff);
		}
		fclose(fp);
	}
	return 0;
}

ファイルから読み込んだ一行の文字列には改行文字が含まれているため注意する必要があります
ファイルの最終行が改行で終わるならば
最後の一文字を削除するのも有効かもしれません

	buff[strlen(buff)-1]='\0';//終端の1文字を削除する


■ファイルに文字を書き込む

//fprintf : printfと同じように書き込みます

#include <stdio.h>

int main(int argc, char* argv[])
{
	FILE * fp;
	if((fp=fopen("c:/test.txt","w"))==NULL){
		//エラー処理
	}else{
	fprintf(fp,"%s\n","testテスト");
    fclose(fp);
}
	return 0;
}

■ファイルを1バイト(文字)ずつ最後まで読み込む

//int c=fgetc(fp); : charがintにキャストされて出てきます

#include <stdio.h>

int main(int argc, char* argv[])
{
	FILE * fp;
	if((fp=fopen("c:/test.txt","r"))==NULL){
		//エラー処理
	}else{
		while(!feof(fp)){
			int c=fgetc(fp);
			char buff[2]="";
			buff[0]=c;
			printf("%s\n",buff);
		}
    fclose(fp);
}
	return 0;
}

■ファイルに1バイトずつ書き込む

fputc(int,fp);

を使用します

■バイナリデータをファイルに書き込む

//WriteLen = fwrite(buff,sizeof(char),Len,fp)
//
//WriteLen : 書き込んだデータ数
//buff : 値の入った配列
//sizeof(char) : 書き込むデータ―のサイズ(バイト)
//fp : 書き込むストリーム

#include <stdio.h>
#include <string.h>

int main(int argc, char* argv[])
{
	FILE * fp;
	if((fp=fopen("c:/test.txt","wb"))==NULL){//"ab"にすると追記できます。
		//エラー処理
	}else{

		char buff[10]="";
		buff[0]=10;//書き込むダミーのデータを作成
		buff[1]=20;
		buff[2]=30;
		buff[3]=40;

		//strlenはNULL文字で終端と判断するためNULL文字が含まれるデータの場合は
		//他の方法でサイズを求める必要がある
		unsigned int WriteLen = strlen(buff);//書き込むサイズを求める

		if(WriteLen!=fwrite(buff,sizeof(char),WriteLen,fp)){
			printf("書き込みエラー%dバイト書き込まれました",WriteLen);
		}

		fclose(fp);
	}
	return 0;
}

■バイナリデータをファイルから読み出す

#include <stdio.h>

int main(int argc, char* argv[])
{
	FILE * fp;
	if((fp=fopen("c:/test.txt","rb"))==NULL){
		//エラー処理
	}else{

		char buff[10]="";

		unsigned int ReadLen = 4;//4バイト読み込みます。
		if(ReadLen!=fread(buff,sizeof(char),ReadLen,fp)){
			printf("読み込みエラー\n");
		}

		printf("%d  %d  %d  %d\n",buff[0],buff[1],buff[2],buff[3]);//読み込んだ値の表示

		fclose(fp);
	}
	return 0;
}

■バイナリデータを1バイトづつEOFまで読み込む

#include <stdio.h>

int main(int argc, char* argv[])
{
	FILE * fp;
	if((fp=fopen("c:/test.txt","rb"))==NULL){
		//エラー処理
	}else{
		char buff[256]="";
		int i=0;

		while(!feof(fp)){
			fread(&buff[i++],sizeof(char),1,fp);
		}
		fclose(fp);
	}
	return 0;
}

■ランダムアクセス



#include <stdio.h>

int main(){

	FILE * fp;
	if((fp=fopen("test.txt","rb+"))==NULL){//バイナリ読み書きモード
						//"wb+"ファイルの新規作成
						//"rb+"ファイルが無ければエラー
		//エラー処理
	}else{
		fseek(fp,(long)0,SEEK_END);//ファイルの終端を基準にして移動バイト数0移動(つまりEOFの位置)
					//前に進むには移動バイト数にマイナスの数値を入れる
		fputc('a',fp);//現在の位置に書き込み(EOFの位置のため追記と同じ意味になる)

		fseek(fp,(long)0,SEEK_SET);//ファイルの先頭を基準にして移動バイト数0移動
		fputc('s',fp);//現在の位置に書き込み(先頭の位置のため先頭に書き込み)
		fflush(fp);//書き込んだ後にはバッファをフラッシュしないと環境によりまともに動かない事がある

		fseek(fp,(long)-1,SEEK_CUR);//ファイルの現在位置を基準にして移動バイト数-1移動

		long p=ftell(fp);//現在の位置を取得

		int val=fgetc(fp);//値を取得してファイルポインタを一つ進める

		printf("位置:%d 値:%c\n",p,val);

		fclose(fp);
	}
	return 0;
}


2Gバイトぐらいまでのファイルなら上のコードで動きますが、
それ以上のファイルサイズになるとまともに動きません。
なぜならftellの戻り値が符号付32ビットだからです。
そこで、64ビットで位置を取得できるfgetposを使います。

VC6 32bit環境では次のようになります。
__int64 _eof;
fgetpos( fp, &_eof );
printf( "%I64d\n", _eof );

しかし、fseekも2Gバイトぐらいのファイルまでしか対応してないみたいです。
内部で32ビットで動いてるようで、相対で飛んでも2G以降は動きませんでした。

私の古いVC6では動きませんがVisual Studio 2005以降ぐらいの
新しいバージョンなら64ビット対応の fseek、_fseeki64 があるそうですよ。
ちなみにftellも、64ビット対応の_ftelli64があるそうです。


▼2Gバイト以上のランダムアクセス

VisualStudio Express 2013で32ビットアプリケーションでコンパイルしました。
無料で開発環境が手に入るとは良い時代になったもんだ。

ファイルを開いて前から順番に読み取り、前から順番に書き込むプログラム
一部の関数が64ビット対応に変更されています。

#include <stdio.h>

#pragma warning(disable : 4996)

#define BUFF_SIZE 0x800
unsigned char buff[BUFF_SIZE];

int main(int argc, char* argv[]){
	FILE * fp;
	fp = fopen("test.txt", "rb+");

	_fseeki64(fp, (long)0, SEEK_END);//ファイルの終端に移動して
	__int64 _eof;

	//fgetpos(fp, &_eof);//現在の位置を取得
	_eof = _ftelli64(fp);//現在の位置を取得

	_fseeki64(fp, (long long)0, SEEK_SET);//ファイルの先頭に移動

	__int64 pos = 0;
	unsigned int i = 0;

	while (_eof>pos){
		int fs;
		if (_eof<(pos + BUFF_SIZE)){
			fs = (int)(_eof - pos);
		}else{
			fs = BUFF_SIZE;
		}
		fs = fread(buff, sizeof(char), fs, fp);

		_fseeki64(fp, (long long)-fs, SEEK_CUR);//現在位置から前に戻る
		fwrite(buff, sizeof(char), fs, fp);
		fflush(fp);//書き込んだ後にはバッファをフラッシュしないとまともに動かない事がある
		//fgetpos(fp, &pos);//現在の位置を取得
		pos = _ftelli64(fp);//現在の位置を取得
	}
	return 0;
}


■APIを使ったファイル操作

▼ファイル読み込み


HANDLE hFile;
hFile = CreateFile(filename, GENERIC_READ,FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL );

if( hFile != INVALID_HANDLE_VALUE ){
	DWORD len;
	ReadFile( hFile, buff, BUFF_SIZE, &len, NULL );
	buff[len] = _T('\0');
}
CloseHandle( hFile );


▼ファイル書き込み


HANDLE hFile;
hFile = CreateFile(filename, GENERIC_WRITE,FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL );
if( hFile != INVALID_HANDLE_VALUE ){
	code_edit->GetWindowText(buff,BUFF_SIZE);
	DWORD len;
	WriteFile( hFile, buff,(DWORD)strlen(buff), &writesize, len );
}
CloseHandle( hFile );




▲トップページ > Windows と C++