ゲームプログラミングTips(6):ファイル入出力

 

2012年5月14日……前回の講座より、実に7日ぶりである。
実は最近忙しく、次の講座を書けるのははたしていつになるのやら……ううむ(´Д`)

 

1.ファイル入出力とは?

 ……説明するまでもない、ファイルからの読み込みとファイルへの書き込みのことである。

 

2.ファイル入出力のゲームプログラミングにおける活用事例

 むしろ、非活用事例の方を教えてもらいたい(;´Д`)
 基本、セーブおよびロードの概念が存在するゲームでは使われている。
 いや、セーブおよびロードの概念がなくともコンフィグの自動セーブぐらいやってるだろう。
 「セーブもロードもしねぇ!コンフィグも毎回起動時に設定し直せ!」という気概がなきゃ普通は使うだろう。

 本項で語る内容はここからが本題である。何故、ファイル入出力なんぞに1回の説明を要するのか?
 C言語を使う人は「fprintfとかfwriteとか使えばいいんじゃね?語る内容なくね?」と思う人もいるだろう。
 語る内容はあるのである、詳しくは下で!

 

3.具体例(ヘッダ解説)

 本項では、俺が作ったファイル入出力クラスを掲載する。

/*************************************************
ファイル名:FileReader.h
作成者  :あびす
役割   :ファイル出力
*************************************************/
/**
*	@file	FileReader.h
*	@brief	ファイル入力を行うクラス関係です。
*/
#ifndef DX9A_FILE_FILEREADER_H
#define DX9A_FILE_FILEREADER_H

