和風スパゲティのレシピ

日本語でコーディングするExcelVBA

FileSystemObject入門-FSOって何が便利なの?

ファイル・フォルダを処理するときに扱う「FileSystemObject」について、
何が便利で、何のために使うのかを解説します。

  • FileSystemObjectが便利と聞いて使ってみたけど、何が便利なのかよくわからない
  • Dirシリーズとの違いやFileSystemObjectにするメリットがよくわからない

という方はぜひご一読ください。


なお、この記事は本サイトのFSO入門シリーズの最後の記事です。




「FSOはまだ触ったことがない」という方は、
1番目の記事から読んでいただければと思います。


では始めます。

FileSystemObjectとは

FileSystemObjectとは、フォルダ・ファイルを処理するための機能・関数がつまったオブジェクトです。(以下、FSOと略記します)


特に「フォルダやファイルをオブジェクトとして扱うことができる」のが特長です。


と、ここでいきなり混乱ポイントなのですが、上の文にオブジェクトが2回出てきて、
要約すると「FSOはオブジェクトの扱いに長けたオブジェクト」です。

「オブジェクト」という概念に慣れていない方にはわかりづらい表現ですよね。


なんのこっちゃ?って方は、とりあえずFSO自体がオブジェクトということは忘れて、
FSOは便利ツール集」だと思って読み進めてください。

  • FileSystemObjectとは「便利ツール集」
  • フォルダやファイルをオブジェクトとして扱えるようになる

この2つが重要なキーワードです。

「オブジェクトで扱える」と何が便利なの?

「FileSystemObjectって何が便利なの?」を理解する上では、
オブジェクトというものを何となくでいいので理解する必要があります。


なにせ
フォルダやファイルをオブジェクトとして扱うことができるのが特長
ですからね。

オブジェクトとして扱うことができることを便利だと感じれないと、
FileSystemObjectが便利だと感じることはできません。


ということで、一旦FSOから離れて、
馴染みのある「シートやセル」でオブジェクトの便利さを説明したいと思います。

もしセルがオブジェクトじゃなかったら

突然ですがVBAの仕様を変えます。

セルの指定が、

Range("[Book1.xlsx]Sheet1!A1")

こうしないとできなくなったとしましょう。


つまり、

Workbooks("Book1.xlsx").Worksheets("Sheet1").Range("A1")

と書けなくなったとします。


そしてシートの指定も同じ関数を使う必要があり、

Range("[Book1.xlsx]Sheet1")

こう書いた場合はシートを指すことにします。

何となく不便そうですよね?


Range(○○)がセルなのかシートなのかわからないですし、

Dim R As Long
For R = 1 To 10
    Range("[Book1.xlsx]Sheet1!A" & R)
Next

こんなループ文だと読んだり改修したりが大変そうです。


「セルから親シート名を取得したい」となったときも、今のVBAは

シート名 = セル.Parent.Name

と「親の名前」でストレートに取得できますが、変わった世界では、

Dim ]の位置 As String: ]の位置 = Instr(アドレス, "]")
Dim !の位置 As String: !の位置 = Instr(アドレス, "!")

シート名 = Mid(アドレス, ]の位置, !の位置 - ]の位置 + 1)

のように「セルのアドレスのうち]から!まで」を取得する必要があります。
これはたまったもんじゃないですね。


さらには「値の変更」「背景色の変更」「セルの削除」をする際に、

セル.Value = 1
セル.Interior.Color = RGB(255, 255, 255)
セル.Delete

で済んでいたものが、

ValueChange セル, 1
InteriorColorChange セル, RGB(255, 255, 255)
DeleteRange セル

こんな風に「全部関数」になったとしましょう。


これも大変ですよね。

普通に読みづらいことこの上ないですし、
書くときにはどの関数が何に使えるのかを全部覚えないといけません。


間違って

ValueChange シート, 1

と書いたらエラーになるわけですから、難易度爆上がりです。


そう考えると、
Rangeオブジェクトのメンバー候補
この選択肢ってすごく有難いですよね。(たまに出ないけど)



と、これらが「オブジェクトの便利さ」です。

失ってはじめてわかる何とやら


「オブジェクト」のメリットをざっくりまとめると、

  • オブジェクト同士に親子関係があって階層化されている
  • 名前や値、背景色などの状態も子供として持っている
  • その状態の変更を値の代入で行える
  • 削除やコピーといった動きも実行できる

こんな感じになります。

普段当たり前に使ってるとあまり意識しないですが、
あらためて考えるととても便利ですよね。


ちなみにここでについでに覚えておくとよいですが、

  • 名前や値、背景色などの状態も子供として持っている
  • その状態の変更を値の代入で行える

この状態のことをプロパティと呼びます。

  • 削除やコピーといった動きも実行できる

こっちがメソッドです。


そして一番大事な「オブジェクト同士の親子関係」ですが、
プログラミングの世界ではこの親子関係を「.」で表し、

Rangeオブジェクトのメンバー候補

こんな風に「親から子を選べる」ことで、プログラミングを楽にしてくれています。


