【MQL5】FileSeek関数について

MQL5リファレンス
スポンサーリンク
スポンサーリンク
  1. FileSeek関数の働き・役割
  2. FileSeek関数の引数について
    1. 第1引数: file_handle
    2. 第2引数: offset
    3. 第3引数: origin
  3. ENUM_FILE_POSITION列挙型について
    1. SEEK_SET
    2. SEEK_CUR
    3. SEEK_END
  4. FileSeek関数の戻り値について
  5. FileSeek関数を使ったサンプルコード
    1. スクリプトの実行手順
  6. サンプルコード解説1:プロパティ命令およびinput変数部分
    1. プロパティ命令 #property script_show_inputs
    2. 入力パラメータ(input変数)
  7. サンプルコード解説2:OnStart関数部分その1
    1. _RandomSeedの初期化
    2. 文字列の開始位置を保存する配列
    3. ファイル内の文字列数を格納する変数
    4. エラー情報のリセット
    5. ファイルを読み込みモードで開く
  8. サンプルコード解説3:OnStart関数部分その2
    1. ファイルが正常に開けたか確認
    2. エキスパートログにファイルが読み取り可能であることを出力
    3. ファイル内の文字列の開始位置を取得
    4. 文字列の数を配列のサイズから決定
  9. サンプルコード解説4:OnStart関数部分その3
    1. ファイルに文字列が存在するかの確認
    2. エキスパートログへの通知とファイルのクローズ
    3. ランダムに文字列番号を選択
  10. サンプルコード解説5:OnStart関数部分その4
    1. 選ばれた文字列の位置にファイルポインタを移動
    2. ファイルポインタの移動が成功した場合の処理
    3. ファイルを閉じる
    4. ファイルが開けなかった場合の処理
  11. サンプルコード解説6:GetStringPositions関数部分その1
    1. GetStringPositions関数の引数について
    2. 配列の初期サイズを指定
    3. 配列のサイズをリサイズ
    4. 文字列カウンタの初期化
  12. サンプルコード解説7:GetStringPositions関数部分その2
    1. ファイルが空でないかの確認
    2. 文字列の開始位置を配列に格納
    3. ファイルのエンコーディングによるシフト幅の決定
  13. サンプルコード解説8:GetStringPositions関数部分その3
    1. ファイルを読み込みながら文字列の開始位置を取得するループ
    2. 文字列を読み込む
    3. ファイルの終端かどうかのチェック
    4. 次の文字列の開始位置を配列に格納
    5. 配列のサイズを動的に増やす
    6. ループの終了
    7. 配列のサイズを最終的なサイズに調整

FileSeek関数の働き・役割

FileSeek関数は、開いたファイル内でファイルポインタの位置を変更するために使用されます。

この関数を使うことで、指定された位置からバイト単位でシフトさせ、ファイル内の特定の位置に移動することが可能です。これにより、ファイル内の任意の場所からデータの読み書きを効率的に行うことができます。

ファイルポインタとは、ファイル内のどの場所からデータを読み書きするかを指し示す位置情報のことです。ファイルポインタの移動は、ファイルの先頭、現在の位置、またはファイルの末尾を基準に行われます。

FileSeek関数の引数について

FileSeek関数引数は以下の3つです。

第1引数: file_handle

この引数は、FileOpen関数から取得したファイルハンドル(ファイルを識別するための番号)を指定します。FileOpen関数で開いたファイルに対して操作を行うため、このハンドルを渡します。

第2引数: offset

この引数は、ファイル内で移動したいバイト数を指定します。正の値を指定すると現在位置から前方に、負の値を指定すると後方に移動します。バイト単位での移動であり、必要に応じて位置を調整できます。

第3引数: origin

この引数は、移動の開始位置を指定します。ENUM_FILE_POSITION列挙型の値を使い、以下のいずれかを指定します。

それぞれの引数を正確に設定することで、ファイル内での柔軟なポインタ操作が可能になります。