namespace nsDX9A{
namespace nsFile{
namespace nsFileReader{

//ファイル入力クラス(インターフェース)
/**
*	@brief	ファイル入力を行うクラスのインターフェースです。
*/
class IFileReader{
public:
/**
*	@brief	仮想デストラクタです。
*/
	virtual ~IFileReader(){}													//仮想デストラクタ

/**
*	@brief	fopen関数の互換メソッドです。
*	@param	FileName	[in]オープンするファイル名を指定します。
*	@param	Mode		[in]ファイルモードを指定します。
*	@return	ファイルのオープンが成功した場合はtrue、失敗した場合はfalseを返します。
*/
	virtual bool fopen(const char* FileName, const char* Mode) = 0;				//fopen
/**
*	@brief	fclose関数の互換メソッドです。
*	@return	ファイルのクローズが成功した場合はtrue、失敗した場合はfalseを返します。
*/
	virtual bool fclose() = 0;													//fclose
/**
*	@brief	ファイルがオープンされているかを取得します。
*	@return	ファイルがオープンされている場合はtrue、されていない場合はfalseを返します。
*/
	virtual bool fisopen() = 0;													//fisopen
/**
*	@brief	ファイルサイズを取得します。
*	@return	ファイルサイズを返します(単位:バイト)
*/
	virtual long fsize() = 0;													//fsize

/**
*	@brief	fgetc関数の互換メソッドです。
*	@param	c	[out]読み込んだ文字を格納します。
*	@return	文字の読み込みが成功した場合はtrue、失敗した場合はfalseを返します。
*/
	virtual bool fgetc(char* c) = 0;											//fgetc
/**
*	@brief	fgets関数の互換メソッドです。
*	@param	s	[out]読み込んだ文字列を格納します。
*	@param	n	[in]読み込む最大文字数を指定します。
*	@return	文字列の読み込みが成功した場合はtrue、失敗した場合はfalseを返します。
*/
	virtual bool fgets(char* s, int n) = 0;										//fgets
/**
*	@brief	fread関数の互換メソッドです。
*	@param	buffer	[out]読み込んだデータを格納します。
*	@param	size	[in]項目のサイズを指定します。
*	@param	count	[in]読み込む最大項目数を指定します。
*	@return	実際に読み込まれた完全な項目の数を返します。
*/
	virtual size_t fread(void* buffer, size_t size, size_t count) = 0;			//fread

/**
*	@brief	ftell関数の互換メソッドです。
*	@return	現在のファイル位置を返します(単位:バイト)
*/
	virtual long ftell() = 0;													//ftell
/**
*	@brief	fseek関数の互換メソッドです。
*	@param	offset	[in]originからのバイト数を指定します。
*	@param	origin	[in]初期位置を指定します。
* SEEK_CUR:ファイルポインタの現在位置です。
* SEEK_END:ファイルの終端です。
* SEEK_SET:ファイルの先頭です。 * @return ファイルポインタの移動が成功した場合はtrue、失敗した場合はfalseを返します。 */ virtual bool fseek(long offset, int origin) = 0; //fseek /** * @brief fgetpos関数の互換メソッドです。 * @param pos [out]現在のファイル位置インジケータを格納します(単位:バイト) * @return ファイル位置インジケータの取得が成功した場合はtrue、失敗した場合はfalseを返します。 */ virtual bool fgetpos(fpos_t* pos) = 0; //fgetpos /** * @brief fsetpos関数の互換メソッドです。 * @param pos [in]新たな位置インジケータを指定します(単位:バイト) * @return ファイル位置インジケータの設定が成功した場合はtrue、失敗した場合はfalseを返します。 */ virtual bool fsetpos(const fpos_t* pos) = 0; //fsetpos /** * @brief feof関数の互換メソッドです。 * @return ファイルの終端を越えて読み取り操作を実行した場合はtrueを返し、それ以外の場合はfalseを返します。 */ virtual bool feof() = 0; //feof /** * @brief ferror関数の互換メソッドです。 * @return ファイルストリームにエラーがない場合は0を返し、それ以外の場合は0以外を返します。 */ virtual int ferror() = 0; //ferror }; //ファイル入力クラス(基底クラス) /** * @brief ファイル入力を行うクラスの基底クラスです。 */ class CFileReader : public IFileReader{ public: /** * @brief デフォルトコンストラクタです。 */ CFileReader(); //デフォルトコンストラクタ /** * @brief コンストラクタです。
* オープンするファイル名とファイルモードを指定することで、生成時にファイルのオープンをも行います。 * @param FileName [in]オープンするファイル名を指定します。 * @param Mode [in]ファイルモードを指定します。 */ CFileReader(const char* FileName, const char* Mode); //コンストラクタ /** * @brief デストラクタです。 */ ~CFileReader(); //デストラクタ bool fopen(const char* FileName, const char* Mode); //fopen bool fclose(); //fclose bool fisopen(); //fisopen long fsize(); //fsize bool fgetc(char* c); //fgetc bool fgets(char* s, int n); //fgets size_t fread(void* buffer, size_t size, size_t count); //fread long ftell(); //ftell bool fseek(long offset, int origin); //fseek bool fgetpos(fpos_t* pos); //fgetpos bool fsetpos(const fpos_t* pos); //fsetpos bool feof(); //feof int ferror(); //ferror protected: /** * @brief ファイルストリームです。
* 本メンバに対し、各メソッドで操作を行います。 */ FILE* stream; //ファイルストリーム private: CFileReader(const CFileReader&); //コピーコンストラクタ(禁止) CFileReader& operator =(const CFileReader&); //代入演算子(禁止) }; //補助関数 //暗号化文字列を読み込む /** * @brief 暗号化文字列を読み込みます。
* 暗号化アルゴリズムはXOR暗号です。 * @param FileReader [in]読み込む際に使用する\link IFileReader ファイル入力クラス\endlinkを指定します。
* FileReaderは既にオープンされていなければなりません(読み込み元はFileReaderで決まります) * @param CryptKey [in]暗号化キーを指定します。 * @param Data [out]読み込んだ文字列を格納します。 */ void LoadCryptedString(IFileReader* FileReader, BYTE CryptKey, string* Data); } } } #endif
/*************************************************
ファイル名:FileWriter.h
作成者  :あびす
役割   :ファイル出力
*************************************************/
/**
*	@file	FileWriter.h
*	@brief	ファイル出力を行うクラス関係です。
*/
#ifndef DX9A_FILE_FILEWRITER_H
#define DX9A_FILE_FILEWRITER_H

namespace nsDX9A{
namespace nsFile{
namespace nsFileWriter{

//ファイル出力クラス(インターフェース)
/**
*	@brief	ファイル出力を行うクラスのインターフェースです。
*/
class IFileWriter{
public:
/**
*	@brief	仮想デストラクタです。
*/
	virtual ~IFileWriter(){}													//仮想デストラクタ

/**
*	@brief	fopen関数の互換メソッドです。
*	@param	FileName	[in]オープンするファイル名を指定します。
*	@param	Mode		[in]ファイルモードを指定します。
*	@return	ファイルのオープンが成功した場合はtrue、失敗した場合はfalseを返します。
*/
	virtual bool fopen(const char* FileName, const char* Mode) = 0;				//fopen
/**
*	@brief	fclose関数の互換メソッドです。
*	@return	ファイルのクローズが成功した場合はtrue、失敗した場合はfalseを返します。
*/
	virtual bool fclose() = 0;													//fclose
/**
*	@brief	ファイルがオープンされているかを取得します。
*	@return	ファイルがオープンされている場合はtrue、されていない場合はfalseを返します。
*/
	virtual bool fisopen() = 0;													//fisopen
/**
*	@brief	ファイルサイズを取得します。
*	@return	ファイルサイズを返します(単位:バイト)
*/
	virtual long fsize() = 0;													//fsize

/**
*	@brief	fputc関数の互換メソッドです。
*	@param	c	[in]書き込む文字を指定します。
*	@return	文字の書き込みが成功した場合はtrue、失敗した場合はfalseを返します。
*/
	virtual bool fputc(char c) = 0;												//fputc
/**
*	@brief	fputs関数の互換メソッドです。
*	@param	str	[in]書き込む文字列を指定します。
*	@return	文字列の書き込みが成功した場合はtrue、失敗した場合はfalseを返します。
*/
	virtual bool fputs(const char* str) = 0;									//fputs
/**
*	@brief	fwrite関数の互換メソッドです。
*	@param	buffer	[in]書き込むデータへのポインタを指定します。
*	@param	size	[in]項目のサイズを指定します。
*	@param	count	[in]書き込む最大項目数を指定します。
*	@return	実際に書き込まれた完全な項目の数を返します。
*/
	virtual size_t fwrite(const void* buffer, size_t size, size_t count) = 0;	//fwrite
/**
*	@brief	fprintf関数の互換メソッドです。
*	@param	format	[in]出力文字列を指定します。
* fprintfと同等の書式指定が可能です。 * @param ... [in]出力する変数を指定します。
* fprintfと同等の書式指定が可能です。 * @return 出力したバイト数を返します。 */ virtual int __cdecl fprintf(const char* format, ...) = 0; //fprintf /** * @brief ftell関数の互換メソッドです。 * @return 現在のファイル位置を返します(単位:バイト) */ virtual long ftell() = 0; //ftell /** * @brief fseek関数の互換メソッドです。 * @param offset [in]originからのバイト数を指定します。 * @param origin [in]初期位置を指定します。
* SEEK_CUR:ファイルポインタの現在位置です。
* SEEK_END:ファイルの終端です。
* SEEK_SET:ファイルの先頭です。 * @return ファイルポインタの移動が成功した場合はtrue、失敗した場合はfalseを返します。 */ virtual bool fseek(long offset, int origin) = 0; //fseek /** * @brief fgetpos関数の互換メソッドです。 * @param pos [out]現在のファイル位置インジケータを格納します(単位:バイト) * @return ファイル位置インジケータの取得が成功した場合はtrue、失敗した場合はfalseを返します。 */ virtual bool fgetpos(fpos_t* pos) = 0; //fgetpos /** * @brief fsetpos関数の互換メソッドです。 * @param pos [in]新たな位置インジケータを指定します(単位:バイト) * @return ファイル位置インジケータの設定が成功した場合はtrue、失敗した場合はfalseを返します。 */ virtual bool fsetpos(const fpos_t* pos) = 0; //fsetpos /** * @brief feof関数の互換メソッドです。 * @return ファイルの終端を越えて読み取り操作を実行した場合はtrueを返し、それ以外の場合はfalseを返します。 */ virtual bool feof() = 0; //feof /** * @brief ferror関数の互換メソッドです。 * @return ファイルストリームにエラーがない場合は0を返し、それ以外の場合は0以外を返します。 */ virtual int ferror() = 0; //ferror }; //ファイル出力クラス(基底クラス) /** * @brief ファイル出力を行うクラスの基底クラスです。 */ class CFileWriter : public IFileWriter{ public: /** * @brief デフォルトコンストラクタです。 */ CFileWriter(); //デフォルトコンストラクタ /** * @brief コンストラクタです。
* オープンするファイル名とファイルモードを指定することで、生成時にファイルのオープンをも行います。 * @param FileName [in]オープンするファイル名を指定します。 * @param Mode [in]ファイルモードを指定します。 */ CFileWriter(const char* FileName, const char* Mode); //コンストラクタ /** * @brief デストラクタです。 */ ~CFileWriter(); //デストラクタ bool fopen(const char* FileName, const char* Mode); //fopen bool fclose(); //fclose bool fisopen(); //fisopen long fsize(); //fsize bool fputc(char c); //fputc bool fputs(const char* str); //fputs size_t fwrite(const void* buffer, size_t size, size_t count); //fwrite int __cdecl fprintf(const char* format, ...); //fprintf long ftell(); //ftell bool fseek(long offset, int origin); //fseek bool fgetpos(fpos_t* pos); //fgetpos bool fsetpos(const fpos_t* pos); //fsetpos bool feof(); //feof int ferror(); //ferror protected: /** * @brief ファイルストリームです。
* 本メンバに対し、各メソッドで操作を行います。 */ FILE* stream; //ファイルストリーム private: CFileWriter(const CFileWriter&); //コピーコンストラクタ(禁止) CFileWriter& operator =(const CFileWriter&); //代入演算子(禁止) }; //補助関数 //暗号化文字列を書き込む /** * @brief 暗号化文字列を書き込みます。
* 暗号化アルゴリズムはXOR暗号です。 * @param FileWriter [in]書き込む際に使用する\link IFileWriter ファイル出力クラス\endlinkを指定します。
* FileWriterは既にオープンされていなければなりません(書き込み先はFileWriterで決まります) * @param CryptKey [in]暗号化キーを指定します。 * @param Data [in]暗号化対象の文字列を指定します。 */ void SaveCryptedString(IFileWriter* FileWriter, BYTE CryptKey, const string& Data); } } } #endif

