Microsoft Learnを参考にしながら、
C++を学んでいます。
今は例外処理の箇所を読んでいます。
そのに記載されていた例で、
ファイル処理の方法も載っていたので、
覚書で残しておきます。
サンプルは、
二つのファイルを比較して
内容が同じかどうかを比較するプログラムです。
// compile with: /EHsc
#include <Windows.h>
#include <stdlib.h>
#include <vector>
#include <iostream>
#include <string>
#include <limits>
#include <stdexcept>
using namespace std;
/* システムのエラーメッセージを得る */
string FormatErrorMessage(DWORD error, const string& msg)
{
static const int BUFFERLENGTH = 1024; //バッファサイズ
vector<char> buf(BUFFERLENGTH); // ベクターの作成
FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, 0, error, 0, buf.data(),
BUFFERLENGTH - 1, 0); // システムのエラーメッセージを受け取る
return string(buf.data()) + " (" + msg + ")"; // メッセージを返す。
}
/* 独自のエラークラス */
class Win32Exception : public runtime_error
{
private:
DWORD m_error;
public:
Win32Exception(DWORD error, const string& msg)
: runtime_error(FormatErrorMessage(error, msg)), m_error(error) { }
DWORD GetErrorCode() const { return m_error; }
};
/* エラーをスローする関数*/
void ThrowLastErrorIf(bool expression, const string& msg)
{
if (expression) {
throw Win32Exception(GetLastError(), msg);
}
}
class File
{
private:
HANDLE m_handle;
// Declared but not defined, to avoid double closing.
// ダブルクローズを避けるために宣言されていますが、定義されていません。
File& operator=(const File&); //定義はない
File(const File&); // 定義はない
public:
explicit File(const string& filename) // コンストラクタ
{
m_handle = CreateFileA(filename.c_str(), GENERIC_READ, FILE_SHARE_READ,
nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_READONLY, nullptr);
ThrowLastErrorIf(m_handle == INVALID_HANDLE_VALUE,
"CreateFile call failed on file named " + filename);
}
~File() { CloseHandle(m_handle); } //ハンドルを閉じる
HANDLE GetHandle() { return m_handle; } //ハンドルを渡す
};
/* ファイルサイズを得る */
size_t GetFileSizeSafe(const string& filename)
{
File fobj(filename); // FIleのインスタンスを作成する
LARGE_INTEGER filesize; // 64 ビット符号付き整数値
BOOL result = GetFileSizeEx(fobj.GetHandle(), &filesize);
ThrowLastErrorIf(result == FALSE, "GetFileSizeEx failed: " + filename);
if (filesize.QuadPart < (numeric_limits<size_t>::max)()) { // size_t型の最大値
return filesize.QuadPart; //64bitでサイズを得る。
}
else {
throw;
}
}
/* ファイルをベクターに読み込む*/
vector<char> ReadFileVector(const string& filename)
{
File fobj(filename);
size_t filesize = GetFileSizeSafe(filename); // ファイルサイズを得る。
DWORD bytesRead = 0;
vector<char> readbuffer(filesize); // ファイルデータを読み込むバッファ
BOOL result = ReadFile(fobj.GetHandle(), readbuffer.data(), readbuffer.size(),
&bytesRead, nullptr);
ThrowLastErrorIf(result == FALSE, "ReadFile failed: " + filename);
cout << filename << " file size: " << filesize << ", bytesRead: "
<< bytesRead << endl;
return readbuffer; // ベクターのバッファを返す。
}
/* 二つのファイル内容の比較 */
bool IsFileDiff(const string& filename1, const string& filename2)
{
return ReadFileVector(filename1) != ReadFileVector(filename2);
}
#include <iomanip>
int main(int argc, char* argv[])
{
string filename1("file1.txt");
string filename2("file2.txt");
try
{
if (argc > 2) {
filename1 = argv[1];
filename2 = argv[2];
}
cout << "Using file names " << filename1 << " and " << filename2 << endl;
if (IsFileDiff(filename1, filename2)) {
cout << "+++ Files are different." << endl;
}
else {
cout << "=== Files match." << endl;
}
}
catch (const Win32Exception& e)
{
ios state(nullptr);
state.copyfmt(cout); // 現在の書式を保存しておく
cout << e.what() << endl;
cout << "Error code: 0x" << hex << uppercase << setw(8) << setfill('0')
<< e.GetErrorCode() << endl;
cout.copyfmt(state); // 以前の書式を戻す。
}
〇各関数の説明
FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, 0, error, 0, buf.data(),BUFFERLENGTH - 1, 0);
システムメッセージを取り出します。
DWORD FormatMessage(
[in] DWORD dwFlags,
[in, optional] LPCVOID lpSource,
[in] DWORD dwMessageId,
[in] DWORD dwLanguageId,
[out] LPTSTR lpBuffer,
[in] DWORD nSize,
[in, optional] va_list *Arguments
);
1.dwFlags
書式設定オプション
FORMAT_MESSAGE_FROM_SYSTEMはシステム メッセージ テーブルでメッセージを探します。
2.lpSource
メッセージ定義の場所。ここでは使いません。
3.dwMessageId
要求されたメッセージのメッセージ識別子。
ここでは、GetLastError 関数の結果を使ってます。
4.dwLanguageId
要求されたメッセージの 言語識別子 。ここでは、0です。デフォルトの処理に任せます。
5.lpBuffer
書式設定されたメッセージを指定する null で終わる文字列を受け取るバッファーへのポインター。
ここでは、ベクターのbufのポインターを渡しています。
6.nSize
出力バッファーのサイズ
7.Arguments
書式設定されたメッセージの挿入値として使用される値の配列。ここでは使っていませんので、0を指定します。
〇CreateFileA(filename.c_str(), GENERIC_READ, FILE_SHARE_READ,
nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_READONLY, nullptr);
ファイルを開く関数です。
HANDLE CreateFileA(
[in] LPCSTR lpFileName,
[in] DWORD dwDesiredAccess,
[in] DWORD dwShareMode,
[in, optional] LPSECURITY_ATTRIBUTES lpSecurityAttributes,
[in] DWORD dwCreationDisposition,
[in] DWORD dwFlagsAndAttributes,
[in, optional] HANDLE hTemplateFile
);
パラメータ
1.lpFileName
ファイル名。ここではfilename.c_str()です。c_str()はC言語タイプの文字列を返します。
2.dwDesiredAccess
ファイルまたはデバイスへの要求されたアクセス。GENERIC_READです。これは読み取りアクセスです。
3.dwShareMode
ファイルまたはデバイスの要求された共有モード。
FILE_SHARE_READ:ファイルまたはデバイスで後続のオープン操作を有効にして、読み取りアクセスを要求します。
4.lpSecurityAttributes
2 つの独立した関連データ メンバーを含む SECURITY_ATTRIBUTES 構造体へのポインター。
ここでは、使わないのでnullptrを指定しています。
5.dwCreationDisposition
存在するか存在しないファイルまたはデバイスに対して実行するアクション。
OPEN_EXISTINGを指定。ファイルが存在しない場合は、最後のエラー コードは ERROR_FILE_NOT_FOUNDに設定されます。
6.dwFlagsAndAttributes
FILE_ATTRIBUTE_READONLYを指定。アプリケーションではファイルは読み取り専用となります。
7.hTemplateFile
GENERIC_READアクセス権を持つテンプレート ファイルへの有効なハンドル。
既存のファイルを開くため、nullptrを指定してます。
戻り値
エラーの場合は、INVALID_HANDLE_VALUEです。
〇GetFileSizeEx(fobj.GetHandle(), &filesize)
指定したファイルのサイズを取得します。
BOOL GetFileSizeEx(
[in] HANDLE hFile,
[out] PLARGE_INTEGER lpFileSize
);
サイズを受け取るにはLARGE_INTEGER型の変数が必要です。
〇ReadFile(fobj.GetHandle(), readbuffer.data(), readbuffer.size(),
&bytesRead, nullptr);
指定したファイルまたは入出力 (I/O) デバイスからデータを読み取ります。
BOOL ReadFile(
[in] HANDLE hFile, // デバイスへのハンドル
[out] LPVOID lpBuffer, // データを受信するバッファーへのポインター。
[in] DWORD nNumberOfBytesToRead, // 読み取る最大バイト数
[out, optional] LPDWORD lpNumberOfBytesRead, // 読み取られたバイト数
[in, out, optional] LPOVERLAPPED lpOverlapped
);
パラメータ
1.lpOverlapped
FILE_FLAG_OVERLAPPED で開かれた場合は、OVERLAPPED 構造体へのポインターが必要です。
ここでは、使わないのでnullptrです。