ENUM_FILE_POSITION列挙型について

ENUM_FILE_POSITION列挙型は、FileSeek関数の第3引数で使用される列挙型で、ファイル内でポインタの移動を開始する位置を指定します。この列挙型には、次の3つの識別子があります。

SEEK_SET

SEEK_SETは、ファイルの先頭を基準にしてポインタを移動します。指定したバイト数だけファイルの先頭からシフトさせるため、主にファイルの冒頭から読み書きを行う場合に使用されます。例えば、オフセット値を0にするとファイルの先頭にポインタを移動させることができます。

SEEK_CUR

SEEK_CURは、現在のファイルポインタの位置を基準にしてポインタを移動します。正のオフセット値を指定すると前方に移動し、負のオフセット値を指定すると後方に移動します。この識別子は、ファイル内の現在の読み書き位置から相対的に移動したい場合に便利です。

SEEK_END

SEEK_ENDは、ファイルの末尾を基準にしてポインタを移動します。オフセット値を負にするとファイルの末尾から手前に移動し、正にすると末尾を超えて新たな位置に移動します。この識別子は、ファイルの最後にデータを書き込みたい場合や、末尾近くから読み込みたい場合に使用されます。

FileSeek関数の戻り値について

FileSeek関数は、ファイルポインタの移動に成功した場合に「true」を返します。移動に失敗した場合は「false」を返し、その際はGetLastError関数を使ってエラーの詳細を確認することができます。

この関数は、ファイル内でのポインタ移動が正しく行われたかどうかを判断するための重要な情報を提供します。たとえば、ファイルの存在やアクセス権の問題、指定した位置が無効である場合などに失敗することがあります。

FileSeek関数を使ったサンプルコード

//--- スクリプトの起動時に入力パラメータのウィンドウを表示する
#property script_show_inputs

//--- 入力パラメータ(ユーザーが指定するファイル名、ディレクトリ名、エンコーディングタイプ)
input string InpFileName="file.txt";      // ファイル名
input string InpDirectoryName="Data";     // ディレクトリ名
input int InpEncodingType=FILE_ANSI;      // エンコーディングタイプ:ANSI=32 または UNICODE=64

//+------------------------------------------------------------------+
//| スクリプトプログラムを開始する関数(スクリプトのエントリーポイント)                         |
//+------------------------------------------------------------------+
void OnStart()
{
  //--- 乱数を生成するための種を現在のTickCountから取得(乱数を使用する準備)
  _RandomSeed=GetTickCount();

  //--- 文字列の開始位置を保存するための配列
  ulong pos[];

  //--- ファイル内の文字列数を格納する変数
  int size;

  //--- エラー情報をリセットする(過去のエラー情報をクリア)
  ResetLastError();

  //--- 指定されたファイルを読み込みモードで開く
  int file_handle = FileOpen(InpDirectoryName + "//" + InpFileName, FILE_READ | FILE_TXT | InpEncodingType);

  //--- ファイルが正常に開けたか確認
  if(file_handle != INVALID_HANDLE)
  {
    //--- ファイルが読み取り可能であることをコンソールに出力
    PrintFormat("%s file is available for reading", InpFileName);

    //--- ファイル内の各文字列の開始位置を取得する関数を呼び出し
    GetStringPositions(file_handle, pos);

    //--- 取得した文字列の数を配列のサイズから決定
    size = ArraySize(pos);

    //--- もしファイルに文字列が存在しなければ
    if(!size)
    {
      //--- ファイルが空であることを通知し、ファイルを閉じて処理を終了する
      PrintFormat("%s file is empty!", InpFileName);
      FileClose(file_handle);
      return;
    }

    //--- 文字列番号をランダムに選択(0からsize-1までの範囲で乱数を生成)
    int ind = MathRand() % size;

    //--- 選ばれた文字列の位置にファイルポインタを移動
    if(FileSeek(file_handle, pos[ind], SEEK_SET) == true)
    {
      //--- ファイルポインタの移動が成功した場合、指定された位置の文字列を読み取り出力
      PrintFormat("String text with %d number: \"%s\"", ind, FileReadString(file_handle));
    }

    //--- ファイルを閉じる(操作が完了したのでリソースを解放)
    FileClose(file_handle);
    PrintFormat("%s file is closed", InpFileName);
  }
  else
  {
    //--- ファイルが開けなかった場合、エラーコードと共にエラーメッセージを出力
    PrintFormat("Failed to open %s file, Error code = %d", InpFileName, GetLastError());
  }
}