 今回は2ファイルに対する解説を行っていく。
 ヘッダに対する解説では、本クラスの使用方法ならびに存在意義を説明する。

 使用方法は簡単である。

 1.インスタンスを生成する
 2.コンストラクタまたはfopenメソッドでファイルをオープンする
 3.各種メソッドまたは関数を使用しファイルへの入力またはファイルへの出力を行う
 4.デストラクタまたはfcloseメソッドでファイルをクローズする

具体例

CFileReader reader("a.txt", "rb");		//ファイル入力クラス
CFileWriter writer("b.txt", "wb");		//ファイル出力クラス

printf("%d", reader.fsize());			//ファイルサイズを表示
writer.fprintf("てすと");				//ファイルに文字列を書き込み

string temp;
LoadCryptedString(&reader, 255, &temp);	//ファイルから暗号化した文字列を読み込み
SaveCryptedString(&writer, 255, temp);	//ファイルに暗号化した文字列を書き込み

 ……これだけである、C言語を使う人は各種ファイル入出力関数に読み替えれば理解できる筈。
 (まあ、fsizeとか〜CryptedString関数とか標準関数に存在しないものもあるが)

 では、以下にて具体的な説明を行っていく。
 ただし、なるべく説明は簡略に行う。

 ヘッダの説明

