【MQL5】FileFindFirst関数について

MQL5リファレンス
スポンサーリンク
スポンサーリンク

FileFindFirst関数の働き・役割

FileFindFirst関数は、指定された検索フィルタに基づいてディレクトリ(フォルダ)内のファイルやサブディレクトリ(ディレクトリ内にあるフォルダ)を検索するために使用されます。FileFindFirst関数を使用することで、特定の条件に合致するファイルやサブディレクトリを効率的に見つけることができます。

FileFindFirst関数の引数について

FileFindFirst関数は以下のように定義されています。

long FileFindFirst(
  const string file_filter,          // 検索フィルタ
  string& returned_filename,         // 見つかったファイルまたはサブディレクトリの名前
  int common_flag=0                  // 検索を定義するフラグ
);
  1. file_filter
    種類: 文字列
    説明: 検索フィルタを指定します。このフィルタは、ファイルが存在するディレクトリ(フォルダ)内の特定のパターンに一致するファイルやサブディレクトリ(フォルダ内のフォルダ)を探すために使われます。
    例えば、Dir1\\*のように指定することで、「Dir1」というフォルダ内にある全てのファイルやフォルダを対象にします。この場合、Dir1はフォルダ名、\はフォルダの区切り文字、*はすべてのファイルやフォルダを意味します。
  2. returned_filename
    種類: 文字列の参照
    説明: 検索によって最初に見つかったファイルまたはサブディレクトリ(フォルダ内のフォルダ)の名前が返されます。このパラメータは関数が呼び出された後に更新され、見つかったファイルやサブディレクトリの名前が格納されます。
  3. common_flag
    種類: 整数型
    説明: 検索の範囲を指定するフラグです。common_flagにFILE_COMMONを指定すると、全てのクライアント端末の共有フォルダ(\Terminal\Common\Files 内のフォルダ)内のファイルが検索対象になります。それ以外の場合はローカルフォルダ(ユーザーの端末内のフォルダ)内のファイルが検索対象となります。初期値は0です。

FileFindFirst関数の戻り値について

FileFindFirst関数戻り値は、検索オブジェクトのハンドル(リソースやデータへのアクセスを表す識別子)です。
このハンドルは後続のFileFindNext関数を使用してさらにファイルやサブディレクトリの検索に使用されます。もしフィルタに一致するファイルやサブディレクトリが存在しない場合や、ディレクトリが空の場合はINVALID_HANDLEが返されます。

FileFindFirst関数を使う際の注意点

FileFindFirst関数を使用する際の注意点は以下の通りです:

  • セキュリティ上の理由から、MQL5ではファイル操作が厳しく制御されています。MQL5で操作されるファイルは、ファイルサンドボックス(安全にファイルを操作するための隔離環境)の外に存在することはできません。
  • 検索が終了したら、FileFindClose関数を使用してハンドルを必ず閉じる必要があります。これはリソースリーク(プログラムが使用したリソースを解放しないことによる無駄なリソース消費)を防ぐためです。

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

以下に、FileFindFirst関数を使用したサンプルコードを示します。このコードは指定されたフィルタに基づいてファイルやディレクトリを検索し、結果をエキスパートログに出力します。

//--- スクリプト起動時に入力パラメータウィンドウを表示する
#property script_show_inputs
//--- フィルタ
input string InpFilter="Dir1\\*"; // 検索フィルタとして使用する文字列