//+-------------------------------------------------------------------------------+
//| この関数はファイル内の文字列の開始点を配列に格納する関数                                          |
//+-------------------------------------------------------------------------------+
void GetStringPositions(const int handle, ulong &arr[])
{
  //--- 配列の初期サイズを指定(初期は127要素分のサイズ)
  int def_size = 127;

  //--- 配列のサイズを指定したサイズにリサイズ
  ArrayResize(arr, def_size);

  //--- 文字列カウンタ(文字列の開始位置を追跡するための変数)
  int i = 0;

  //--- ファイルが空でなければ最初の文字列の開始位置を配列に格納
  if(!FileIsEnding(handle))
  {
    arr[i] = FileTell(handle);  // 現在のファイルポインタの位置(最初の文字列の開始位置)を取得
    i++;  // 文字列カウンタをインクリメント
  }
  else
    return;  // ファイルが空であれば関数を終了

  //--- ファイルのエンコーディング(ANSIかUNICODEか)によってバイト単位のシフト幅を決定
  int shift;
  if(FileGetInteger(handle, FILE_IS_ANSI))
    shift = 1;  // ANSIなら1バイト
  else
    shift = 2;  // UNICODEなら2バイト

  //--- ファイルを最後まで読み込みながら文字列の開始位置を取得するループ
  while(1)
  {
    //--- 文字列を読み込む
    FileReadString(handle);

    //--- ファイルの終端かどうかをチェック
    if(!FileIsEnding(handle))
    {
      //--- 次の文字列の開始位置を配列に格納
      arr[i] = FileTell(handle) + shift;
      i++;  // 文字列カウンタをインクリメント

      //--- 配列がオーバーフローしそうな場合は配列のサイズを増やす
      if(i == def_size)
      {
        def_size += def_size + 1;  // 現在のサイズに更にサイズを追加
        ArrayResize(arr, def_size);  // 新しいサイズにリサイズ
      }
    }
    else
      break;  // ファイルの終端に到達したらループを抜ける
  }

  //--- 最終的な配列のサイズに調整
  ArrayResize(arr, i);
}

このスクリプトは、指定されたファイルを開き、その中にある文字列の開始位置を取得し、ランダムに選ばれた文字列を読み取ってエキスパートログに出力する処理を行います。具体的な挙動は以下の通りです。

スクリプトの実行手順

  1. ファイルの指定
    ユーザーは、スクリプト実行時にファイル名、ディレクトリ名、エンコーディング(ANSIまたはUnicode)を指定します。初期値としては、「file.txt」というファイルが「Data」ディレクトリ内に存在するものとして処理が進行します。
  2. ファイルを開く
    指定されたファイルを読み込みモードで開きます。ファイルが正常に開けなかった場合は、エラーコードをエキスパートログに出力して終了します。
  3. ファイルの内容を解析
    ファイルが開けた場合は、ファイル内の各文字列の開始位置を記録します。GetStringPositions関数が、ファイル内のすべての文字列の開始位置を配列に格納し、文字列の数を特定します。
  4. ファイルに文字列が存在するか確認
    ファイルが空であれば「ファイルが空です」というメッセージをエキスパートログに出力して処理を終了します。文字列が存在すれば次のステップに進みます。
  5. ランダムに文字列を選択
    ファイル内の文字列のうち、ランダムに1つの文字列を選びます。このランダム選択は、MathRand関数によって行われ、ファイル内の文字列の数に応じた範囲からランダムなインデックスが生成されます。
  6. 選択された文字列を読み込む
    ランダムに選ばれた文字列の開始位置にファイルポインタを移動し、その位置から文字列を読み込みます。
  7. エキスパートログに文字列を出力
    読み込んだ文字列をエキスパートログに出力します。エキスパートログには、ランダムに選ばれた文字列の番号(インデックス)と、その文字列の内容が表示されます。
  8. ファイルを閉じる
    ファイル操作が完了したら、ファイルを閉じてリソースを解放します。ファイルが正常に閉じられたことを確認するために、「ファイルが閉じられました」というメッセージもエキスパートログに出力されます。

