質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.35%
VBA

VBAはオブジェクト指向プログラミング言語のひとつで、マクロを作成によりExcelなどのOffice業務を自動化することができます。

Q&A

解決済

5回答

4844閲覧

エクセルでファイルリストの作成VBAをもっと高速にする方法を教えてください。

KANNORYUJI

総合スコア7

VBA

VBAはオブジェクト指向プログラミング言語のひとつで、マクロを作成によりExcelなどのOffice業務を自動化することができます。

0グッド

0クリップ

投稿2020/10/10 03:09

編集2020/10/10 03:52
Sub FolderScript() Dim strPath As String, i As Long strPath = Range("B2").Value Range("B3").Select i = 3 Fileshow strPath, i End Sub Public Sub Fileshow(strPath, i) Dim objFso As Object, objFolder As Object, objFile As Object Dim objSub As Object, ws As Worksheet Dim strList() As String Dim rr As Range Dim bb Dim y As Integer Dim col As Integer Dim oFile As Object Set ws = ThisWorkbook.Worksheets("FileList") Set objFso = CreateObject("scripting.Filesystemobject") Set objFolder = objFso.GetFolder(strPath) Application.ScreenUpdating = False For Each objFile In objFolder.Files ws.Cells(i, 2) = objFso.GetBaseName(objFile.Path) ws.Cells(i, 2).Select ActiveSheet.Hyperlinks.Add Anchor:=Selection, Address:=objFile.Path, TextToDisplay:=objFile.Name ws.Cells(i, 3) = objFile.Type ws.Cells(i, 4) = Int(objFile.Size / 1024) ws.Cells(i, 5) = objFile.DateCreated ws.Cells(i, 6) = objFile.DatelastAccessed ws.Cells(i, 7) = objFile.DateLastModified ws.Cells(i, 8) = objFile.ParentFolder.Path For Each rr In ws.Cells(i, 8).Rows bb = Split(rr, "\") col = 9 For y = 1 To UBound(bb) Cells(rr.Row, col) = bb(y) col = col + 1 Next y i = i + 1 Next rr Next For Each objSub In objFolder.SubFolders Fileshow objSub.Path, i Next End Sub コード ```Excel VBAを使ってファイルリスト作成をおこなっています。 現在の課題は、5万ファイルに及ぶ大規模なリスト作成のためにこのマクロコードを使うと リスト作成の完了までおよそ4時間くらいかかってしまうため、この所要時間を2時間くらいまで短縮化したいと思っています。 要望 ファイルリストの作成VBAをもっと高速にする方法を教えてください。 よろしくアドバイスをお願いいたします。 Sub FolderScript() Dim strPath As String, i As Long strPath = Range("B2").Value Range("B3").Select i = 3 Fileshow strPath, i End Sub Public Sub Fileshow(strPath, i) Dim objFso As Object, objFolder As Object, objFile As Object Dim objSub As Object, ws As Worksheet Dim strList() As String Dim rr As Range Dim bb Dim y As Integer Dim col As Integer Dim oFile As Object Set ws = ThisWorkbook.Worksheets("FileList") Set objFso = CreateObject("scripting.Filesystemobject") Set objFolder = objFso.GetFolder(strPath) Application.ScreenUpdating = False For Each objFile In objFolder.Files ws.Cells(i, 2) = objFso.GetBaseName(objFile.Path) ws.Cells(i, 2).Select ActiveSheet.Hyperlinks.Add Anchor:=Selection, Address:=objFile.Path, TextToDisplay:=objFile.Name ws.Cells(i, 3) = objFile.Type ws.Cells(i, 4) = Int(objFile.Size / 1024) ws.Cells(i, 5) = objFile.DateCreated ws.Cells(i, 6) = objFile.DatelastAccessed ws.Cells(i, 7) = objFile.DateLastModified ws.Cells(i, 8) = objFile.ParentFolder.Path For Each rr In ws.Cells(i, 8).Rows bb = Split(rr, "\") col = 9 For y = 1 To UBound(bb) Cells(rr.Row, col) = bb(y) col = col + 1 Next y i = i + 1 Next rr Next For Each objSub In objFolder.SubFolders Fileshow objSub.Path, i Next End Sub

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

hatena19

2020/10/10 03:19

コードはマークダウンのコードブロックに入れてください。 ```vba ここにコードを記述 ```
ELBE

2020/10/10 05:22

セル書き込みとセル選択がループ1回毎に約10回行われています。 描画をオフにしているとはいえ、5万回×10回のセル書き込みとセル選択が行われていることが遅い原因です。 仮に1回のセル書き込みが20msであったとしても、50万回も行われれば3時間はかかりますね。
KANNORYUJI

2020/10/10 05:31

ご指摘ありがとうございます。そのとうりです 例えば全ての情報を取得後にセル書き込みを1回だけで済ますコードの案があればもっと早くなるかと思うのですが、もし方策がありましたら是非アドバイス願います。
hihijiji

2020/10/10 08:21

一度CSVに書き込んで、そのCSVをExcelにインポートするのは如何でしょう?
guest

回答5

0

自分が作ったExcelアドインで実行した結果

ローカルHDDで
180,000ファイルで約90秒でした
(PCスペックはそれほど良いものではありません。)

工夫点は

  • ファイル情報はAPIをメインに使用

ちょっと言葉が足りなかったですね。最近は「API」と書くと「Web API」と思われてしますのですが
ここでは「Windows API」事です。
質問者様が今回使われた"scripting.Filesystemobject"のさらに下の階層にある命令で行うので
余計な処理が含まれない為処理を早くすることが出来ます。
(これは他の回答者様がすでに指摘されている内容です)

  • クラス化

これは単純にデータの持ち方の為に用意しました。(階層構造の保存の為)

  • 再帰呼び出しを使用

自関数をさらに内部でパラメータを変えて自関数を呼び出す方法です。
今回はフォルダが階層になっているので判りやすいかと思います。

  • ファイル情報はまとめて最後に出力

(これも他の回答者様が最後一括でvalueに設定すればよいと、すでに指摘されている内容です)

でしょうか?

投稿2020/10/10 09:33

編集2020/10/10 10:04
kuma_kuma_

総合スコア2506

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

KANNORYUJI

2020/10/10 09:37

180,000ファイルで約90秒は超早いです! 詳しく教えてください
KANNORYUJI

2020/10/10 09:38

ファイル情報はAPIをメインに使用 から理解できておりません ご教授をお願いいたします
kuma_kuma_

2020/10/10 10:05

すみませんいま別の回答準備中なので返答までお時間いただければ もう少しは追記できます。
KANNORYUJI

2020/10/10 10:15

わかりやすい補足説明をいただきありがとうございました。
guest

0

ファイルリストの作成VBAをもっと高速にする方法を教えてください。

1)セルをSelectしない。
Selectしなくても命令は書けるので、Selectしないような書き方を覚えてください。Selectすることで画面の表示を変えるとか、無駄なことが行われるので、時間がかかります。やめましょう。

