コラム
2026/02/27
プログラミングについて 第127回目
『作っておくと便利なちょっとしたプログラム その11』
今回は前回までのものをなんとか完成させましょう。
前回の時点で残っていたのは、非表示属性の処理とサブディレクトリの処理でした。
まず簡単な非表示属性についての処理を加えましょう。関数 display_total_lines のファイルを検索する部分、
do{
if(!is_file())continue;
の次に、
if(is_hide() && !with_hide)continue;
を追加し、関数 is_hide を
is_hide()
{
if(file_info.attrib & _A_HIDDEN)return 1;
return 0;
}
とするだけです。
前回言い忘れてしまいましたが、中身が簡単なのに関数 start_search、next_search、is_file を作ったことにちょっと理由がありました。関数にしないでそのままこれらの内容を呼び出し部分にはめこんでもいいのですが、このプログラムを他のOSに移植したり、他のコンパイラでコンパイルする方のためにプログラムの本題から独立させたのです。本来ならば1つのソースプログラムではなく、複数のソースプログラムに分けて機能単位にしておくならば、これらの関数とグローバルに定義しておいた
struct _finddata_t file_info;
long find_handle;
の部分もひとまとめにして、もっと移植性の高いものにできるはずです。
次に追加する機能はサブディレクトリの検索です。これはちょっと厄介です。まず次のことを考えてみましょう。いま指定されたファイル名が、
*.c
だったときには、おおよそ次のような流れになります。
(1)検索ファイルを *.c とし検索を開始。
(2)発見されたファイルでディレクトリ以外のものの行数を表示。
(3)ファイルの検索終了後、検索ファイルを *.* にしてサブディレクトリの検索を開始。
(4)発見されたサブディレクトリを指定されたファイル名と同様に検索ディレクトリのリストデータに追加。
(5)サブディレクトリの検索終了後(1)に戻りますが、そのときに検索するファイル名は サブディレクトリ+検索ファイル名(サブディレクトリ名が sub のときにはsub\*.c )にして検索します。
検索ディレクトリのリストデータという言葉がでてきましたが、ここではあくまでも概念として検索すべきディレクトリ名をいっぱい記憶しておけるデータとでも考えておいて下さい。
では、指定されたファイル名が、
\work\*.c
というようにカレントディレクトリ以外のもの(ファイル名以外に絶対パスまたは相対パスが指定されている)であったときには発見されたサブディレクトリが、sub だったとすると、\work\sub\*.c とするのが厄介になることと、最初は指定されたファイル名での検索だったものが、2回目からはサブディレクトリ名も含めた検索になるので上記の手順ではプログラムに表現するのが面倒になってしまいます。そこで、
(1)検索ファイル名をパス名とファイル名に分離し、検索ディレクトリリストに追加。
(2)検索するディレクトリ名とファイル名を連結し、検索を開始。
(3)検索ディレクトリリストが空になるまで、検索ディレクトリ名に分離した検索ファイルを連結して検索を開始。
(4)発見されたファイルでディレクトリ以外のものの行数を表示。
(5)ファイルの検索終了後、検索ファイルを *.* にしてサブディレクトリの検索を開始。
(6)発見されたサブディレクトリを検索ディレクトリリストに追加。
(7)検索が終了したら、検索ディレクトリの先頭のものを削除し、(2)からの処理を繰返す。
とします。最初の手順と殆ど似ているのですが、最初のものではサブディレクトリ内のファイルの検索については不明ですが、後のものでは最初からサブディレクトリ内のファイルの検索を考慮してある点が違います。このちょっとした手順の違いが実際のプログラムの記述を大きく左右しますので注意してください。
この手順で次のようなディレクトリ構造のファイルを検索する様子を考えてみましょう。
┌[sub11]
│ s11.c
┌[sub1]┤
│ s1.c │
[main]┤ └[sub12]
m.c │ s12.c
└[sub2]
s2.c
このとき、検索するファイル名を \main\*.c として指定したとし、各段階での状態を示します。
(1)\main\*.c を パス名とファイル名に分離。
検索ディレクトリリスト 検索ファイルリスト
---------------------- ------------------
\main\ *.c
(2)\main\ と *.c を連結し、\main\*.c でファイル検索を開始。
(3) m.c を発見。
(4) m.c の行数を表示。
(5)\main\*.* でディレクトリ検索を開始。
(6)sub1 を発見。
検索ディレクトリリスト 検索ファイルリスト
---------------------- ------------------
\main\ *.c
\main\sub1\
sub2 を発見。
検索ディレクトリリスト 検索ファイルリスト
---------------------- ------------------
\main\ *.c
\main\sub1\
\main\sub2\
(7)検索ディレクトリリストの先頭を削除する。
検索ディレクトリリスト 検索ファイルリスト
---------------------- ------------------
\main\sub1\ *.c
\main\sub2\
(2)からの処理を繰返す。
(2)\main\sub1\ と *.c を連結し、\main\sub1\*.c でファイル検索を開始。
(3) s1.c を発見。
(4) s1.c の行数を表示。
(5)\main\sub1\*.* でディレクトリ検索を開始。
(6)sub11 を発見。
検索ディレクトリリスト 検索ファイルリスト
---------------------- ------------------
\main\sub1\ *.c
\main\sub1\sub11\
\main\sub2\
sub12 を発見。
検索ディレクトリリスト 検索ファイルリスト
---------------------- ------------------
\main\sub1\ *.c
\main\sub1\sub11\
\main\sub1\sub12\
\main\sub2\
(7)検索ディレクトリリストの先頭を削除する。
検索ディレクトリリスト 検索ファイルリスト
---------------------- ------------------
\main\sub1\sub11\ *.c
\main\sub1\sub12\
\main\sub2\
(2)からの処理を繰返す。
(2)\main\sub1\sub11\ と *.c を連結し、\main\sub1\sub11\*.c でファイル検索を開始。
(3) s11.c を発見。
(4) s11.c の行数を表示。
(5)\main\sub1\sub11\*.* でディレクトリ検索を開始。
(6)なにも発見されなかった。
(7)検索ディレクトリリストの先頭を削除する。
検索ディレクトリリスト 検索ファイルリスト
---------------------- ------------------
\main\sub1\sub12\ *.c
\main\sub2\
(2)からの処理を繰返す。
(2)\main\sub1\sub12\ と *.c を連結し、\main\sub1\sub12\*.c でファイル検索を開始。
(3) s12.c を発見。
(4) s12.c の行数を表示。
(5)\main\sub1\sub12\*.* でディレクトリ検索を開始。
(6)なにも発見されなかった。
(7)検索ディレクトリリストの先頭を削除する。
検索ディレクトリリスト 検索ファイルリスト
---------------------- ------------------
\main\sub2\ *.c
(2)からの処理を繰返す。
(2)\main\sub2\ と *.c を連結し、\main\sub2\*.c でファイル検索を開始。
(3) s2.c を発見。
(4) s2.c の行数を表示。
(5)\main\sub2\*.* でディレクトリ検索を開始。
(6)なにも発見されなかった。
(7)検索ディレクトリリストの先頭を削除する。
検索ディレクトリリスト 検索ファイルリスト
---------------------- ------------------
*.c
(2)からの処理を繰返す。
(8)検索ディレクトリリストが空になったので処理を終了。
というようになります。処理の過程での検索ディレクトリリストが成長したり、検索済みのリストが削除される様子が理解できたでしょうか。sub1 内で発見されたサブディレクトリを検索ディレクトリリストの最後に追加してしまうと、確かに必要なファイルの行数は表示されますが、表示される順番がメチャメチャになってしまいます。ですので発見されたサブディレクトリは現在検索中のディレクトリと次の同レベルのディレクトリとの間に入れなければなりません。
説明が長くなってしまいました。このまま進めると長くなってしまいますので続きは次回にしましょう。
第127回 プログラミングについて『作っておくと便利なちょっとしたプログラム その11』
プログラミングについて 第127回目
『作っておくと便利なちょっとしたプログラム その11』
今回は前回までのものをなんとか完成させましょう。
前回の時点で残っていたのは、非表示属性の処理とサブディレクトリの処理でした。
まず簡単な非表示属性についての処理を加えましょう。関数 display_total_lines のファイルを検索する部分、
do{
if(!is_file())continue;
の次に、
if(is_hide() && !with_hide)continue;
を追加し、関数 is_hide を
is_hide()
{
if(file_info.attrib & _A_HIDDEN)return 1;
return 0;
}
とするだけです。
前回言い忘れてしまいましたが、中身が簡単なのに関数 start_search、next_search、is_file を作ったことにちょっと理由がありました。関数にしないでそのままこれらの内容を呼び出し部分にはめこんでもいいのですが、このプログラムを他のOSに移植したり、他のコンパイラでコンパイルする方のためにプログラムの本題から独立させたのです。本来ならば1つのソースプログラムではなく、複数のソースプログラムに分けて機能単位にしておくならば、これらの関数とグローバルに定義しておいた
struct _finddata_t file_info;
long find_handle;
の部分もひとまとめにして、もっと移植性の高いものにできるはずです。
次に追加する機能はサブディレクトリの検索です。これはちょっと厄介です。まず次のことを考えてみましょう。いま指定されたファイル名が、
*.c
だったときには、おおよそ次のような流れになります。
(1)検索ファイルを *.c とし検索を開始。
(2)発見されたファイルでディレクトリ以外のものの行数を表示。
(3)ファイルの検索終了後、検索ファイルを *.* にしてサブディレクトリの検索を開始。
(4)発見されたサブディレクトリを指定されたファイル名と同様に検索ディレクトリのリストデータに追加。
(5)サブディレクトリの検索終了後(1)に戻りますが、そのときに検索するファイル名は サブディレクトリ+検索ファイル名(サブディレクトリ名が sub のときにはsub\*.c )にして検索します。
検索ディレクトリのリストデータという言葉がでてきましたが、ここではあくまでも概念として検索すべきディレクトリ名をいっぱい記憶しておけるデータとでも考えておいて下さい。
では、指定されたファイル名が、
\work\*.c
というようにカレントディレクトリ以外のもの(ファイル名以外に絶対パスまたは相対パスが指定されている)であったときには発見されたサブディレクトリが、sub だったとすると、\work\sub\*.c とするのが厄介になることと、最初は指定されたファイル名での検索だったものが、2回目からはサブディレクトリ名も含めた検索になるので上記の手順ではプログラムに表現するのが面倒になってしまいます。そこで、
(1)検索ファイル名をパス名とファイル名に分離し、検索ディレクトリリストに追加。
(2)検索するディレクトリ名とファイル名を連結し、検索を開始。
(3)検索ディレクトリリストが空になるまで、検索ディレクトリ名に分離した検索ファイルを連結して検索を開始。
(4)発見されたファイルでディレクトリ以外のものの行数を表示。
(5)ファイルの検索終了後、検索ファイルを *.* にしてサブディレクトリの検索を開始。
(6)発見されたサブディレクトリを検索ディレクトリリストに追加。
(7)検索が終了したら、検索ディレクトリの先頭のものを削除し、(2)からの処理を繰返す。
とします。最初の手順と殆ど似ているのですが、最初のものではサブディレクトリ内のファイルの検索については不明ですが、後のものでは最初からサブディレクトリ内のファイルの検索を考慮してある点が違います。このちょっとした手順の違いが実際のプログラムの記述を大きく左右しますので注意してください。
この手順で次のようなディレクトリ構造のファイルを検索する様子を考えてみましょう。
┌[sub11]
│ s11.c
┌[sub1]┤
│ s1.c │
[main]┤ └[sub12]
m.c │ s12.c
└[sub2]
s2.c
このとき、検索するファイル名を \main\*.c として指定したとし、各段階での状態を示します。
(1)\main\*.c を パス名とファイル名に分離。
検索ディレクトリリスト 検索ファイルリスト
---------------------- ------------------
\main\ *.c
(2)\main\ と *.c を連結し、\main\*.c でファイル検索を開始。
(3) m.c を発見。
(4) m.c の行数を表示。
(5)\main\*.* でディレクトリ検索を開始。
(6)sub1 を発見。
検索ディレクトリリスト 検索ファイルリスト
---------------------- ------------------
\main\ *.c
\main\sub1\
sub2 を発見。
検索ディレクトリリスト 検索ファイルリスト
---------------------- ------------------
\main\ *.c
\main\sub1\
\main\sub2\
(7)検索ディレクトリリストの先頭を削除する。
検索ディレクトリリスト 検索ファイルリスト
---------------------- ------------------
\main\sub1\ *.c
\main\sub2\
(2)からの処理を繰返す。
(2)\main\sub1\ と *.c を連結し、\main\sub1\*.c でファイル検索を開始。
(3) s1.c を発見。
(4) s1.c の行数を表示。
(5)\main\sub1\*.* でディレクトリ検索を開始。
(6)sub11 を発見。
検索ディレクトリリスト 検索ファイルリスト
---------------------- ------------------
\main\sub1\ *.c
\main\sub1\sub11\
\main\sub2\
sub12 を発見。
検索ディレクトリリスト 検索ファイルリスト
---------------------- ------------------
\main\sub1\ *.c
\main\sub1\sub11\
\main\sub1\sub12\
\main\sub2\
(7)検索ディレクトリリストの先頭を削除する。
検索ディレクトリリスト 検索ファイルリスト
---------------------- ------------------
\main\sub1\sub11\ *.c
\main\sub1\sub12\
\main\sub2\
(2)からの処理を繰返す。
(2)\main\sub1\sub11\ と *.c を連結し、\main\sub1\sub11\*.c でファイル検索を開始。
(3) s11.c を発見。
(4) s11.c の行数を表示。
(5)\main\sub1\sub11\*.* でディレクトリ検索を開始。
(6)なにも発見されなかった。
(7)検索ディレクトリリストの先頭を削除する。
検索ディレクトリリスト 検索ファイルリスト
---------------------- ------------------
\main\sub1\sub12\ *.c
\main\sub2\
(2)からの処理を繰返す。
(2)\main\sub1\sub12\ と *.c を連結し、\main\sub1\sub12\*.c でファイル検索を開始。
(3) s12.c を発見。
(4) s12.c の行数を表示。
(5)\main\sub1\sub12\*.* でディレクトリ検索を開始。
(6)なにも発見されなかった。
(7)検索ディレクトリリストの先頭を削除する。
検索ディレクトリリスト 検索ファイルリスト
---------------------- ------------------
\main\sub2\ *.c
(2)からの処理を繰返す。
(2)\main\sub2\ と *.c を連結し、\main\sub2\*.c でファイル検索を開始。
(3) s2.c を発見。
(4) s2.c の行数を表示。
(5)\main\sub2\*.* でディレクトリ検索を開始。
(6)なにも発見されなかった。
(7)検索ディレクトリリストの先頭を削除する。
検索ディレクトリリスト 検索ファイルリスト
---------------------- ------------------
*.c
(2)からの処理を繰返す。
(8)検索ディレクトリリストが空になったので処理を終了。
というようになります。処理の過程での検索ディレクトリリストが成長したり、検索済みのリストが削除される様子が理解できたでしょうか。sub1 内で発見されたサブディレクトリを検索ディレクトリリストの最後に追加してしまうと、確かに必要なファイルの行数は表示されますが、表示される順番がメチャメチャになってしまいます。ですので発見されたサブディレクトリは現在検索中のディレクトリと次の同レベルのディレクトリとの間に入れなければなりません。
説明が長くなってしまいました。このまま進めると長くなってしまいますので続きは次回にしましょう。