サンプルコード解説1:プロパティ命令およびinput変数部分

//--- スクリプトの起動時に入力パラメータのウィンドウを表示する
#property script_show_inputs

//--- 入力パラメータ(ユーザーが指定するファイル名、ディレクトリ名、エンコーディングタイプ)
input string InpFileName="file.txt";      // ファイル名
input string InpDirectoryName="Data";     // ディレクトリ名
input int InpEncodingType=FILE_ANSI;      // エンコーディングタイプ:ANSI=32 または UNICODE=64

このセクションでは、コードの冒頭部分にあるプロパティ命令と入力パラメータ(input変数)の役割について説明します。

プロパティ命令 #property script_show_inputs

property script_show_inputs は、スクリプトが実行される際に、ユーザーに入力パラメータのウィンドウを表示するための命令です。このプロパティが宣言されていると、スクリプト実行時に入力用のウィンドウが開き、ユーザーはスクリプトに対して設定値を入力できます。特に、ファイル名やディレクトリ、エンコーディングの種類など、スクリプトの動作をカスタマイズする場合に便利です。

入力パラメータ(input変数)

inputキーワードを使用することで、ユーザーがスクリプトの実行前に変更できるパラメータを宣言します。この部分では、ファイル操作に必要な情報をユーザーが指定できるようにしています。以下の3つの入力パラメータが設定されています。

  • InpFileName(ファイル名)
    ファイル名を指定するための変数です。デフォルト値として「file.txt」が設定されています。スクリプトが操作する対象ファイルの名前を指定します。ユーザーが必要に応じてファイル名を変更することができます。
  • InpDirectoryName(ディレクトリ名)
    ファイルが保存されているディレクトリ名を指定するための変数です。デフォルトでは「Data」となっており、スクリプトはこのディレクトリ内のファイルを操作します。ユーザーはこの値を変更して他のディレクトリを指定することも可能です。
  • InpEncodingType(エンコーディングタイプ)
    ファイルのエンコーディング方式を指定するための変数です。デフォルトではANSIが設定されており、他にUnicodeを選択することもできます。ファイルがどの形式でエンコードされているかによって、このパラメータを設定する必要があります。

これらのinput変数は、ユーザーがスクリプト実行時にカスタマイズできる柔軟性を提供し、スクリプトの再利用性を高めています。

サンプルコード解説2:OnStart関数部分その1