2)個別のセルの読み書きをすることをやめる
個別のセルを読み書きすることも時間がかかります。
まとめてセル範囲でできることはまとめてセル範囲で実行しましょう。
つまり値の加工は変数上でやりきってから、シート上に書き込みます。

3)FSOは割と遅い
VBAでファイルの一覧を取得する方法はいくつかあります。
FSO>Dir関数>Dirコマンド の順で速かった気がします。
ただし、FSOの利便性も捨てがたいので適材適所で道具を使い分けましょう。

ExcelVBA

1Option Explicit 2 3Sub test() 4 Dim v 5 Dim t 6 7 t = Timer 8 9 v = GetFileList("D:\現場") 'ファイルの一覧を一次元配列で取得 10 v = GetFilesInfo(v) 'ファイルの情報を付加して二次元配列に展開 11 12 With ActiveSheet.Range("C3").Resize(UBound(v, 1), UBound(v, 2)) 13 .Value = v '変数の値をシート上に書き込み 14 .Columns(0).Formula = "=HYPERLINK(C3,D3)" '数式でハイパーリンクを設定 15 End With 16 17 MsgBox Timer - t 18End Sub 19 20Function GetFileList(ByVal SEARCH_DIR As String) As Variant 21 Dim tmpFile As String 22 Dim strCmd As String 23 Dim buf() As Byte 24 Dim FileList() As String 25 26 'Dirコマンドの結果を出力する一時ファイル 27 tmpFile = Environ("TEMP") & "\Dir.tmp" 28 29 'Dirコマンド用の文字列を編集 30 strCmd = "Dir """ & SEARCH_DIR & """ /b/s/a:-d-h-s > """ & tmpFile & """" 31 32 'WSHでDirコマンドを実行 33 With CreateObject("Wscript.Shell") 34 .Run "cmd /c" & strCmd, 7, True 35 End With 36 37 '該当ファイルの存在チェック 38 If FileLen(tmpFile) < 1 Then 39 MsgBox "該当するファイルがありません" 40 Exit Function 41 End If 42 43 'Dirコマンドの結果を出力した一時ファイルを読み込み 44 Open tmpFile For Binary As #1 45 ReDim buf(1 To LOF(1)) 46 Get #1, , buf 47 Close #1 48 Kill tmpFile 49 50 GetFileList = Split(StrConv(buf, vbUnicode), vbCrLf) 51End Function 52 53Function GetFilesInfo(ByVal vrtList As Variant) As Variant 54 Dim FSO As New FileSystemObject 55 Dim f As File 56 Dim i As Long 57 Dim v() As Variant 58 ReDim v(1 To UBound(vrtList) + 1, 1 To 7) 59 60 For i = LBound(v, 1) To UBound(v, 1) 61 Set f = Nothing 62 On Error Resume Next 63 Set f = FSO.GetFile(vrtList(i - 1)) 64 On Error GoTo 0 65 66 If Not f Is Nothing Then 67 v(i, 1) = f.Path 68 v(i, 2) = FSO.GetBaseName(f) 69 v(i, 3) = f.Type 70 v(i, 4) = f.Size 71 v(i, 5) = f.DateLastAccessed 72 v(i, 6) = f.DateLastModified 73 v(i, 7) = f.ParentFolder.Path 74 End If 75 Next 76 77 GetFilesInfo = v 78End Function

ハイパーリンクも個別に設定すると、5万回、セルに設定しないといけないので、一括でできる数式でやってみました。(数式は一括で設定できる)
それを、一括で普通のハイパーリンクにもできそうですが手順が面倒なので、
今回は数式を入力するまでとしてます。
興味があればネットで調べてみてください。
つまり、今回のマクロは、
一括で数式を入力するために色々やっています。

2800個のファイルで
5秒弱だったので数分でできると思います。
Dirコマンドの使い方をよくわかってないので、
リストが上手く作れてないかもです。
動作確認を十分にし、自己責任でお使いください。

投稿2020/10/10 06:56

mattuwan

総合スコア2163

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

KANNORYUJI

2020/10/10 07:05

ありがとうございます早速試しにやってみます
guest

0

シート(FileList)の中にもし、関数式が埋め込まれているなら、以下の対策が有効になります。
Sub FolderScript()のFileshow strPath, iの前で、
自動再計算、画面表示をOFFします。
そのあとで、自動再計算、画面表示をもとに戻します。
尚、そのようにした場合、
Public Sub Fileshow(strPath, i)内の
Application.ScreenUpdating = False
は不要ですので削除してください。

VBA

1Sub FolderScript() 2 3 Dim strPath As String, i As Long 4 strPath = Range("B2").Value 5 6 Range("B3").Select 7 i = 3 8 Application.Calculation = xlCalculationManual 9 Application.ScreenUpdating = False 10 Fileshow strPath, i 11 Application.Calculation = xlCalculationAutomatic 12 Application.ScreenUpdating = True 13 14End Sub 15

投稿2020/10/10 06:08

tatsu99

総合スコア5493

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

KANNORYUJI

2020/10/10 07:10

大変勉強になります。 データ取り込みの効率化につながります ありがとうございます
guest

0

ベストアンサー

とりあえず無駄なSelect、無駄な代入は削除。

vba

1ws.Cells(i, 2) = objFso.GetBaseName(objFile.Path) 2ws.Cells(i, 2).Select 3ActiveSheet.Hyperlinks.Add Anchor:=Selection, Address:=objFile.Path, TextToDisplay:=objFile.Name

これは下記の1行でOK。

vba

1ActiveSheet.Hyperlinks.Add Anchor:=ws.Cells(i, 2), Address:=objFile.Path, TextToDisplay:=objFile.Name

これで若干は改善されるかも。半分はたぶん無理。

セル一つずつに代入するのではなく取得したファイルデータを配列にいれて、できる限りまとめて代入すると改善するでしょう。

vba

1ws.Cells(i, 3) = objFile.Type 2ws.Cells(i, 4) = Int(objFile.Size / 1024) 3ws.Cells(i, 5) = objFile.DateCreated 4ws.Cells(i, 6) = objFile.DatelastAccessed 5ws.Cells(i, 7) = objFile.DateLastModified 6ws.Cells(i, 8) = objFile.ParentFolder.Path

の部分は、下記にすると、6回の代入が1回ですみます。

vba

1ws.Cells(i, 3).Resize(, 6).Value = _ 2 Array(objFile.Type, Int(objFile.Size / 1024), objFile.DateCreated, _ 3 objFile.DatelastAccessed, objFile.DateLastModified, objFile.ParentFolder.Path)

さらに、改善させるには、
全ての行のデータを配列にいれて一気に代入するとさらに高速化できますが、
ハイパーリンクとかは無理なので、ハイパーリンクは後から設定するとかの工夫は必要になります。


全ての行のデータを配列にいれて一気に代入する方法をぼちぼち書いていたら、既に他の方から回答がいろいろついてしまいましたね。

配列は10万行固定の無理やりのものです(;^_^A
行方向は増やせないし、縦横変換も面倒なので、、、
フォルダー単位で書き出してますので、1フォルダー内のファイルが10万を超えるとエラーになります。
手抜きです。

vba

1Sub FolderScript1() 2 Application.Calculation = xlCalculationManual 3 Application.ScreenUpdating = False 4 5 Dim StartRow As Long, ws As Worksheet 6 Set ws = ThisWorkbook.Worksheets("FileList") 7 StartRow = 3 8 9 Fileshow1 ws.Range("B2").Value, ws, StartRow 10 11 With ThisWorkbook.Worksheets("FileList").Range("B3:B" & StartRow) 12 .Formula = .Value 13 End With 14 15 Application.Calculation = xlCalculationAutomatic 16 Application.ScreenUpdating = True 17End Sub 18 19Public Sub Fileshow1(ByVal strPath As String, ws As Worksheet, ByRef StartRow As Long) 20 21 Dim objSub As Object 22 Dim strList() As String 23 24 Dim rr As Range 25 Dim bb 26 Dim y As Integer 27 Dim col As Integer 28 29 Dim objFso As Object, objFolder As Object 30 Set objFso = CreateObject("scripting.Filesystemobject") 31 Set objFolder = objFso.GetFolder(strPath) 32 33 34 Dim FileDatas() As String 35 ReDim FileDatas(1 To 10000, 1 To 8) As String 36 37 Dim i As Long 38 i = 0 39 40 Dim objFile As Object 41 For Each objFile In objFolder.Files 42 i = i + 1 43 FileDatas(i, 1) = "=HYPERLINK(""" & objFile.Path & """,""" & objFile.Name & """)" 44 FileDatas(i, 2) = objFile.Type 45 FileDatas(i, 3) = Int(objFile.Size / 1024) 46 FileDatas(i, 4) = objFile.DateCreated 47 FileDatas(i, 5) = objFile.DatelastAccessed 48 FileDatas(i, 6) = objFile.DateLastModified 49 FileDatas(i, 7) = objFile.ParentFolder.Path 50 51 Dim aSubFolders, aSubFolder 52 aSubFolders = Split(FileDatas(i, 7), "\") 53 If UBound(aSubFolders) > UBound(FileDatas, 2) - 7 Then 54 ReDim Preserve FileDatas(1 To 10000, 1 To UBound(aSubFolders) + 7) 55 End If 56 57 Dim c As Long 58 For c = 1 To UBound(aSubFolders) 59 FileDatas(i, c + 7) = aSubFolders(c) 60 Next 61 Next 62 63 64 If i > 0 Then ws.Cells(StartRow, 2).Resize(i, UBound(FileDatas, 2)).Value = FileDatas 65 66 StartRow = StartRow + i 67 68 For Each objSub In objFolder.SubFolders 69 Fileshow1 objSub.Path, ws, StartRow 70 Next 71 72End Sub

投稿2020/10/10 05:38

編集2020/10/10 08:34
hatena19

総合スコア34075

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

こんな感じでどうでしょう?
・何はともあれ配列化
・配列でハイパーリンクを扱うため、HYPERLINK関数を利用
・2次元配列は2次元目しか拡張できないので、1次元目を大きくとる(ただの力技)
・Functionの最後におまじないとしてDoEvents入れてますが、フォルダーの数が多いとDoEventsの回数も増えるので、処理が遅くなります

わたしの環境ですが、所要時間は100000行(到達した時点で強制的にExit)で一時間程度でした。

VBA

1Sub FolderScript() 2 3Dim strPath As String, i As Long 4Dim filelist() As String 5 strPath = Range("B2").Value 6 7 Range("B3").Select 8 i = 0 9 10 outlist = Fileshow(strPath, i, filelist, start) 11 12ReDim output(UBound(outlist, 2), 50) 13 14'配列を縦横入れ替え 15 For n = 0 To UBound(outlist, 1) 16 For i = 0 To UBound(outlist, 2) 17 output(i, n) = outlist(n, i) 18 Next 19 Next 20 21 With ThisWorkbook.Worksheets("FileList") 22 .Range(.Cells(3, 2), .Cells(3 + i, 2 + n)) = output 23 End With 24 25 With Application 26 .ScreenUpdating = True 27 .Calculation = True 28 End With 29 30End Sub 31 32Public Function Fileshow(strPath, i, filelist() As String) 33Dim objFso As Object, objFolder As Object, objFile As Object 34Dim objSub As Object, ws As Worksheet 35Dim strList() As String 36 37Dim rr As Range 38Dim bb 39Dim y As Integer 40Dim col As Integer 41 42Dim oFile As Object 43 44Set ws = ThisWorkbook.Worksheets("FileList") 45Set objFso = CreateObject("scripting.Filesystemobject") 46Set objFolder = objFso.GetFolder(strPath) 47 48With Application 49 .ScreenUpdating = False 50 .Calculation = False 51End With 52For Each objFile In objFolder.Files 53 54 ReDim Preserve filelist(50, i) 'お好みの大きさ 55                   56 'Application.StatusBar = i 57 58'配列に格納するため、HYPERLINK関数で入力 59 filelist(0, i) = "=HYPERLINK(""" & objFile.Path & """,""" & objFso.GetBaseName(objFile.Path) & """)" 60 61 filelist(1, i) = objFile.Type 62 filelist(2, i) = Int(objFile.Size / 1024) 63 filelist(3, i) = objFile.DateCreated 64 filelist(4, i) = objFile.DatelastAccessed 65 filelist(5, i) = objFile.DateLastModified 66 filelist(6, i) = objFile.ParentFolder.Path 67 68 69’For Eachの理由がわからなかったので削除。必要なら復活させてください。 70 bb = Split(filelist(6, i), "\") 71 72 col = 7 73 For y = 1 To UBound(bb) 74 filelist(col, i) = bb(y) 75 col = col + 1 76 Next y 77 78 i = i + 1 79 80Next 81 82For Each objSub In objFolder.SubFolders 83 Fileshow objSub.Path, i, filelist 84Next 85 86DoEvents 87Fileshow = filelist 88End Function

投稿2020/10/10 06:45

編集2020/10/10 06:50
Usirow

総合スコア364

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

KANNORYUJI

2020/10/10 09:24

アドバイスをありがとうございます 早速試してみましたが Fileshowでどういう訳か ”引数がありません または不正なパラメータです” と返ってきて止まってしまいました。 原因を調査中です
Usirow

2020/10/10 13:05

あ、すみません。最初にFileShowを呼び出す outlist = Fileshow(strPath, i, filelist, start) を outlist = Fileshow(strPath, i, filelist) に変えてください。時間測るために入れておいた変数の削除を忘れていました……
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.35%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問