//+------------------------------------------------------------------+
//| スクリプトプログラムを開始する関数                              |
//+------------------------------------------------------------------+
void OnStart()
 {
  string file_name; // 見つかったファイルやディレクトリの名前を格納する変数
  string int_dir=""; // 検索対象のディレクトリを格納する変数
  int i=1, pos=0, last_pos=-1; // カウンタ変数iと検索位置を管理する変数

  //--- 最後のバックスラッシュを探す
  while(!IsStopped()) // スクリプトが停止されていない間ループを続ける
    {
     pos=StringFind(InpFilter,"\\",pos+1); // バックスラッシュの位置を検索
     if(pos>=0) // バックスラッシュが見つかった場合
        last_pos=pos; // 最後に見つかった位置を更新
     else // バックスラッシュが見つからなかった場合
        break; // ループを終了
    }

  //--- フィルタにフォルダ名が含まれる
  if(last_pos>=0) // バックスラッシュが見つかった場合
     int_dir=StringSubstr(InpFilter,0,last_pos+1); // フィルタからフォルダ名部分を抽出

  //--- ローカルフォルダのルートで検索ハンドルを取得する
  long search_handle=FileFindFirst(InpFilter,file_name); // 検索を開始し、ハンドルを取得

  //--- FileFindFirst() の実行が成功したかをチェックする
  if(search_handle!=INVALID_HANDLE) // 検索ハンドルが有効かどうかを確認
    {
    //--- 渡された文字列がファイル名かディレクトリ名かをループでチェックする
    do
       {
        ResetLastError(); // 最後のエラーコードをリセット
        //--- ファイルの場合、関数が true を返し、ディレクトリの場合 ERR_FILE_IS_DIRECTORY エラーを返す
        FileIsExist(int_dir+file_name); // ファイルが存在するか確認
        // ファイルかディレクトリかをログに出力
        PrintFormat("%d : %s name = %s",i,GetLastError()==ERR_FILE_IS_DIRECTORY ?"Directory" : "File",file_name);
        i++; // カウンタをインクリメント
       }
    while(FileFindNext(search_handle,file_name)); // 次のファイルまたはディレクトリを検索
    //--- 検索ハンドルを閉じる
    FileFindClose(search_handle); // 検索ハンドルを閉じる
    }
  else // 検索ハンドルが無効な場合
    Print("Files not found!"); // ファイルが見つからなかったことをログに出力
 }

サンプルコードに使われた関数や文法要素の解説

このサンプルコードは、指定されたフィルタに基づいてディレクトリ内のファイルやサブディレクトリを検索し、その結果をエキスパートログに出力します。以下は各部分の詳細な解説です。若干複雑なので、小分けにして解説していきたいと思います。

解説1:グローバル領域での記述

//--- スクリプト起動時に入力パラメータウィンドウを表示する
#property script_show_inputs
//--- フィルタ
input string InpFilter="Dir1\\*"; // 検索フィルタとして使用する文字列

#property script_show_inputs

#property script_show_inputsは、スクリプトが起動する際に入力パラメータウィンドウを表示するためのディレクティブです。
※ディレクティブとは、コンパイラ(プログラムを機械語に翻訳するソフトウェア、MQL5で言えばメタエディターにあたります)に対して特定の動作を指示するための命令です。

このディレクティブを使用することで、ユーザーがスクリプトを実行する前に入力パラメータを設定できるようになります。

MQL5では、スクリプトインジケータエキスパートアドバイザーなどのプログラムにはプロパティ(属性や設定)を付与することができます。

#property script_show_inputsを設定すると、スクリプトが実行される際に自動的に入力パラメータウィンドウが表示され、ユーザーはパラメータを確認・変更することができます。

input string InpFilter=”Dir1\*”;

input string InpFilter="Dir1\\*";は、スクリプトが実行される際にユーザーが指定できる入力パラメータを定義しています。

  • input
    inputは、ユーザーがスクリプト実行時に設定できるパラメータを定義するために使用されます。このパラメータは、スクリプトの実行中に変更することはできませんが、実行前にユーザーが値を設定できます。
  • string
    パラメータのデータ型を示しています。この場合、文字列型のパラメータです。
  • InpFilter
    パラメータの名前です。スクリプト内でこの名前を使用して、ユーザーが指定した値にアクセスします。
  • “Dir1\*”
    パラメータの初期値です。この初期値は、フォルダ内の全てのファイルやサブディレクトリを検索するためのフィルタとして機能します。具体的には、Dir1という名前のフォルダ内にある全てのファイルやサブディレクトリを対象にします。
  • Dir1
    フォルダ名を示しています。
  • \
    フォルダの区切り文字です。Windowsのパス名において、ディレクトリを区切るために使用されます。ここでは、Dir1フォルダの中を指定するために使用されています。
  • *
    ワイルドカード文字(ファイル名や拡張子の一部を指定するために使用される特別な文字)で、全てのファイルやサブディレクトリを意味します。このフィルタを使用することで、Dir1フォルダ内の全ての項目が検索対象となります。