//+------------------------------------------------------------------+
//| スクリプトプログラムを開始する関数(スクリプトのエントリーポイント)                         |
//+------------------------------------------------------------------+
void OnStart()
{
  //--- 乱数を生成するための種を現在のTickCountから取得(乱数を使用する準備)
  _RandomSeed=GetTickCount();

  //--- 文字列の開始位置を保存するための配列
  ulong pos[];

  //--- ファイル内の文字列数を格納する変数
  int size;

  //--- エラー情報をリセットする(過去のエラー情報をクリア)
  ResetLastError();

  //--- 指定されたファイルを読み込みモードで開く
  int file_handle = FileOpen(InpDirectoryName + "//" + InpFileName, FILE_READ | FILE_TXT | InpEncodingType);

このセクションでは、OnStart関数の冒頭部分について解説します。この関数は、スクリプトが実行された際に最初に呼び出される関数で、MQL5スクリプトでは、OnStart関数内で主要な処理が行われます。

_RandomSeedの初期化

最初に、_RandomSeedGetTickCount関数の値を格納しています。GetTickCount関数は、システムが起動してからの経過時間をミリ秒単位で返す関数です。これを乱数生成の種として使用します。_RandomSeedは乱数を生成する際に使われる変数で、同じ乱数列を再現しないために、システムの現在の時間を元にした値を使用します。このように時刻などの変動する値を乱数の種に指定することで、毎回異なる乱数を生成できるようになります。

文字列の開始位置を保存する配列

次に、配列を宣言しています。この配列は、ファイル内の文字列の開始位置を記録するために使用されます。ファイルから読み込んだ複数の文字列がどこから始まるのかを把握するために、各文字列の開始位置をこの配列に格納します。

ファイル内の文字列数を格納する変数

int型の変数sizeは、ファイル内の文字列の数を保持するために使われます。この変数には、後の処理で取得したファイル内の文字列数が格納され、後続の処理で使用されます。

エラー情報のリセット

ResetLastError関数を呼び出して、これまでに発生したエラー情報をクリアしています。ファイル操作やその他の操作でエラーが発生した場合、GetLastError関数でエラーコードを取得できますが、正確なエラー情報を得るために、処理を開始する前にエラー情報をリセットします。

ファイルを読み込みモードで開く

最後に、FileOpen関数を使って指定されたファイルを開いています。InpDirectoryNameとInpFileNameの値を組み合わせたファイルパスでファイルを開きます。このファイルは読み込み専用で開かれ、エンコーディング方式はユーザーが指定したもの(ANSIまたはUnicode)が使用されます。ファイルが正常に開けた場合、file_handleにはファイルハンドルが格納され、後続の処理で使用されます。

サンプルコード解説3:OnStart関数部分その2

//--- ファイルが正常に開けたか確認
  if(file_handle != INVALID_HANDLE)
  {
    //--- ファイルが読み取り可能であることをコンソールに出力
    PrintFormat("%s file is available for reading", InpFileName);

    //--- ファイル内の各文字列の開始位置を取得する関数を呼び出し
    GetStringPositions(file_handle, pos);

    //--- 取得した文字列の数を配列のサイズから決定
    size = ArraySize(pos);

このセクションでは、OnStart関数内でのファイル操作の処理について解説します。この部分では、開いたファイルが正常に操作できるかを確認し、その後ファイル内の文字列の処理が進行します。

ファイルが正常に開けたか確認

まず、ファイルが正常に開けたかどうかを確認しています。FileOpen関数で取得したファイルハンドルが有効かどうかをチェックしています。ファイルハンドルは、FileOpen関数が正常にファイルを開けた場合に取得されますが、開けなかった場合はINVALID_HANDLEが返されます。この比較により、ファイルが正しく開かれたかどうかを確認しています。

エキスパートログにファイルが読み取り可能であることを出力

ファイルが正常に開けた場合、PrintFormat関数を使ってエキスパートログにメッセージを出力しています。このメッセージは、指定されたファイルが読み取り可能であることをユーザーに知らせる役割を果たします。エキスパートログに表示されるメッセージには、ファイル名も含まれており、読み込み対象のファイルが正しいかどうかも確認できます。

ファイル内の文字列の開始位置を取得

次に、GetStringPositions関数を呼び出して、ファイル内の文字列の開始位置を取得します。この関数は、ファイル内の各文字列の先頭位置を検出し、その位置を配列に格納します。ここで取得した配列は、後の処理でファイルの任意の位置から文字列を読み込むために使用されます。

文字列の数を配列のサイズから決定

GetStringPositions関数によって格納された文字列の開始位置が入った配列のサイズをArraySize関数で取得し、size変数に格納します。これにより、ファイル内の文字列の数がわかり、後続の処理でその数を基にしてランダムに文字列を選んだり、特定の文字列を読み込む操作を行います。

サンプルコード解説4:OnStart関数部分その3

    //--- もしファイルに文字列が存在しなければ
    if(!size)
    {
      //--- ファイルが空であることを通知し、ファイルを閉じて処理を終了する
      PrintFormat("%s file is empty!", InpFileName);
      FileClose(file_handle);
      return;
    }

    //--- 文字列番号をランダムに選択(0からsize-1までの範囲で乱数を生成)
    int ind = MathRand() % size;

このセクションでは、OnStart関数内でファイル内の文字列が存在するか確認し、存在する場合はその文字列をランダムに選択する処理について解説します。

ファイルに文字列が存在するかの確認

まず、ファイル内に文字列が存在するかを確認しています。size変数には、先に取得した文字列の数が格納されています。この数が0、つまり文字列が存在しない場合は、if文の条件が真となり、以下の処理が実行されます。

エキスパートログへの通知とファイルのクローズ

ファイルに文字列が存在しない場合、PrintFormat関数を使ってエキスパートログに「ファイルが空である」ことを通知します。メッセージにはファイル名も含まれ、どのファイルが空であるかがわかるようになっています。通知後、FileClose関数でファイルを閉じて、処理を終了します。ファイルを開いたままにしておくとリソースを無駄に消費するため、必ず閉じる必要があります。

ランダムに文字列番号を選択

ファイルに文字列が存在する場合、次のステップではランダムに文字列を選択します。MathRand関数を使って0からsize-1までの範囲で乱数を生成し、ind変数に格納します。この変数は、後続の処理で選択された文字列を読み取る際に使用されます。

この処理により、ファイル内の任意の文字列をランダムに選んで操作を行うことができます。

サンプルコード解説5:OnStart関数部分その4

//--- 選ばれた文字列の位置にファイルポインタを移動
    if(FileSeek(file_handle, pos[ind], SEEK_SET) == true)
    {
      //--- ファイルポインタの移動が成功した場合、指定された位置の文字列を読み取り出力
      PrintFormat("String text with %d number: \"%s\"", ind, FileReadString(file_handle));
    }

    //--- ファイルを閉じる(操作が完了したのでリソースを解放)
    FileClose(file_handle);
    PrintFormat("%s file is closed", InpFileName);
  }
  else
  {
    //--- ファイルが開けなかった場合、エラーコードと共にエラーメッセージを出力
    PrintFormat("Failed to open %s file, Error code = %d", InpFileName, GetLastError());
  }
}

