Microsoft Learnを参考にしながら、

C++を学んでいます。

今は例外処理の箇所を読んでいます。

 

そのに記載されていた例で、

ファイル処理の方法も載っていたので、

覚書で残しておきます。

 

サンプルは、

二つのファイルを比較して

内容が同じかどうかを比較するプログラムです。

 

  1. // compile with: /EHsc
  2. #include <Windows.h>
  3. #include <stdlib.h>
  4. #include <vector>
  5. #include <iostream>
  6. #include <string>
  7. #include <limits>
  8. #include <stdexcept>
  9.  
  10. using namespace std;
  11.  
  12. /* システムのエラーメッセージを得る */
  13. string FormatErrorMessage(DWORD error, const string& msg)
  14. {
  15.     static const int BUFFERLENGTH = 1024; //バッファサイズ
  16.     vector<char> buf(BUFFERLENGTH); // ベクターの作成
  17.     FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, 0, error, 0, buf.data(),
  18.         BUFFERLENGTH - 1, 0); // システムのエラーメッセージを受け取る
  19.     return string(buf.data()) + " (" + msg + ")"; // メッセージを返す。
  20. }
  21.  
  22. /* 独自のエラークラス */
  23. class Win32Exception : public runtime_error
  24. {
  25. private:
  26.     DWORD m_error;
  27. public:
  28.     Win32Exception(DWORD error, const string& msg)
  29.         : runtime_error(FormatErrorMessage(error, msg)), m_error(error) { }
  30.  
  31.     DWORD GetErrorCode() const { return m_error; }
  32. };
  33.  
  34. /* エラーをスローする関数*/
  35. void ThrowLastErrorIf(bool expression, const string& msg)
  36. {
  37.     if (expression) {
  38.         throw Win32Exception(GetLastError(), msg);
  39.     }
  40. }
  41.  
  42. class File
  43. {
  44. private:
  45.     HANDLE m_handle;
  46.  
  47.     // Declared but not defined, to avoid double closing.
  48.     // ダブルクローズを避けるために宣言されていますが、定義されていません。
  49.     File& operator=(const File&); //定義はない
  50.     File(const File&); // 定義はない
  51.  
  52. public:
  53.     explicit File(const string& filename) // コンストラクタ
  54.     {
  55.         m_handle = CreateFileA(filename.c_str(), GENERIC_READ, FILE_SHARE_READ,
  56.             nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_READONLY, nullptr);
  57.         ThrowLastErrorIf(m_handle == INVALID_HANDLE_VALUE,
  58.             "CreateFile call failed on file named " + filename);
  59.     }
  60.  
  61.     ~File() { CloseHandle(m_handle); } //ハンドルを閉じる
  62.  
  63.     HANDLE GetHandle() { return m_handle; } //ハンドルを渡す
  64. };
  65.  
  66. /* ファイルサイズを得る */
  67. size_t GetFileSizeSafe(const string& filename)
  68. {
  69.     File fobj(filename); // FIleのインスタンスを作成する
  70.     LARGE_INTEGER filesize; // 64 ビット符号付き整数値
  71.  
  72.     BOOL result = GetFileSizeEx(fobj.GetHandle(), &filesize);
  73.     ThrowLastErrorIf(result == FALSE, "GetFileSizeEx failed: " + filename);
  74.  
  75.     if (filesize.QuadPart < (numeric_limits<size_t>::max)()) { // size_t型の最大値
  76.         return filesize.QuadPart; //64bitでサイズを得る。
  77.     }
  78.     else {
  79.         throw;
  80.     }
  81. }
  82.  
  83. /* ファイルをベクターに読み込む*/
  84. vector<char> ReadFileVector(const string& filename)
  85. {
  86.     File fobj(filename);
  87.     size_t filesize = GetFileSizeSafe(filename); // ファイルサイズを得る。
  88.     DWORD bytesRead = 0;
  89.  
  90.     vector<char> readbuffer(filesize); // ファイルデータを読み込むバッファ
  91.  
  92.     BOOL result = ReadFile(fobj.GetHandle(), readbuffer.data(), readbuffer.size(),
  93.         &bytesRead, nullptr);
  94.     ThrowLastErrorIf(result == FALSE, "ReadFile failed: " + filename);
  95.  
  96.     cout << filename << " file size: " << filesize << ", bytesRead: "
  97.         << bytesRead << endl;
  98.  
  99.     return readbuffer; // ベクターのバッファを返す。
  100. }
  101.  
  102. /* 二つのファイル内容の比較 */
  103. bool IsFileDiff(const string& filename1, const string& filename2)
  104. {
  105.     return ReadFileVector(filename1) != ReadFileVector(filename2);
  106. }
  107.  
  108. #include <iomanip>
  109.  
  110. int main(int argc, char* argv[])
  111. {
  112.     string filename1("file1.txt");
  113.     string filename2("file2.txt");
  114.  
  115.     try
  116.     {
  117.         if (argc > 2) {
  118.             filename1 = argv[1];
  119.             filename2 = argv[2];
  120.         }
  121.  
  122.         cout << "Using file names " << filename1 << " and " << filename2 << endl;
  123.  
  124.         if (IsFileDiff(filename1, filename2)) {
  125.             cout << "+++ Files are different." << endl;
  126.         }
  127.         else {
  128.             cout << "=== Files match." << endl;
  129.         }
  130.     }
  131.     catch (const Win32Exception& e)
  132.     {
  133.         ios state(nullptr);
  134.         state.copyfmt(cout); // 現在の書式を保存しておく
  135.         cout << e.what() << endl;
  136.         cout << "Error code: 0x" << hex << uppercase << setw(8) << setfill('0')
  137.             << e.GetErrorCode() << endl;
  138.         cout.copyfmt(state); // 以前の書式を戻す。
  139.     }

 

〇各関数の説明

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です。