この部分のコードは、スクリプトが開始される前にユーザーが入力パラメータを設定できるようにし、検索対象のフォルダとファイルフィルタを定義するためのものです。

解説2:OnStart関数の中1

void OnStart()

void OnStart()は、スクリプトが開始される際に最初に実行される関数です。MQL5では、スクリプトエキスパートアドバイザーインジケータなどの各プログラムタイプごとに特定のエントリポイント関数が用意されています。スクリプトの場合、OnStart関数がそのエントリポイントとなります。

この関数戻り値を持たず(void)、引数も取りません。スクリプトが実行されると、この関数内のコードが一度だけ実行されます。

変数の宣言

cppコードをコピーするstring file_name; // 見つかったファイルやディレクトリの名前を格納する変数
string int_dir=""; // 検索対象のディレクトリを格納する変数
int i=1, pos=0, last_pos=-1; // カウンタ変数iと検索位置を管理する変数
  • string file_name;
    この変数は、見つかったファイルやディレクトリの名前を格納するために使用されます。FileFindFirst関数FileFindNext関数の呼び出し時に、検索結果として返されるファイル名やディレクトリ名がここに格納されます。
  • string int_dir=””;
    この変数は、検索対象のディレクトリを格納するために使用されます。検索フィルタから抽出されたディレクトリ名が格納されます。初期値として空の文字列が設定されています。
  • int i=1, pos=0, last_pos=-1;
    • i
      この変数はカウンタとして使用されます。検索結果の番号付けやループの回数を数えるために使用されます。初期値は1です。
    • pos
      この変数は、文字列内の検索位置を管理するために使用されます。StringFind関数を使って検索フィルタ内のバックスラッシュの位置を見つける際に使用されます。初期値は0です。
    • last_pos
      この変数は、最後に見つかったバックスラッシュの位置を格納するために使用されます。StringFind関数でバックスラッシュを検索するループ内で更新されます。初期値は-1です。

これらの変数は、ファイル検索のプロセスや結果の処理において重要な役割を果たします。OnStart関数内でこれらの変数がどのように使用されるかを理解することで、スクリプト全体の動作を把握することができます。

解説3:OnStart関数の中2

  //--- 最後のバックスラッシュを探す
  while(!IsStopped()) // スクリプトが停止されていない間ループを続ける
    {
     pos=StringFind(InpFilter,"\\",pos+1); // バックスラッシュの位置を検索
     if(pos>=0) // バックスラッシュが見つかった場合
        last_pos=pos; // 最後に見つかった位置を更新
     else // バックスラッシュが見つからなかった場合
        break; // ループを終了
    }

whileループと停止チェック

  • while(!IsStopped()) この行は、スクリプトが停止されていない間ループを継続するための条件を設定しています。IsStopped関数は、スクリプトが停止されたかどうかを確認するための関数です。この関数falseを返している間はループが継続され、trueを返すとループが終了します。このチェックを行うことで、スクリプトが外部から停止された場合に適切に処理を中断できるようにしています。

バックスラッシュの位置を検索

  • pos=StringFind(InpFilter,”\”,pos+1); この行は、指定された文字列InpFilter内でバックスラッシュ(\)の位置を検索するために使用されます。StringFind関数は、検索対象の文字列内で指定した文字や文字列の最初の出現位置を返します。検索は、前回見つけた位置posの次の位置(pos+1)から開始されます。