このセクションでは、選ばれた文字列の位置にファイルポインタを移動し、文字列を読み取る処理について解説します。さらに、ファイル操作が終了した際の後処理も含まれています。

選ばれた文字列の位置にファイルポインタを移動

FileSeek関数を使用して、選ばれた文字列の開始位置にファイルポインタを移動しています。この位置は、先ほどランダムに選択した文字列の番号(ind)を基に、pos配列から取得された開始位置です。FileSeek関数戻り値がtrueである場合、つまりファイルポインタの移動が成功した場合に、次の処理が行われます。

ファイルポインタの移動が成功した場合の処理

ファイルポインタが指定された位置に正常に移動すると、FileReadString関数を使用してその位置から文字列を読み取ります。読み取った文字列は、PrintFormat関数を使ってエキスパートログに出力されます。このメッセージには、選ばれた文字列の番号(ind)とその内容が含まれ、エキスパートログで確認できるようになっています。

ファイルを閉じる

ファイル操作が完了した後は、FileClose関数を使用してファイルを閉じます。ファイルを開いたままにしておくとシステムのリソースを無駄に消費するため、操作が完了したら必ずファイルを閉じるようにします。ファイルが正常に閉じられたことを確認するために、PrintFormat関数でエキスパートログに「ファイルが閉じられた」ことを通知します。

