- ベストアンサー
VBSでサブフォルダの絶対パスを取得する関数の作成方法について
- VBSで指定したフォルダ内の全てのサブフォルダの絶対パスを取得する関数を作成したいです。
- 現在は再帰的に呼び出すSubプロシージャとして作成していますが、一つのFunctionプロシージャに統合したいと考えています。
- イメージとしてはSplit関数のように、関数に文字列を送ると配列が返ってくるような形にしたいです。
- みんなの回答 (1)
- 専門家の回答
質問者が選んだベストアンサー
2つのプロシージャを1つにするのですか。 呼び出し元の引数が増えてしまってもよいのなら難しくはないですよね。 mgGetAllSubFolders_rcrsvをSubからFunctionに変え、 最後に__mgGetAllSubFolders_rcrsv = fldarry を書き加え、 関数mgGetAllSubFoldersは廃止し、 呼び出し側は mgGetAllSubFolders_rcrsv("C:\hoge",Array("")) で一応OKですよね。 (途中の戻り値を使ってないけど、とかいう話はやめておきます) でもおそらく質問者様のご希望は、呼び出し元の引数を変えずに関数を1つにしたいのでしょうね。 そうであるなら、呼び出し元での引数と再帰の途中の呼び出しの引数を同じにしないといけないのでByRef fldarryは削除しなければならず、結果を累積する配列を受け渡していく方法は使えません。それと、返す値は配列ですよね。 となると、より深い再帰呼び出しから配列を受け取ってそれを結合して現在の階層の結果を加えて1つの配列で返すしかないと思います。 VBScriptで配列を結合するには、結合結果を入れる配列を用意してそこに要素を1つずつコピーするか、Join→文字列結合→Splitの順に処理するか、といったあたりでしょう。いずれにしろ今の方法より効率は悪そうですがフォルダ数が多くなければ大きな問題にはならずに済みそうです。 Join→文字列結合→Splitの方が書くのが簡単そうだったのでこちらを採用しました。 Function mgGetAllSubFolders_rcrsv(ByVal folderpath) __Set sbfld = Wscript.CreateObject("Scripting.FileSystemObject").GetFolder(folderpath).SubFolders __Dim fldstr __If sbfld.Count Then ____For Each tmp In sbfld ______fldstr = fldstr & Join(mgGetAllSubFolders_rcrsv(tmp.Path), Chr(0)) & Chr(0) ____Next __End If __mgGetAllSubFolders_rcrsv = Split(fldstr & folderpath, Chr(0)) End Function と、ここまで1つにまとめることを書きましたが、個人的見解を言わせていただくなら、今回のような場合に1つにまとめる必要はないと思います。 分かれている構成でも特に問題があるとは思いません。 逆に配列処理の効率はまとめた場合より優れているようですし、さらにたとえばCreateObjectの実行回数を1回だけにしてオブジェクトを使い回したいといった場合でも分かれている方が対応が容易だと思います。
お礼
あー、やっと返事ができる。というのが率直な感想です。 本当に遅くなってしまってすみません。 なんか、やはり再帰を理解しきるのは難しいと思いました。 まず一つ目の >呼び出し元の引数が増えてしまってもよいのなら難しくはないですよね。 まず、これを理解するのが最初でした。VBSに不慣れなもので構文エラー出まくり でてこずりました。 そして出来た関数が次です。 単にIsEmpty関数を使いたかったので呼び出しもとのArray("")をArray(Empty)としました。 111111111111111111111111111111111111 Option Explicit Function mgGetAllSubFolders_rcrsv(ByVal folderpath, ByRef retarry) Dim sbfld Set sbfld = Wscript.CreateObject("Scripting.FileSystemObject").GetFolder(folderpath).SubFolders If sbfld.Count Then For Each tmp In sbfld Call mgGetAllSubFolders_rcrsv(tmp.Path, retarry) Next End If If IsEmpty(retarry(0)) Then retarry(0) = folderpath Else ReDim Preserve retarry(UBound(retarry) + 1) retarry(UBound(retarry)) = folderpath End If Set sbfld = Nothing mgGetAllSubFolders_rcrsv = retarry End Function '呼び出しもと-----------main------------------------------------- Dim tmp, hogestr hogestr = "" For Each tmp In mgGetAllSubFolders_rcrsv(".\hogefolder", Array(Empty)) hogestr = hogestr & tmp & vbCrLf Next Wscriot.Echo("lastecho : " & vbCrLf & hogestr) >(途中の戻り値を使ってないけど、とかいう話はやめておきます) 末尾再帰を使った方が速いらしいので mgGetAllSubFolders_rcrsv("..\test", Array(Empty)) のように、文字列、戻り値の配列というように定義するのが正当と思います。しかし、 >でもおそらく質問者様のご希望は、呼び出し元の引数を変えずに関数を1つにしたいのでしょうね。 そう、そのとおりなんです。 とにかくmgGetAllSubFolders_rcrsv("..\test")の形にしたかったのです。 LISPのように関数の中に関数が定義できればとりあえず一つの関数で定義できるのですが、 VBSではSubの中にSubもFunction、Functionの中にSubもFunctionも定義できないということをいろいろやっている うちに知りました。それで二つの手続きになってしまったのです。 二つの手続きだとコピペするときに一つ(mgGetAllSubFolders)だけコピペされちゃうかなと思い、 Function .... End Function だけを見てコピペできればいいと思い、一つにこだわりました。 そして、queuerev2さんのコードを理解しようとしたらめちゃめちゃ時間がかかりました。 理解した結果たどり着いたコードが次です。 222222222222222222222222222222222222222 Option Explicit Function mgGetAllSubFolders_rcrsv(ByVal folderpath) Dim sbfld,tmp Set sbfld = Wscript.CreateObject("Scripting.FileSystemObject").GetFolder(folderpath).SubFolders If sbfld.Count Then For Each tmp In sbfld mgGetAllSubFolders_rcrsv = mgGetAllSubFolders_rcrsv & Join(mgGetAllSubFolders_rcrsv(tmp.Path), "<>") & "<>" Next End If mgGetAllSubFolders_rcrsv = Split(mgGetAllSubFolders_rcrsv & folderpath, "<>") Set sbfld = Nothing End Function '呼び出しもと-----------main------------------------------------- Dim tmp,echostr echostr = "" For Each tmp In mgGetAllSubFolders_rcrsv(".\hogefolder") echostr = echostr & tmp & vbCrLf Next Wscript.Echo("lastecho : " & vbCrLf & echostr) なにもかわってねーじゃねーかこの野郎という感じですが、 fldstr = fldstr & Join(mgGetAllSubFolders_rcrsv(tmp.Path), Chr(0)) & Chr(0) を mgGetAllSubFolders_rcrsv = mgGetAllSubFolders_rcrsv & Join(mgGetAllSubFolders_rcrsv(tmp.Path), "<>") & "<>" にしました。 つまり、Dim fldstrの宣言がなくなりました。 まだ、再帰がどのように呼び出されて、どのような変数の適用範囲をもっているのかいまひとつ理解していないので これが正しいことのかわかりませんが。。。。。。。。。 あと、区切り文字をchr(0)とするテクニックを知りました。有難うございます。 今回はデバッグで見えなくなってしまうので"<>"を使いましたが、chr(0)にするのはスマートだと思います。 これからはchr(0)を活用していきたいと思います。 Splitで渡した戻り値をJoinでつなげると言う発想はすごいと思いました。 最初のmgGetAllSubFoldersは "<>foderpah<>foderpah<>foderpah<>foderpah<>foderpah<>foderpah<>foderpah" のような戻り値が返るようにして、Split(mgGetAllSubFolders(pathstr))のようにして 外側でSplitを使うという考えだったのですが、 これだと、最初か最後に空要素が入ってしまうし、なによりも外側のSplitが気に入りませんでした。 そこを関数内でSplitとJoinを使うことによって回避しているのすごいと思いました。 なので理解にすごく時間が掛かってしまいました。というか、まだ理解しきれてないと思います。 後、デバッグしていて気がついたのですが Option Explicitは何気に大事と思いました。 返答が遅くなりすみません、そして有難うございました。 もっと再帰を使って再帰を完全に理解したいと思います。