この「.」を自作することを「オブジェクト指向プログラミング」と呼んだりします。
(オブジェクト指向警察さん怒らないで)


オブジェクトというものが、何となくわかりましたでしょうか?

Dir関数とFileSystemObjectの違い

さて勘の鋭い人は、上の説明でもうなんとなくわかったかもしれません。

上記の例において、
セルもシートもRange(アドレス)でしか指定できない世界がDir関数で、
WorkbooksとかWorksheetsとかが使える世界がFileSystemObjectです。


例えば「フォルダパスからフォルダ名を取得したい」としましょう。

「C:\Users\○○\Desktop\商品」から「商品」を取り出す処理です。


Dirの世界ではそもそもそんな関数がないため、

フォルダ名 = Mid(フォルダパス, InstrRev(フォルダパス, "\")+1)

と、最後の\より右を取ってくるしかありません。


しかしFileSystemObjectの世界では、

Set 対象フォルダ = FSO.GetFolder(フォルダパス)
フォルダ名 = 対象フォルダ.Name

と、FolderオブジェクトをSetすれば、あとはNameプロパティで持ってこれます。



続いて親フォルダの取得を見てみましょう。

「C:\Users\○○\Desktop\商品」から「C:\Users\○○\Desktop」を取り出す処理です。


これもDirの世界では「最後の\より右を削除」する必要がありますが、
FSOの世界では、

親フォルダパス = FSO.GetParentFolderName(フォルダパス)

と、専用のプロパティが用意されています。

すでにFolderオブジェクトにSetしていた場合は、

Set 親フォルダ = 対象フォルダ.ParentFolder
親フォルダパス = 親フォルダ.Path

とオブジェクトから親オブジェクトを取得することまでできます。



さらに、FSOの世界ではフォルダ名の変更も簡単で、

対象フォルダ.Name = "新しいフォルダ名"

で終わりです。


Dirの世界ではNameステートメントで実行しますが、
フルパス⇒フルパスでしか変更ができないためここでも文字列処理が必要です。


要はDirの世界はフォルダパスという文字列をキーにして成り立っているんですね。

対してFSOの世界は各フォルダがちゃんと親子関係で結ばれているということです。



そしてこの親子関係で重要なのが、
FSOの世界では「ファイルもフォルダの子供として定義されている」ことで、

Dim ファイル As File
For Each ファイル In 対象フォルダ.Files

Next

と、「対象フォルダを親とするすべてのファイル」をFilesで持ってこれます。

全シートを処理するときに書く、

Dim ws As Worksheet
For Each ws In 対象ブック.Worksheets

Next

このFor Each文とそっくりですよね。


Dir関数でこれをやる場合は、

Dim dirファイル名 As String
dirファイル名 = Dir("C:\Users\○○\Desktop\商品\*")
Do While dirファイル名<>""

    dirファイル名 = Dir()
Loop

このようなコードになり、
「Dir()と引数を省略すると、前回の検索設定で次のファイルを持ってくる」
という、あらためて考えると割と無理やりな仕様で頑張る必要があります。


と、これがFileSystemObjectの特長である、
フォルダやファイルをオブジェクトとして扱うことができる」の全容です。

何となくわかってもらえましたでしょうか?


この「オブジェクトとして扱う」ことのメリット

  • オブジェクト同士に親子関係があって階層化されている
  • 名前や値、背景色などの状態も子供として持っている
  • その状態の変更を値の代入で行える
  • 削除やコピーといった動きも実行できる

は、プログラミングの基礎となる考え方です。


FileSystemObjectを勉強する上で欠かせない考え方ですので、
FileSystemObjectと一緒に、ぜひとも習得していきましょう。

FileSystemObjectを便利に感じるためには

コピペでマクロを作る分には正直変わらない

さてFileSystemObjectの便利さはもうわかったと思いますので、
あとは余談です。

なぜこの便利さが理解しづらいのでしょうか?


それはズバリ「コピペで作る分にはDirとそんなに変わらない」からだと思います。


先ほどFSOが優れているとした「すべてのファイルを持ってくる」ですが、

Dim FSO As New FileSystemObject

Dim 対象フォルダ As Folder
Set 対象フォルダ = FSO.GetFolder("C:\Users\○○\Desktop\商品")
Dim ファイル As File
For Each ファイル In 対象フォルダ.Files

Next
Dim dirファイル名 As String
dirファイル名 = Dir("C:\Users\○○\Desktop\商品\*")
Do While dirファイル名<>""

    dirファイル名 = Dir()
Loop

このどちらのコードが書くのが簡単か考えましょう。


まず「空で」書こうとすると、この選択肢↓を使って書けるFSOが圧倒的に楽です。
FileSystemObjectのメンバー候補

そもそもDir関数は

  • ファイルがあればファイル名を返す
  • Dir()と引数を省略すると次のファイルを持ってくる
  • 最後まで見終わったら""を返す

という仕様を知らないと、このループ処理は書けません。

完全な初見殺しですし、忘れたらもう一度調べるより他ありません。


対してFSOのコードは、もし忘れていても普通に書けます。

メソッド名もきれいな英語で予測しやすいので、

Dim ws As Worksheet
For Each ws In 対象ブック.Worksheets

Next

これが空で書ける人なら、下手すると初見で書けてしまうかもしれません。



ただですね。


コードを空で書くのはまだ難しい初学者にとっては、
普通にDir関数の方が簡単
なんですよ。


Dir関数が「文字列処理だけでコードが理解ができる」と言うのは、
それはそれで素晴らしい利点なんです。

FSOには変数宣言とSetがある分、コード量が多くなる傾向がありますしね。



ぶっちゃけ言ってしまえば、
コピペ⇒書き換えで使う分にはDirシリーズの方が普通に便利です。


ということで、さんざんFSOが便利と語っておいてあれなんですが、
FSOには「使うだけで便利!」なものではありません。

FSOが便利と思えるようになるには時間がかかります。


ただし、FSOの考え方はオブジェクト指向の考え方に触れるにはもってこいです。
FSOが便利と思えるように訓練するのは、とても意義があります。


ということでまとめますと、

  • オブジェクトに慣れていない方にはFSOは別に便利じゃない
  • オブジェクトに慣れるための学習題材としてFSOはとても便利

という感じで、便利の意味がちょっと違います。


FileSystemObjectは(教科書として)便利

というのが真理な気がしますので、
そんな風にとらえて勉強していってもらえればと思います。

オブジェクトというメリットを抜きにしたFSOとDir関数の違い

今回は「オブジェクト」という着眼点でFSOを解説しましたが、
FSOにはオブジェクトを抜きにした以下の特長があります。

  • フォルダごと何かをするのが得意
  • フォルダ内のフォルダを検索するのが得意
  • 環境依存文字の対応範囲が広い(UTF-16に対応)

 
特に1番目の「中身のファイルごとフォルダをコピー/削除/移動」は、
Dir関数群では実行する手段がありません。


この3つはFile/Folderオブジェクトに関係なくFSOがDir関数群に勝る点です。


こちらの長所もしっかりおさえておきましょう。


この長所を既に知っていた方は、本記事を
「FSOはフォルダの操作が得意だけど、ファイル操作でもFSOって便利なの?」
と読みかえてもらうとわかりやすいかもしれませんね。

FileSystemObjectの参照方法について

最後に、参照設定についても軽く触れておきます。


FSOを使うには、まずFSOをオブジェクト変数にSetする必要があり、
これを「バインディング」と呼びます。


このバインディングには2通りのやり方があり、

◇ 遅延バインディング

Dim FSO As Object
Set FSO = CreateObject("Scripting.FileSystemObject")


◇ 事前バインディング
「ツール」→「参照設定」→「Microsoft Scripting Runtime」にレ点を入れてから

Dim FSO As New FileSystemObject

この二つがありますね。


両者のメリット比較はこんな感じです。

  • 遅延バインディングのメリットは参照設定がいらないこと
  • 事前バインディングのメリットは入力選択肢がでること

 

これを「事前バインディング目線」で比較すると、

Microsoft Scripting Runtimeの参照設定
このレ点チェックをする手間が増える代わりに、

FileSystemObjectのメンバー候補
この入力選択肢が手に入るということになります。



ここまで読んでいただいた方には、もうどちらにすべきかはわかりますよね?


FSOには事前バインディングを用いてください。


ファイルオブジェクトのメンバー候補

この選択肢がないと「オブジェクトを扱えるFSO」にした意味がありません。



一見、参照設定がいらない遅延バインディングが楽そうに感じてしまうんですけどね。

個人的にはこの遅延バインディングが、
「FSOの何が便利なのかわからない」を生む諸悪の根源になっている気がします。


ネットのコードは「コピペだけで動く」ことも重要なため、
参照設定が不要な遅延バインディングで書かれているものが多いです。

そして初学者さんがそれを持ってきて書き換えたりしようとすると、
せっかくのプロパティやメソッドが手入力になってしまい、
「なぜ便利なのか?」が迷宮入りします。


繰り返しになりますが、「オブジェクトを活用する」ことは、
FileSystemObjectのメンバー候補
この選択肢を使ってコードを書くこと」から始まります。

その練習にFileSystemObjectはとても便利という話でしたからね。


参照設定のレ点チェックは、最初は戸惑うかもしれませんが、
慣れれば大した作業ではありません。

同じく「Microsoft Scripting Runtime」を使用するDictionaryもかなり便利なため、
マクロブックを作るときは必ず参照している方も多いくらいです。


FSOを使用する際は、必ず事前バインディングを用いましょう。




以上でFileSystemObject入門「FSOって何が便利なの?」を終わります。

FSOをすぐに便利と感じるのは難しいかもしれませんが、
プログラミングを学ぶ上で、オブジェクトに慣れる題材としてはとても便利です。


そういうものと分かった上でFSOに触れていった方が、
案外習得も早く、割とすぐ便利に感じるかもしれません。

ぜひFileSystemObjectを勉強してみてください。