ファイルが開けなかった場合の処理

もしファイルが開けなかった場合、elseブロックに入ります。このブロックでは、PrintFormat関数を使ってエラーコードと共にエラーメッセージをエキスパートログに出力します。エラーコードはGetLastError関数で取得され、問題の詳細を特定する手助けとなります。

サンプルコード解説6:GetStringPositions関数部分その1

//+-------------------------------------------------------------------------------+
//| この関数はファイル内の文字列の開始点を配列に格納する関数                                          |
//+-------------------------------------------------------------------------------+
void GetStringPositions(const int handle, ulong &arr[])
{
  //--- 配列の初期サイズを指定(初期は127要素分のサイズ)
  int def_size = 127;

  //--- 配列のサイズを指定したサイズにリサイズ
  ArrayResize(arr, def_size);

  //--- 文字列カウンタ(文字列の開始位置を追跡するための変数)
  int i = 0;

GetStringPositions関数の引数について

この関数には2つの引数が渡されています。

配列の初期サイズを指定

最初に、配列の初期サイズを127要素に設定しています。これにより、配列が最初に持つメモリ領域の大きさが決まります。なぜ127という値が選ばれているかは特に重要ではありませんが、ファイル内に多数の文字列が存在する可能性がある場合には、この初期サイズを設定しておくことで、後から動的にサイズを増やす必要を減らす狙いがあります。

配列のサイズをリサイズ

次に、配列のサイズを初期設定値にリサイズしています。配列のサイズが決まっていない場合や、サイズが不十分な場合に備えて、初期サイズを事前に確保するための処理です。ArrayResize関数を使用することで、指定した要素数の領域を確保し、配列に必要なメモリを割り当てています。

文字列カウンタの初期化

ここでは、ファイル内の文字列の開始位置を追跡するためのカウンタを宣言しています。このカウンタは、ファイル内で何番目の文字列を処理しているかを追跡するために使用され、配列に格納する際のインデックスとしても機能します。このカウンタがファイル内の全文字列を追跡できるように、処理の進行に従ってカウンタの値が増えていきます。

この関数のこの部分は、ファイル内の文字列位置を効率的に格納するための準備を行っています。

サンプルコード解説7:GetStringPositions関数部分その2

 //--- ファイルが空でなければ最初の文字列の開始位置を配列に格納
  if(!FileIsEnding(handle))
  {
    arr[i] = FileTell(handle);  // 現在のファイルポインタの位置(最初の文字列の開始位置)を取得
    i++;  // 文字列カウンタをインクリメント
  }
  else
    return;  // ファイルが空であれば関数を終了

  //--- ファイルのエンコーディング(ANSIかUNICODEか)によってバイト単位のシフト幅を決定
  int shift;
  if(FileGetInteger(handle, FILE_IS_ANSI))
    shift = 1;  // ANSIなら1バイト
  else
    shift = 2;  // UNICODEなら2バイト

このセクションでは、GetStringPositions関数内でファイルの内容を確認し、文字列の開始位置を取得する処理について解説します。また、ファイルのエンコーディングに基づいて、バイト単位でのシフト幅を決定する処理も含まれています。

ファイルが空でないかの確認

最初に、FileIsEnding関数を使用して、ファイルが空でないかを確認しています。この関数は、ファイルの終端に達したかどうかを判定します。もしファイルが空であれば、すぐに関数を終了します(return)。ファイルが空でない場合は、ファイル内に少なくとも1つの文字列があることが保証され、その後の処理が続行されます。

文字列の開始位置を配列に格納

ファイルが空でないことが確認されたら、最初の文字列の開始位置を取得します。ここでは、FileTell関数を使用して、現在のファイルポインタの位置(最初の文字列の開始位置)を取得し、これを配列に格納します。この位置は、後続の処理でファイル内の特定の文字列を読み込むために使用されます。

さらに、文字列カウンタ(i)をインクリメントして、次の文字列の位置を追跡できるように準備します。

ファイルのエンコーディングによるシフト幅の決定

ファイルがANSI形式かUnicode形式かによって、ファイル内の文字のバイト数が異なるため、適切なシフト幅を決定します。

  • ANSIの場合: 1バイト単位でシフトするため、shift変数には1が格納されます。ANSIでは、1文字が1バイトとして扱われます。
  • Unicodeの場合: 2バイト単位でシフトするため、shift変数には2が格納されます。Unicodeでは、1文字が2バイトとして扱われるため、文字列の開始位置もその分だけシフトする必要があります。

このシフト幅は、後続の文字列をファイルから読み込む際に、正確に次の文字列の開始位置を計算するために使われます。

サンプルコード解説8:GetStringPositions関数部分その3

  //--- ファイルを最後まで読み込みながら文字列の開始位置を取得するループ
  while(1)
  {
    //--- 文字列を読み込む
    FileReadString(handle);

    //--- ファイルの終端かどうかをチェック
    if(!FileIsEnding(handle))
    {
      //--- 次の文字列の開始位置を配列に格納
      arr[i] = FileTell(handle) + shift;
      i++;  // 文字列カウンタをインクリメント

      //--- 配列がオーバーフローしそうな場合は配列のサイズを増やす
      if(i == def_size)
      {
        def_size += def_size + 1;  // 現在のサイズに更にサイズを追加
        ArrayResize(arr, def_size);  // 新しいサイズにリサイズ
      }
    }
    else
      break;  // ファイルの終端に到達したらループを抜ける
  }

  //--- 最終的な配列のサイズに調整
  ArrayResize(arr, i);
}

