達人養成塾 小川です。
さてさて。
前回書いた、以下のブログ。
不要な重複ファイルは自動削除するツールをVBAで自製してみた – Excel マクロ・VBA
で、紹介した、以下のプログラム。
‘Microsoft Scripting Runtime への参照設定をしてください
Sub mydel()
Debug.Print “start”
Dim c As Integer
c = CInt(InputBox(“set max num”))
myMove “.jpg”, c
myMove “.png”, c
Debug.Print “end”
End Sub
Sub myMove(ext As String, mx As Integer)
Dim fs As New Scripting.FileSystemObject
Dim fl(1) As Scripting.File
Dim fBase As String
Dim fname(2) As String
Dim s As String
Dim c As Long
Dim suf As Long
For c = 1 To mx
s = String(4 – Len(CStr(c)), “0”) & c
fBase = ThisWorkbook.path & “\IMG_” & s
fname(0) = fBase & ext
suf = 1
fname(1) = fBase & “-” & suf & ext
Do While fs.FileExists(filespec:=fname(1))
Set fl(0) = fs.GetFile(fname(0))
Set fl(1) = fs.GetFile(fname(1))
If fl(0).DateLastModified = fl(1).DateLastModified And fl(0).Size = fl(1).Size Then
fname(2) = ThisWorkbook.path & “\del\IMG_” & s & “-” & suf & ext
Debug.Print “exists” & vbTab & fname(2)
fs.MoveFile Source:=fname(1), Destination:=fname(2)
End If
suf = suf + 1
fname(1) = fBase & “-” & suf & ext
Loop
Next
For c = LBound(fl) To UBound(fl)
Set fl(c) = Nothing
Next
Set fs = Nothing
End Sub
今日から、この完成形そのものではなく、ここに至るまでに書いた未完成のプログラムをいろいろ紹介していこうと思う。
何故、そんなことをするのか、というと。
どういう過程を踏んで、プログラムができあがるか、ということについて、ぜひ、あなたに知ってもらいたいからである。
世の中の実に多くの人が、ネットや書籍に載っているサンプルコードを見て、
「こういうのは、『アタマがよくて、特別な訓練を受けた人』が、『アタマの中で完成型を作ってから、特別な、厳粛な手続きを踏みながら、ダーッ!っと、上から順番に書いている』のだ。」
と思いこんでいる。
(そして、そういう人は、同じ機能のマクロを書こうとしたとき、ネットに転がっているサンプルを、上からなぞってマネしようとする)
しかし、実際には、違う。
プログラムを書ける人が、自分の手元の問題を解決する目的でプログラムを書くとき、実際にはどうやっているのかというと。
- 手元の資料や、過去に書いたサンプルコードをパクってきて
- とりあえず、テキトーにカスタマイズしてみて
- 別のところから、またパクってきて
- さっきのヤツに、無理矢理くっつけてみて
- そんなことをしていたらグチャグチャなコードになってしまったので、一度、スッキリさせて
- 気が向いたから、もう少し機能を追加して
なんて感じで、「けっこうテキトー」に作っているのだ。
ただ、その過程をそのまま晒すとカッコ悪そうだから、人に見せるときには、改めて、かなりスッキリしたコードになるまで書き直している、というだけのことである(笑
ちょうど、プログラムができあがる過程というのは、絵が描かれる過程に似ている。
展覧会に出品される作品の制作も、最初は、デッサンからはじまる。
デッサンのときには、ムダな線や、失敗の線、結果的に遠回りになってしまうような線も描かれる。
しかし、その過程を通じて、本当に描きたいラインを表現した、求める真の線が見つかるのだ。
はっきり言えば、「ネットに転がっているサンプルを、上からなぞってマネして書いていく」というのは、
「ゴッホのひまわり」をマネしようとして、ひまわりや花瓶からではなく、左上の背景から順番にマネしていくようなもの
である。
賢明なあなたならば、「そんなことをいくらくり返しても、達人の域に到達できない」ということは分かるだろう。
この一連の記事から、そんな感覚をつかんでもらいたい。
そして同時に、あなたのアタマの中から、「マクロなんて、アタマがよくて、特別な訓練を受けた人しか書けないものなんだ」という錯覚を外してもらいたい、というわけだ。
以下、僕が想定している読者層に比して内容的に若干レベルが高いので、個々のプログラムで各行が言わんとしていることについては細かく触れない。
ここでは、全体の流れだけを感じて欲しい。
さっそく、行ってみよう。
実は、前回紹介したマクロを書くよりだいぶ前に、やはり、画像の扱いに困ってマクロを書いたことがあった。
iPhoneからツールを使って吸い上げた写真が、作成日ごとに作られた 「2010 04 05」 みたいな名称のフォルダに格納されてしまい、そのフォルダが百を超えるような状態になってしまったのだ。
それで、それらを同じフォルダに移動させるツールを作った。
具体的には、あるフォルダのサブフォルダすべてを見にいって、「写真.jpg」というファイルと、「.mov」という拡張子のファイル以外は、「gather」という名のサブフォルダに移動させるようにした。
それが、以下。
以下、Microsoft Scripting Runtime への参照設定をしてから動かしてください。
あと、所定のフォルダはあらかじめ作っておくこと等々いろいろあるが、面倒なのと、今回イイタイコトと直接関係ないので、解説省略(笑
Sub fuga()
Dim fs As Scripting.FileSystemObject
Dim basefolder As Scripting.folder ‘調査対象のフォルダ
Dim mySubFolders As Scripting.Folders ‘調査対象のフォルダ内のすべてのフォルダ
Dim mySubFiles As Scripting.Files ‘調査対象のフォルダ内のすべてのファイル
Dim mySubFolder As Scripting.folder
Dim mySubFile As Scripting.File
Set fs = New Scripting.FileSystemObject
Set basefolder = fs.GetFolder(ThisWorkbook.path)
Set mySubFolders = basefolder.subfolders
For Each mySubFolder In mySubFolders
‘ Debug.Print mySubFolder.Name & ” ” & mySubFolder.DateLastModified
Set mySubFiles = mySubFolder.Files
For Each mySubFile In mySubFiles
‘ Debug.Print mySubFile.Name & ” ” & mySubFile.DateLastModified
Debug.Print mySubFile.path
If mySubFile.Name <> “写真.jpg” & InStr(LCase(mySubFile.Name), “.mov”) Then
fs.MoveFile Source:=mySubFile.path, Destination:=ThisWorkbook.path & “\gather\” & mySubFile.Name
End If
Next
Next
Set mySubFile = Nothing
Set mySubFolder = Nothing
Set mySubFiles = Nothing
Set mySubFolders = Nothing
Set basefolder = Nothing
Set fs = Nothing
End Sub
さあ、では、これを僕がどうやって作ったのかというと。
こんなの、イチから書いたりはしない。
実は、自分でやっている、エクセルマクロ・VBAの上級講座のために作ったテキストのサンプルコードからパクってきた(笑
最初にパクってきたモトネタのが、これ↓。あるフォルダにあるサブフォルダのリスト、ファイルのリストを出力する。
Sub GetSubFolders()
Dim fs As Scripting.FileSystemObject
Dim basefolder As Scripting.folder ‘調査対象のフォルダ
Dim mySubFolders As Scripting.Folders ‘調査対象のフォルダ内のすべてのフォルダ
Dim mySubFiles As Scripting.Files ‘調査対象のフォルダ内のすべてのファイル
Dim mySubFolder As Scripting.folder
Dim mySubFile As Scripting.File
Set fs = New Scripting.FileSystemObject
Set basefolder = fs.GetFolder(ThisWorkbook.path & “\fsosample”)
Set mySubFolders = basefolder.subfolders
For Each mySubFolder In mySubFolders
Debug.Print mySubFolder.Name & ” ” & mySubFolder.DateLastModified
Next
Set mySubFiles = basefolder.Files ‘[1]
For Each mySubFile In mySubFiles
Debug.Print mySubFile.Name & ” ” & mySubFile.DateLastModified
Next ‘[2]
Set mySubFile = Nothing
Set mySubFolder = Nothing
Set mySubFiles = Nothing
Set mySubFolders = Nothing
Set basefolder = Nothing
Set fs = Nothing
End Sub
そして、まずは、これを加工して、すべてのサブフォルダ内のファイルのリストを出力する、という趣旨のマクロを作った。
こんな感じ↓
Sub fuga()
Dim fs As Scripting.FileSystemObject
Dim basefolder As Scripting.folder ‘調査対象のフォルダ
Dim mySubFolders As Scripting.Folders ‘調査対象のフォルダ内のすべてのフォルダ
Dim mySubFiles As Scripting.Files ‘調査対象のフォルダ内のすべてのファイル
Dim mySubFolder As Scripting.folder
Dim mySubFile As Scripting.File
Set fs = New Scripting.FileSystemObject
Set basefolder = fs.GetFolder(ThisWorkbook.path)
Set mySubFolders = basefolder.subfolders
For Each mySubFolder In mySubFolders
Debug.Print mySubFolder.Name & ” ” & mySubFolder.DateLastModified
Set mySubFiles = mySubFolder.Files ‘[3]
For Each mySubFile In mySubFiles
Debug.Print mySubFile.Name & ” ” & mySubFile.DateLastModified
Debug.Print mySubFile.path
Next ‘[4]
Next
Set mySubFile = Nothing
Set mySubFolder = Nothing
Set mySubFiles = Nothing
Set mySubFolders = Nothing
Set basefolder = Nothing
Set fs = Nothing
End Sub
まずは、サブプロシージャの名前に注目して欲しい。「fuga」なんて。ムチャムチャテキトーである(笑
そして、このとき、僕がどういう気分であったかを想像して欲しい。
そう。「テキトー」な気分で書いているのである。
何がどうなるかなんて、よく見えていない。厳粛な手続きを取る気もない。荘厳な作品を作るつもりもない。
直前で紹介した、[1]~[2]の部分を、[3]~[4]の部分に移動させて、ちょっと改変しただけ。
ついでに、 Debug.Print を使って、ここまでの目的どおりに動いているかを確認してみた。
うまく動いたので、もうちょっと改変。移動したいファイルを見つけたら移動させるようにしてみた。
Sub fuga()
Dim fs As Scripting.FileSystemObject
Dim basefolder As Scripting.folder ‘調査対象のフォルダ
Dim mySubFolders As Scripting.Folders ‘調査対象のフォルダ内のすべてのフォルダ
Dim mySubFiles As Scripting.Files ‘調査対象のフォルダ内のすべてのファイル
Dim mySubFolder As Scripting.folder
Dim mySubFile As Scripting.File
Set fs = New Scripting.FileSystemObject
Set basefolder = fs.GetFolder(ThisWorkbook.path)
Set mySubFolders = basefolder.subfolders
For Each mySubFolder In mySubFolders
‘ Debug.Print mySubFolder.Name & ” ” & mySubFolder.DateLastModified
Set mySubFiles = mySubFolder.Files
For Each mySubFile In mySubFiles
‘ Debug.Print mySubFile.Name & ” ” & mySubFile.DateLastModified
Debug.Print mySubFile.path
If mySubFile.Name <> “写真.jpg” & InStr(LCase(mySubFile.Name), “.mov”) Then ‘[5]
fs.MoveFile Source:=mySubFile.path, Destination:=ThisWorkbook.path & “\gather\” & mySubFile.Name
End If ‘[6]
Next
Next
Set mySubFile = Nothing
Set mySubFolder = Nothing
Set mySubFiles = Nothing
Set mySubFolders = Nothing
Set basefolder = Nothing
Set fs = Nothing
End Sub
[5]~[6]の部分が新しいが。
これも、If文はともかく、その中の、実際にファイルを移動させる部分は、自分の過去に書いたマクロ(というか、上級編講座用に書いたテキスト)からパクってきて、テキトーに書き換えただけ。
このように、マトモに動くプログラムが仕上がる過程では、プログラムは、上から順番に書かれていくのではなく、得てして、
- ざっくりした全体像が描かれ
- あとからあとから、中の部分に加筆がされていく
というステップを踏むのである。
あと、自分が過去に書いた遺産をそのまま活かして再利用することもしょっちゅうだ。
別に、悪いことではない。
あなたも、毎度毎度同じ書式の報告書とか請求書とかを書いたことがあるならそれと同じことをしている。
そのとき、イチイチ厳粛な気分にはならないだろう。
そもそも、こういう過程で作られたのだ、という認識もないまま「洗練された完成型」という雰囲気のサンプルコードを見ていても、絶対にマクロを書けるようにはならない。
「自分にはとてもできるとは思えない」と思う人の世界観と、「こんなのお茶の子再々」と思っている人の世界観は、根本的に異なるのだ。
だから、もしもあなたが「自分にはマクロなんてとても書けるとは思えない」と思っているならば、あなたがするべきことは、まずは、世界観を変えることだ。
実践的なノウハウは、それからついてくる。