バックスラッシュが見つかった場合

  • if(pos>=0) この条件は、バックスラッシュが見つかったかどうかを確認します。
    StringFind関数がバックスラッシュの位置を見つけた場合、その位置を返します。見つからなかった場合、-1を返します。条件pos>=0は、バックスラッシュが見つかったことを意味します。
  • last_pos=pos; バックスラッシュが見つかった場合、その位置poslast_posに格納します。これにより、最後に見つかったバックスラッシュの位置を追跡することができます。

バックスラッシュが見つからなかった場合

  • else は、if条件が満たされなかった場合に実行されるコードブロックを示します。ここでは、バックスラッシュが見つからなかった場合に実行されます。
  • break; この行は、ループを終了するための命令です。whileループから抜け出すために使用されます。これにより、バックスラッシュの検索が完了したことを示します。

このセクションでは、検索フィルタInpFilter内の最後のバックスラッシュを見つけるための処理が行われています。バックスラッシュはディレクトリパス(フォルダの位置を示す文字列)として使用されるため、最後のバックスラッシュの位置を特定することで、フィルタ内のディレクトリパスとファイル名を分離する準備が整います。

解説4:OnStart関数の中3

//--- フィルタにフォルダ名が含まれる
if(last_pos>=0) // バックスラッシュが見つかった場合
  int_dir=StringSubstr(InpFilter,0,last_pos+1); // フィルタからフォルダ名部分を抽出

//--- ローカルフォルダのルートで検索ハンドルを取得する
long search_handle=FileFindFirst(InpFilter,file_name); // 検索を開始し、ハンドルを取得

フィルタにフォルダ名が含まれる

  • if(last_pos>=0) この条件文は、先に見つけた最後のバックスラッシュの位置last_posが0以上であるかを確認します。これは、バックスラッシュが見つかった場合にのみ実行される処理を示します。
  • int_dir=StringSubstr(InpFilter,0,last_pos+1); StringSubstr関数は、指定された文字列の一部を抽出するために使用されます。この場合、InpFilter文字列の先頭から最後に見つかったバックスラッシュの位置last_posの次の文字まで(last_pos + 1)の部分文字列を抽出し、それをint_dir変数に格納します。これにより、フィルタからディレクトリパス(フォルダの位置を示す文字列)部分を抽出します。たとえば、InpFilter"Dir1\\*"の場合、int_dirには"Dir1\\"が格納されます。

ローカルフォルダのルートで検索ハンドルを取得する

  • long search_handle=FileFindFirst(InpFilter,file_name); FileFindFirst関数は、指定されたフィルタInpFilterに基づいてディレクトリ内のファイルやサブディレクトリの検索を開始するために使用されます。
    この関数は、検索を開始し、最初に見つかったファイルまたはサブディレクトリの名前をfile_nameに格納します。また、検索に使用するハンドル(識別子)を返します。このハンドルは、後続のFileFindNext関数で追加のファイルやサブディレクトリを検索するために使用されます。

この部分のコードは、フィルタ内のディレクトリパスを抽出し、指定されたフィルタに基づいてファイルやサブディレクトリの検索を開始するための準備を行います。検索が正常に開始されると、検索ハンドルが返され、次のステップでそのハンドルを使用してさらなる検索操作を行うことができます。

解説5:OnStart関数の中4

//--- FileFindFirst() の実行が成功したかをチェックする
if(search_handle!=INVALID_HANDLE) // 検索ハンドルが有効かどうかを確認
  {
  //--- 渡された文字列がファイル名かディレクトリ名かをループでチェックする
  do
     {
      ResetLastError(); // 最後のエラーコードをリセット
      //--- ファイルの場合、関数が true を返し、ディレクトリの場合 ERR_FILE_IS_DIRECTORY エラーを返す
      FileIsExist(int_dir+file_name); // ファイルが存在するか確認
      // ファイルかディレクトリかをログに出力
      PrintFormat("%d : %s name = %s",i,GetLastError()==ERR_FILE_IS_DIRECTORY ?"Directory" : "File",file_name);
      i++; // カウンタをインクリメント
     }
  while(FileFindNext(search_handle,file_name)); // 次のファイルまたはディレクトリを検索
  //--- 検索ハンドルを閉じる
  FileFindClose(search_handle); // 検索ハンドルを閉じる
  }