  何故書き込みと読み込みでクラスを分けているの?

   同じファイルに対して、書き込みと読み込みを同時に行うことはないからである。
   こうしておいた方が、読み込み対象のファイルにうっかりfwriteとか馬鹿な真似を防止できる。
   ……ファイルモードがその役目だって? はいはいそうでしたすんません、俺が悪うござんした('A`)

  何故fscanfメソッドがないの?

   ぶっちゃけると俺の技術力不足です、すんません(;´Д`)

  LoadCryptedString関数およびSaveCryptedString関数は何?

   暗号化文字列の読み込み・書き込みを行う関数である。
   何故その必要があるのか、以下に理由を記す。

    暗号化の必要性

     これはゲームにもよる。
     たとえばネットゲームの場合、暗号化を行わないと「チートおk^^」になってしまう。
     他プレイヤーに影響を及ぼすことのないゲームの場合は、暗号化を行う必要は一切ない。

     ただし、文字列に関しては一応暗号化を行っておいた方がいい。
     何故なら、テキストエディタでセーブデータを開いた時に文字列はそのまま見えてしまうからである。
     その点、int型等の数値はそのままでは見えない。よって、文字列にかぎり関数を作成し提供している。

  そもそもこんなクラス必要なの?

   この疑問に対する答えが本項最大の重要ポイントである、まず答えをぶっちゃけてしまうと必要である。
   何故なら、各種リソースを取り扱う際には管理クラスとファイル入出力クラスを分けるべきだからである。
   以下に、具体例を挙げて説明する。

    クラス分割の必要性

     グラフィックを取り扱うクラスG、サウンドを取り扱うクラスSが存在したとする。
     また、ファイル入出力の方法を通常とパッキングを行ったファイルに対するものの2通りが存在したとする。
     この場合、以下のクラスが必要となるだろう。

     グラフィック+読み込み通常:GN
     グラフィック+読み込みパッキング:GP
     サウンド+読み込み通常:SN
     サウンド+読み込みパッキング:SP

     ここで動画を取り扱うクラスMを新たに作るとすると、

     動画+読み込み通常:MN
     動画+読み込みパッキング:MP

     上記のようになる、つまりひとつの取り扱うクラスを必要とする度に2クラスを作成しなければならない。
     2クラスならまだいいのだが、新たなファイル入出力の方法を必要とした場合は更に増えていく。

     その点、あらかじめファイル入出力と管理を分割しておくと、

     グラフィック+読み込み通常:G+FN
     グラフィック+読み込みパッキング:G+FP
     サウンド+読み込み通常:S+FN
     サウンド+読み込みパッキング:S+FP
     動画+読み込み通常:M+FN
     動画+読み込みパッキング:M+FP

     上記のような形となる(※通常のファイル入出力がFN、パッキングを行ったファイルに対する入出力がFP)
     一見何も変わっていないようだが、よく見ると作成するクラス数が変わっている(非分割は6、分割は5)
     上記の例だとたいした差ではないが、取り扱うクラスやファイル入出力の方法の数次第では大きな差となる。

     よって、管理クラスとファイル入出力クラスは分けておくべきである。

 

4.具体例(ソース解説)

 次はソース解説である。

#include "stdafx.h"
#include "../index.h"
#include "FileReader.h"

namespace nsDX9A{
namespace nsFile{
namespace nsFileReader{

//デフォルトコンストラクタ
CFileReader::CFileReader(){
	stream = NULL;
}
//コンストラクタ
CFileReader::CFileReader(const char* FileName, const char* Mode){
	fopen(FileName, Mode);
}
//デストラクタ
CFileReader::~CFileReader(){
	fclose();
}

//fopen
bool CFileReader::fopen(const char* FileName, const char* Mode){
	if(stream){
		DX9AErrOut("Failed : CFileReader::fopen Open Data Already Exists\n");
		return(false);
	}
	if(Mode){
		size_t szMode = strlen(Mode);
		if(szMode>=1 && Mode[0]!='r'){
			DX9AErrOut("Failed : CFileReader::fopen Invalid File Mode\n");
			return(false);
		}
		if(szMode>=2 && Mode[1]=='+'){
			DX9AErrOut("Failed : CFileReader::fopen Invalid File Mode\n");
			return(false);
		}
	}
	stream = ::fopen(FileName, Mode);
	return(stream!=NULL);
}
//fclose
bool CFileReader::fclose(){
	if(stream){
		bool Result = (::fclose(stream) == 0);
		if(Result){
			stream = NULL;
		}
		return(Result);
	}
	return(true);
}
//fisopen
bool CFileReader::fisopen(){
	return(stream!=NULL);
}
//fsize
long CFileReader::fsize(){
	long TempFilePos = ftell();
	fseek(0, SEEK_END);
	long Result = ftell();
	fseek(TempFilePos, SEEK_SET);
	return(Result);
}

//fgetc
bool CFileReader::fgetc(char* c){
	int Temp;
	Temp = ::fgetc(stream);
	*c = static_cast< char >(Temp);
	return(Temp!=EOF);
}
//fgets
bool CFileReader::fgets(char* str, int n){
	return(::fgets(str, n, stream)!=NULL);
}
//fread
size_t CFileReader::fread(void* buffer, size_t size, size_t count){
	return(::fread(buffer, size, count, stream));
}

//ftell
long CFileReader::ftell(){
	return(::ftell(stream));
}
//fseek
bool CFileReader::fseek(long offset, int origin){
	return(::fseek(stream, offset, origin)==0);
}
//fgetpos
bool CFileReader::fgetpos(fpos_t* pos){
	return(::fgetpos(stream, pos)==0);
}
//fsetpos
bool CFileReader::fsetpos(const fpos_t* pos){
	return(::fsetpos(stream, pos)==0);
}

//feof
bool CFileReader::feof(){
	return(::feof(stream)!=0);
}
//ferror
int CFileReader::ferror(){
	return(::ferror(stream));
}



//補助関数
//暗号化文字列を読み込む
void LoadCryptedString(IFileReader* FileReader, BYTE CryptKey, string* Data){
	unsigned int i=0;
	char strData[64 * 1024] = {0};
	while(true){
		FileReader->fread(strData+i, sizeof(char), 1);
		if(strData[i]=='\0'){
			break;
		}
		else{
			if(strData[i]!=CryptKey){
				strData[i] ^= CryptKey;
			}
			i += 1;
		}
	}
	Data->assign(strData);
}

}
}
}
#include "stdafx.h"
#include "../index.h"
#include "FileWriter.h"

namespace nsDX9A{
namespace nsFile{
namespace nsFileWriter{

//デフォルトコンストラクタ
CFileWriter::CFileWriter(){
	stream = NULL;
}
//コンストラクタ
CFileWriter::CFileWriter(const char* FileName, const char* Mode){
	fopen(FileName, Mode);
}
//デストラクタ
CFileWriter::~CFileWriter(){
	fclose();
}

//fopen
bool CFileWriter::fopen(const char* FileName, const char* Mode){
	if(stream){
		DX9AErrOut("Failed : CFileWriter::fopen Open Data Already Exists\n");
		return(false);
	}
	if(Mode){
		size_t szMode = strlen(Mode);
		if(szMode>=1 && Mode[0]=='r'){
			DX9AErrOut("Failed : CFileWriter::fopen Invalid File Mode\n");
			return(false);
		}
	}
	stream = ::fopen(FileName, Mode);
	return(stream!=NULL);
}
//fclose
bool CFileWriter::fclose(){
	if(stream){
		bool Result = (::fclose(stream) == 0);
		if(Result){
			stream = NULL;
		}
		return(Result);
	}
	return(true);
}
//fisopen
bool CFileWriter::fisopen(){
	return(stream!=NULL);
}
//fsize
long CFileWriter::fsize(){
	long TempFilePos = ftell();
	fseek(0, SEEK_END);
	long Result = ftell();
	fseek(TempFilePos, SEEK_SET);
	return(Result);
}

//fputc
bool CFileWriter::fputc(char c){
	return(::fputc(c, stream)!=EOF);
}
//fputs
bool CFileWriter::fputs(const char* str){
	return(::fputs(str, stream)!=EOF);
}
//fwrite
size_t CFileWriter::fwrite(const void* buffer, size_t size, size_t count){
	return(::fwrite(buffer, size, count, stream));
}
//fprintf
int __cdecl CFileWriter::fprintf(const char* format, ...){
	char buffer[64 * 1024];
	const int n = wvsprintf(buffer, format, reinterpret_cast< va_list >(&format+1)) - 1;
	if(buffer[n]=='\n'){
		buffer[n+1] = '\0';
	}
	return(::fprintf(stream, "%s", buffer));
}

//ftell
long CFileWriter::ftell(){
	return(::ftell(stream));
}
//fseek
bool CFileWriter::fseek(long offset, int origin){
	return(::fseek(stream, offset, origin)==0);
}
//fgetpos
bool CFileWriter::fgetpos(fpos_t* pos){
	return(::fgetpos(stream, pos)==0);
}
//fsetpos
bool CFileWriter::fsetpos(const fpos_t* pos){
	return(::fsetpos(stream, pos)==0);
}

//feof
bool CFileWriter::feof(){
	return(::feof(stream)!=0);
}
//ferror
int CFileWriter::ferror(){
	return(::ferror(stream));
}



//補助関数
//暗号化文字列を書き込む
void SaveCryptedString(IFileWriter* FileWriter, BYTE CryptKey, const string& Data){
	char strData[64 * 1024] = {0};
	strcpy(strData, Data.c_str());
	for(unsigned int i=0;strData[i]!='\0';i++){
		if(strData[i]!=CryptKey){
			strData[i] ^= CryptKey;
		}
	}
	FileWriter->fwrite(strData, strlen(strData)+1, 1);
}

}
}
}

 ぶっちゃけ説明するべきところはないので、ソースコードを参照のこと(´Д`)

 

 

 

……以上で本項は終了である、お疲れ様でした(´Д`)
本項に見るべきところはあまりない、ソースコードを見れば分かるが単に標準関数のラップをしているに過ぎない。
本項で最も重要なのは設計である。メンテナンス性の高いプログラムを書くには、設計こそが最も重要なものである。
今後もこういったイディオムは積極的に紹介していく予定である、こういうところまであんまり書いてるサイトはないっぽいしなぁ(´Д`)

 

前へ                                        戻る                                     次へ