移動先にファイルがあった場合は、上書きしてファイルを移動する方法を解説します。
ファイルを移動する2つの機能「Name」「FSO.MoveFile」は、
どちらも行先ファイルを上書きする機能を持っていません。
よって
- 行先のファイルがあればKill/FSO.DeleteFilleで削除して、
- そのあとName/FSO.MoveFileで移動する
という手順を踏む必要があります。
1つのファイルを上書きして移動する場合
対象が1ファイルの場合は、
FileSystemObject(以下FSO)を使わなくても実装できます。
FSOを使った方が柔軟な記述が出来ますが、
実行結果や実行速度にほとんど差はありません。
FileSystemObjectを使わないバージョン
Dim 移動するファイルのパス As String 移動するファイルのパス = "C:\Users\○○\Desktop\テスト1\Book1.xlsx" Dim 移動先のファイルパス As String 移動先のファイルパス = "C:\Users\○○\Desktop\テスト2\Book1.xlsx" ' 移動先にファイルがあれば削除 If Dir(移動先のファイルパス) <> "" Then Kill 移動先のファイルパス End If Name 移動するファイルのパス As 移動先のファイルパス
FSOを使用しない場合は、
- Dir関数で既存ファイルがあるか判定
- Killステートメントで既存ファイルを削除
- Nameステートメントでファイルを移動
という手順になります。
同じパスが何度も出てきますので、
混乱しないよう丁寧な名前の変数を使いましょう。
FileSystemObjectを使うバージョン
Dim FSO As New FileSystemObject Dim 移動するファイルのパス As String 移動するファイルのパス = "C:\Users\○○\Desktop\テスト1\Book1.xlsx" Dim 移動先のファイルパス As String 移動先のファイルパス = "C:\Users\○○\Desktop\テスト2\Book1.xlsx" ' 移動先にファイルがあれば削除 If FSO.FileExists(移動先のファイルパス) Then FSO.DeleteFile 移動先のファイルパス End If FSO.MoveFile 移動するファイルのパス, 移動先のファイルパス
FSOを使用する場合は、
- FileExistsメソッドで既存ファイルがあるか判定
- DeleteFileメソッドで既存ファイルを削除
- MoveFileメソッドでファイルを移動
という手順になります。
上記のように2つのパスをどちらもファイルのフルパスで扱う場合は、
FSOを使わないバージョンと全く同じ文構造になります。
FSOを使うメリットは記述の柔軟性で、以下のように書くことも可能です。
Dim FSO As New FileSystemObject Dim 移動するファイル As File Set 移動するファイル = FSO.GetFile("C:\Users\○○\Desktop\テスト1\Book1.xlsx") Dim 移動先のフォルダ As Folder Set 移動先のフォルダ = FSO.GetFolder("C:\Users\○○\Desktop\テスト2") ' 移動先にファイルがあれば削除 If FSO.FileExists(移動先のフォルダ.Path & "\" & 移動するファイル.Name) Then FSO.DeleteFile 移動先のフォルダ.Path & "\" & 移動するファイル.Name End If 移動するファイル.Move 移動先のフォルダ.Path & "\"
このコードでは、
- FileオブジェクトとFolderオブジェクトを活用(.Name/.Pathが使える)
- MoveFileではなくファイル.Moveで移動を実行
- 行先のファイル名を省略(フォルダパスを指定)すれば同名の移動になる
あたりの機能を使っています。
複雑なファイル処理が必要な場合は、
FSOの方が楽に書けることも多いと思いますので使い分けてください。
FSOについての詳細はこちらの記事一覧からどうぞ
www.limecode.jp
汎用関数化
この処理をよく行う方は、関数化しておくと便利です。
Public FSO As New FileSystemObject ' ファイル上書き移動の汎用関数 Sub 既存ファイルは上書きしてファイルを移動する _ (移動するファイルのパス As String, 移動先のファイルパス As String) If FSO.FileExists(移動先のファイルパス) Then FSO.DeleteFile 移動先のファイルパス End If FSO.MoveFile 移動するファイルのパス, 移動先のファイルパス End Sub
' 実行例 Call 既存ファイルは上書きしてファイルを移動する( _ "C:\Users\○○\Desktop\テスト1\Book1.xlsx", _ "C:\Users\○○\Desktop\テスト2\Book1.xlsx")
中身は上で紹介したコードそのままという簡単設計の関数ですが、
実行例の通りCall1回でこの処理が行えるようになります。
ファイル操作は同じパスを何度も使うことが多いため、
汎用関数にするメリットが大きいです。
簡単な関数ほど、積極的に作っていきましょう。
ついでですが、Subプロシージャを分ける場合は、
Public FSO As New FileSystemObject
この宣言でFSOを全Subで使いまわせます。
これも便利なので覚えておいてください。
www.limecode.jp
複数のファイルを一括で移動する場合
MoveFileメソッドはワイルドカード*を使った一括移動ができますが、
残念ながら同名の上書き判定を行うことができません。
DeleteFileメソッドもワイルドカードが使えるので、
一見同じ判定文(*.xlsx)でDeleteとMoveを実行すればいけそうです。
' これでいけそう? FSO.DeleteFile "C:\Users\○○\Desktop\テスト2\*.xlsx" FSO.MoveFile "C:\Users\○○\Desktop\テスト1\*.xlsx" "C:\Users\○○\Desktop\テスト2\
ですが、これだと「同名でないテスト2のxlsxファイル」も削除されてしまいます。
この対策には2つのやり方があります。
CopyFileメソッドを利用する
CopyFileメソッドには第3引数「OverWriteFiles」があり、
これをTrueにする(省略時はTrueなので省略してもよい)ことで上書きコピーができます。
これとDeleteFileメソッドを組み合わせて、
- まずは複数のファイルを上書きコピー
- 完了後にコピーに使ったファイルを削除
とすることで疑似的に移動を実行できます。
コードはとても単純で、
FSO.CopyFile "C:\Users\○○\Desktop\テスト1\*.xlsx" "C:\Users\○○\Desktop\テスト2\" FSO.DeleteFile "C:\Users\○○\Desktop\テスト1\*.xlsx"
これで移動が完了します。
ただDeleteは取り返しのつかない処理なので、
パスの変え忘れなどに備え変数にしておきましょう。
Dim 移動ファイル判定パス As String 移動ファイル判定パス = "C:\Users\○○\Desktop\テスト1\*.xlsx" Dim 移動先フォルダパス As String 移動先フォルダパス = "C:\Users\○○\Desktop\テスト2\" FSO.CopyFile 移動ファイル判定パス, 移動先フォルダパス FSO.DeleteFile 移動ファイル判定パス
変数化をちゃんとやっておかないと、
Copyの引数を変えてDeleteの引数を変え忘れたとき悲劇が起こります。
すべてのファイルをループして上書き移動する
上記のCopyFileメソッドを利用した方法は簡単なのですが、
少々トリッキーなため微妙な仕様変更ができません。
上書き・スキップの判定を各ファイルで行う必要がある場合は、
For Eachを素直に使った以下のコードで実装してください。
Dim FSO As New FileSystemObject Dim 移動元フォルダ As Folder Set 移動元フォルダ = FSO.GetFolder("C:\Users\○○\Desktop\テスト1") Dim 移動先フォルダ As Folder Set 移動先フォルダ = FSO.GetFolder("C:\Users\○○\Desktop\テスト2") ' 移動元フォルダのすべてのファイルをループ Dim 移動候補 As File For Each 移動候補 In 移動元フォルダ.Files ' 条件に合致するファイルか判定 If 移動候補.Name Like "*.xlsx" Then ' 行先フォルダ内に同名ファイルがあれば削除 If FSO.FileExists(移動先フォルダ.Path & "\" & 移動候補.Name) Then FSO.DeleteFile 移動先フォルダ.Path & "\" & 移動候補.Name End If ' 移動を実行 移動候補.Move 移動先フォルダ.Path & "\" End If Next
すこし長いですがかなり素直なコードなため、
FSOのForEachを書ける方であれば解説なしで読めると思います。
FSOのFor Each文の基本についてはこちらを参照ください。
www.limecode.jp
なお、この処理はDir関数でも出来そうに見えますが必ずFSOが必要です。
なぜならDir関数によるループ中は別のDir関数が使えないからです。
今回の処理では、全ファイルをループしている途中で、
行先のフォルダに同名ファイルがあるかを調べますからね。
どうしてもDirの中でDirを使う必要が出るのですが、
これをやると第1Dirのループが誤作動を起こします。
こちらの記事で解説していますので興味があればご覧ください。
www.limecode.jp