3日がかりのその仕事、3分で終わらせる方法教えます!
パソコンスキルの心技体

String関数は便利だぞ。

2012年2月22日
  • このエントリーをはてなブックマークに追加
  • follow us in feedly

達人養成塾 小川です。

いろいろコンテンツ作りとかに没頭しているうちに、ブログの更新か、3日空いてしまいました(汗

続きモノを書いていたのに… (・ω・)

..てか。

そもそも、「僕には、ブログで続きモノを書くなんて、無理なんじゃないか?」かも思いつつ。

今日は、ボチボチ言ってみます。

以下のシリーズです。

不要な重複ファイルは自動削除するツールをVBAで自製してみた – Excel マクロ・VBA
サブフォルダすべての中身を調べ、条件に合うファイルを移動させる – Excelマクロ・VBA
.jpgファイル、.pngファイルを検出して移動させるマクロ – Excel VBA

最終形は、.jpg, .png ファイルで、

IMG_[num]-1.jpg とか、
IMG_[num]-2.jpg とか、
IMG_[num]-3.jpg とか、

そういう名称の複製物を見つけて、削除したい。

そんなニーズに合うマクロを作りました。
(とはいえ、このサンプルでは、いきなり削除するのもなんとなく怖いので、削除予定フォルダに移動させるだけですが)

最終的に、見栄えのよい、ネットに晒してもまあいいかな、と思えるくらいまで洗練させたマクロは、こんな感じ↓。
(動作確認をしたい場合は、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

でも、最初に殴り書きしたものは、こんな感じ↓だったのですね。

(以下、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

で、世の人は、人に見せる前に、これをいろいろ見栄え良く修正していくわけです。

以下の状態にまでしてみました。

Sub mycnt()
    Dim c As Long
    For c = 1 To 10
        hoge c
    Next
End Sub
Sub hoge(cnt As Long)
    Dim fs As Scripting.FileSystemObject
    Dim f1 As String, f2 As String
    Dim s As String
    Dim c As Long
    Set fs = New Scripting.FileSystemObject
    
    For c = 1 To 1909
        If c < 10 Then
            s = “000” & c
        ElseIf c < 100 Then
            s = “00” & c
        ElseIf c < 1000 Then
            s = “0” & c
        Else
            s = c
        End If
        f1 = ThisWorkbook.path & “\IMG_” & s & “.jpg”
        f2 = ThisWorkbook.path & “\IMG_” & s & “-” & cnt & “.jpg”
‘        If fs.FileExists(filespec:=f1) And fs.FileExists(filespec:=f2) Then
        If fs.FileExists(filespec:=f2) Then
            fs.MoveFile Source:=f2, Destination:=ThisWorkbook.path & “\del\IMG_” & s & “-” & cnt & “.jpg”
            Debug.Print “exists” & vbTab & s
        End If
        f1 = ThisWorkbook.path & “\IMG_” & s & “.png”
        f2 = ThisWorkbook.path & “\IMG_” & s & “-” & cnt & “.png”
‘        If fs.FileExists(filespec:=f1) And fs.FileExists(filespec:=f2) Then
        If fs.FileExists(filespec:=f2) And Not fs.FileExists(ThisWorkbook.path & “\del\IMG_” & s & “-” & cnt & “.png”) Then
            fs.MoveFile Source:=f2, Destination:=ThisWorkbook.path & “\del\IMG_” & s & “-” & cnt & “.png”
            Debug.Print “exists” & vbTab & s
        End If
    Next
    
    Set fs = Nothing
End Sub

で、今日は、その続きです。

さてさて、で、次は、上記のサブプロシージャ「hoge」の中で、.jpg, .png に対して、まったく同じ処理をしています。
「だったら、この部分をサブルーチンとみなしてしまいたい」とか、いくつか変えてみたい箇所があります。

ということで、以下のようにします。

Sub mycnt()
    Dim c As Long
    For c = 1 To 10
        myMove c, “.jpg”
        myMove c, “.png”
    Next
End Sub
Sub myMove(cnt As Long, ext As String)
    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
    
    For c = 1 To 1909
        s = String(4 – Len(CStr(c)), “0”) & c ‘[1]
‘        If c < 10 Then
‘            s = “000” & c
‘        ElseIf c < 100 Then
‘            s = “00” & c
‘        ElseIf c < 1000 Then
‘            s = “0” & c
‘        Else
‘            s = c
‘        End If
        
        Debug.Print s
        
        fBase = ThisWorkbook.path & “\IMG_” & s
        fname(0) = fBase & ext
        fname(1) = fBase & “-” & cnt & ext
        fname(2) = ThisWorkbook.path & “\del\IMG_” & s & “-” & cnt & ext
        If fs.FileExists(filespec:=fname(1)) Then
            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
                Debug.Print “exists” & vbTab & s
                fs.MoveFile Source:=fname(1), Destination:=fname(2)
            End If
        End If
    Next
    
    Set fs = Nothing
End Sub

まず、メインルーチンのほうで、拡張子も指定するように。
呼び出し先のサブルーチンも、名前を「hoge」から、「myMove」という、それでもいい加減な名前ではあるが、少しは指向性の感じられるものに変更。

あ、あと、[1]で、長さ4桁の数字、すなわち、「0003」とか「0653」とかを作るのを、String関数を使った方法に変更しました。
この関数は、マイナーかも。というか、僕のセミナーでも教えていません。

「マクロ覚えたてのころは、なるべく少ない道具でやれるように」というのが、IBM時代にある先輩から教わって以来の僕の基本方針なんで。
(どういうことか?は、今度書きますね)

そして、

比較元となる、「IMG_[num].jpg」という名前のファイルのフルパス
削除候補の「IMG_[num]_1.jpg」という類の名前のファイルのフルパス
削除対象が移動する先となるフルパス

の3つを、配列に格納することにしました。

さてさて、そんなわけで、いよいよ最後。

この状態のものだと、例えなかったとしても、

IMG_0001-1.jpg
IMG_0001-2.jpg
IMG_0001-3.jpg
IMG_0001-4.jpg
IMG_0001-5.jpg

と、

IMG_0001-10.jpg

までのすべてのファイル名称のものを探します。

それでは全体として、処理回数が多すぎる。

てことで、書き直します。

例えば、

IMG_0001-2.jpg

について処理したあと、

IMG_0001-3.jpg

がなかった場合は、すぐに

IMG_0002-1.jpg

を探しにいく、という具合にする。

メインルーチンにあったFor Next構文を、撤廃。

サブルーチンのFor Nextの中に、Do Loop構文を放り込みます。
(Do Loopは、ループに入った段階では何回くり返すか不明なときに使う構文)

Sub mydel()
    Debug.Print “start”
    
    Dim c As Integer
    c = CInt(InputBox(“set max num”)) ‘[1]
    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)) ‘[2-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 ‘[2-2]
    Next
    
    For c = LBound(fl) To UBound(fl) ‘[3-1]
        Set fl(c) = Nothing
    Next ‘[3-2]
    Set fs = Nothing
End Sub

↑で、[2-1] ~ [2-2] が、その、Do Loopにした部分。
Do Loopでは、ループに入る前に自分で必要な初期設定をして、ループの中で、自分でカウンター変数の値を変更しなくてはなりません。
そこが For Next 構文とは異なるので注意。

あと、ちょっとカッコつけて、マクロ実行時に、ダイアログで、ファイル名何番までを調査対象としたいのか聞くようにしました。

もちろん、人に見せるんじゃなければ、決してこんなことはしない。ソースの中に直に数値を書き込んで終わりです(笑

あと、[3]で、思い出したように、Scripting.File型で宣言した変数を初期化することにした(笑

..ということで、ようやく、当初お見せしたマクロに至ります。

くり返すが、初心者が世の中に出回っているカッコいいソースコードを上から順になぞっていっても、決して、プログラミングは上達しません。

簡単なところから、ゴチャゴチャと改変していく個々の過程にこそ、学んでもらいたいものがあります。

今回紹介したマクロは初心者には元よりレベルが高すぎるのでソースの中身そのものは理解できないとは思うが、その思想からはいろいろ吸収していただければ、と思っています。

あと、細かいテクニック的には。今回の見どころは…。

  • String関数を使って、一定長さの文字列を生成する
  • Inputboxの活用法
  • Do Loop構文の活用法

てとこでしょうか。

ではでは。

キーワード

コメント

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

最新の記事

人気記事

最新記事

カテゴリ

最新コメント

タグクラウド