このセクションでは、GetStringPositions関数内で、ファイルを最後まで読み込みながら文字列の開始位置を取得し、配列に格納するループ処理について解説します。

ファイルを読み込みながら文字列の開始位置を取得するループ

この部分では、ファイルの終端に達するまでファイル内の文字列を読み込むために無限ループを使用しています。while(1)という構文は無限ループを意味しており、明示的にループを抜ける条件が満たされるまで繰り返し処理が行われます。

文字列を読み込む

ループ内でFileReadString関数を使用して、ファイルから文字列を1つずつ読み込みます。この関数は、ファイル内の現在の位置から文字列を読み取り、ファイルポインタを次の位置に進めます。

ファイルの終端かどうかのチェック

次に、FileIsEnding関数を使用して、ファイルの終端に達したかどうかを確認します。もしファイルの終端に達していなければ、次の文字列が存在するので、次の処理に進みます。終端に達した場合は、elseブロックに入り、ループを終了します。

次の文字列の開始位置を配列に格納

ファイルの終端に達していない場合、FileTell関数で現在のファイルポインタの位置を取得し、それにシフト幅を加えた値を配列に格納します。これにより、次の文字列の開始位置が記録され、後でファイルからその文字列を正確に読み込むことができます。配列に格納後、文字列カウンタ(i)をインクリメントし、次の文字列を処理する準備を行います。

配列のサイズを動的に増やす

文字列カウンタが配列の初期サイズ(def_size)に達した場合、配列がオーバーフローしないように、配列のサイズを動的に増やします。新しいサイズは現在のサイズに+1を加えたものとし、ArrayResize関数を使って配列のサイズをリサイズします。これにより、ファイル内に予想以上の文字列が含まれている場合でも、配列が不足することなく処理を続けられるようになります。

ループの終了

ファイルの終端に達すると、break文によってループが終了します。これにより、すべての文字列の開始位置が配列に格納された時点で処理を終えることができます。

配列のサイズを最終的なサイズに調整

最後に、配列のサイズを最終的な文字列数に合わせてリサイズします。これにより、実際に格納された文字列の数だけの領域が確保され、余分なメモリが使用されないようにします。ArrayResize関数を使って、文字列カウンタ(i)の値に基づき、配列のサイズを最適化します。

タイトルとURLをコピーしました