検索ハンドルが有効かどうかを確認

ファイルまたはディレクトリをループでチェックする

  • do
    このキーワードは、do-whileループの開始を示します。do-whileループは少なくとも一度は実行され、その後の条件に基づいて繰り返されます。
  • ResetLastError()
    ResetLastError関数は、最後に発生したエラーコードをリセットします。これにより、次にエラーが発生したときに正しいエラーコードを取得できるようにします。
  • FileIsExist(int_dir+file_name)
    FileIsExist関数は、指定されたファイルまたはディレクトリが存在するかどうかを確認します。存在する場合はtrueを返し、存在しない場合はfalseを返します。このサンプルでは、int_dirfile_nameを連結して完全なパスを作成し、そのパスが存在するかを確認します。
  • PrintFormat(“%d : %s name = %s”,i,GetLastError()==ERR_FILE_IS_DIRECTORY ?”Directory” : “File”,file_name)
    PrintFormat関数は、ファイルまたはディレクトリの情報をフォーマットしてログに出力します。以下は、各フォーマット指定子三項演算子についての解説です。
  • %d 整数値をフォーマットするための指定子です。この場合、カウンタ変数iの値を整数として出力します。
  • %s 文字列をフォーマットするための指定子です。この例では2つの文字列指定子が使用されています。
  • 三項演算子 (条件式 ? 真の値 : 偽の値) この演算子は条件式が真の場合に返す値と、偽の場合に返す値を指定するために使用されます。以下がその構造です。
GetLastError() == ERR_FILE_IS_DIRECTORY ? "Directory" : "File"
  • 条件式 (GetLastError() == ERR_FILE_IS_DIRECTORY)
    GetLastError関数ERR_FILE_IS_DIRECTORYを返すかどうかをチェックします。
  • 真の値 (“Directory”) 条件式が真(エラーコードがディレクトリを示す)場合に返される値です。
  • 偽の値 (“File”) 条件式が偽(エラーコードがファイルを示す)場合に返される値です。
    このコードでは、ファイルがディレクトリの場合は”Directory”という文字列が、ファイルの場合は”File”という文字列が出力されます。
  • i++
    この行は、カウンタ変数iをインクリメント(1を加算)します。これにより、次のファイルまたはディレクトリを処理する際に番号が一つ増えます。
  • while(FileFindNext(search_handle,file_name))
    この条件は、FileFindNext関数が次のファイルまたはディレクトリを見つけた場合にループを継続することを示します。FileFindNext関数は、FileFindFirst関数で取得した検索ハンドルを使用して、次のファイルまたはディレクトリを検索します。見つからない場合はfalseを返し、ループを終了します。

検索ハンドルを閉じる

この部分のコードは、FileFindFirst関数が有効な検索ハンドルを返した場合に、ディレクトリ内のファイルやサブディレクトリを順番に処理し、その結果をログに出力します。検索が終了したら、検索ハンドルを閉じてリソースを解放します。

解説6:OnStart関数の中5

else // 検索ハンドルが無効な場合
  Print("Files not found!"); // ファイルが見つからなかったことをログに出力

検索ハンドルが無効な場合

  • else
    if条件が満たされなかった場合に実行されるコードブロックを示します。ここでは、FileFindFirst関数が無効なハンドルを返した場合に実行されます。
  • Print(“Files not found!”);
    この行は、エラーメッセージをログに出力します。FileFindFirst関数が有効なハンドルを返さなかった場合、指定されたフィルタに一致するファイルやディレクトリが見つからなかったことを示します。Print関数はメッセージをエキスパートログに出力するために使用されます。

この部分のコードは、FileFindFirst関数が無効なハンドルを返した場合に、ファイルやディレクトリが見つからなかったことをユーザーに知らせるためのエラーハンドリングを行っています。

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