ファイル一覧解析マクロ
2020年02月10日
今回は、フォルダ内のファイルの一覧をエクセルシートにするマクロ。
過去にファイル解析マクロをファイルシステムオブジェクトやDIR関数を使って
作ったものの、windowsのコマンドプロンプトで出せることを知った。
きっかけはこのページ。
https://www.excel-chunchun.com/entry/2019/01/03/184325
これに習って、DIR関数で作ってみたものの、
記載のとおり、環境依存文字がパスに含まれるとうまくいかない。
パート2でコマンドプロンプトを紹介していて、
ならそれでいいじゃんと思った次第。
ただ、コマンドプロンプトの結果をファイル出力しても
非常に使いづらいので出力結果ファイルを食って
結果をシートに吐き出すマクロを作った。
まずはコマンドプロンプトを起動。
WIndows+Rでウィンドウを開いて、cmdと入力してエンター。
次にコマンドを入力。
> DIR "フォルダパス" /A-D /S /B > "ファイル名"
これで、指定したフォルダ内のサブフォルダ(/S)を含めて、
フォルダ以外(/A-D)の情報を指定したファイル名(フルパス)に出力。
/B指定で、フルパスだけ出力してくれる。
・
・
/Bありの場合
・
・
最初は/Bありの想定でマクロを作ってみたものの、
フルパスから更新日時やファイルサイズを取得するところでうまくいかず断念。
/Bなしは、必要な情報がすべてそろっているし、
「のディレクトリ」を含む行ならば、左辺を変数に格納するだけなので、意外と簡単に。
それが完成してから/Bありを見直したところ、
ファイルパスの最終文字に制御文字かなにかがはいっていたことが原因と判明。
両方のソースを完成できた。
ちなみに
/Bありは4225ファイルを平均3秒で出力
/Bなしは4224ファイルを平均1秒で出力
/Bなしは不要な情報がファイルにたくさん入っているものの、
/Bありのファイルから情報を取り出す処理のほうが当然重い結果でした。
過去にファイル解析マクロをファイルシステムオブジェクトやDIR関数を使って
作ったものの、windowsのコマンドプロンプトで出せることを知った。
きっかけはこのページ。
https://www.excel-chunchun.com/entry/2019/01/03/184325
これに習って、DIR関数で作ってみたものの、
記載のとおり、環境依存文字がパスに含まれるとうまくいかない。
パート2でコマンドプロンプトを紹介していて、
ならそれでいいじゃんと思った次第。
ただ、コマンドプロンプトの結果をファイル出力しても
非常に使いづらいので出力結果ファイルを食って
結果をシートに吐き出すマクロを作った。
まずはコマンドプロンプトを起動。
WIndows+Rでウィンドウを開いて、cmdと入力してエンター。
次にコマンドを入力。
> DIR "フォルダパス" /A-D /S /B > "ファイル名"
これで、指定したフォルダ内のサブフォルダ(/S)を含めて、
フォルダ以外(/A-D)の情報を指定したファイル名(フルパス)に出力。
/B指定で、フルパスだけ出力してくれる。
/Bなしの場合
ドライブ D のボリューム ラベルがありません。
ボリューム シリアル番号は C423-225F です
D:\Dropbox のディレクトリ
2015/03/11 22:08 10 .dropbox
2016/08/11 01:32 9,681 .picasa.ini
2014/03/29 00:10 6,945 0132777_20140329000950.csv
・・
・
/Bありの場合
D:\Dropbox\.dropbox
D:\Dropbox\.picasa.ini
D:\Dropbox\0132777_20140329000950.csv
・・
・
最初は/Bありの想定でマクロを作ってみたものの、
フルパスから更新日時やファイルサイズを取得するところでうまくいかず断念。
/Bなしは、必要な情報がすべてそろっているし、
「のディレクトリ」を含む行ならば、左辺を変数に格納するだけなので、意外と簡単に。
それが完成してから/Bありを見直したところ、
ファイルパスの最終文字に制御文字かなにかがはいっていたことが原因と判明。
両方のソースを完成できた。
ちなみに
/Bありは4225ファイルを平均3秒で出力
/Bなしは4224ファイルを平均1秒で出力
/Bなしは不要な情報がファイルにたくさん入っているものの、
/Bありのファイルから情報を取り出す処理のほうが当然重い結果でした。
Option Explicit
Sub ◆dir_ADSの解析()
' dir "D:\Dropbox\VBA" /s /a-d > "D:\Dropbox\VBA\dirout.txt"
Dim fPath As String: fPath = txtファイル名取得("DIR /A-D /Sの解析結果ファイルの選択")
If fPath = "" Then Exit Sub
Dim tictoc As Double
tictoc = Timer
'Debug.Print "[" & Now & "] "; Format(Timer - tictoc, "0.00秒")
Dim Dic: Set Dic = CreateObject("Scripting.Dictionary")
Dim buf As String, Path As String
Dim strDate As String, fSize As String, fname As String, Ext As String
'タイトル行作成
Dic.Add "FulName", "Folder" & "‡" & "File" & "‡" & "Date" & "‡" & "Size" & "‡" & "Ext"
Open fPath For Input As #1
Do Until EOF(1)
Line Input #1, buf
If Right(buf, Len("のディレクトリ")) = "のディレクトリ" Then
Path = Trim(Replace(buf, "のディレクトリ", ""))
ElseIf Mid(buf, 5, 1) = "/" And Mid(buf, 8, 1) = "/" Then
strDate = Replace(Trim(Mid(buf, 1, 17)), " ", " ")
fSize = Trim(Mid(buf, 19, 17))
fname = Trim(Mid(buf, 37, Len(buf) - 37 + 1))
Ext = 拡張子(fname)
Dic.Add Path & "\" & fname, AddressToLink(Path, ファイル名(Path)) & "‡" & fname & "‡" & strDate & "‡" & fSize & "‡" & Ext
End If
Loop
Close #1
Dim keys: keys = Dic.keys
Dim Items: Items = Dic.Items
Set Dic = Nothing
Dim Arr(): ReDim Arr(UBound(keys) + 1, 5)
Dim oBuf, i As Long, j As Long
For i = 0 To UBound(keys)
oBuf = Split(Items(i), "‡")
For j = 0 To UBound(oBuf)
Arr(i, j) = oBuf(j)
Next j
Next i
'出力
Worksheets.Add
Dim OutPutCell As Range: Set OutPutCell = Cells(1, 1)
Call ArrayToCell_1d_trns(OutPutCell, keys)
Call ArrayToCell_2d(OutPutCell.Offset(0, 1), Arr)
OutPutCell.Offset(1, 1).Select
ActiveWindow.FreezePanes = True 'ウィンドウ枠の固定ON
OutPutCell.AutoFilter
With ActiveSheet.AutoFilter.Range
.CurrentRegion.Borders.LineStyle = xlContinuous
End With
Dim OutPutRange As Range: Set OutPutRange = OutPutCell.Resize(UBound(keys) + 1, 1)
OutPutRange.Offset(1, 3).NumberFormatLocal = "yyyy/mm/dd hh:mm"
OutPutRange.Offset(1, 4).NumberFormatLocal = "#,##0"
Call ChangeSheetName("dirADS結果")
Cells(1, "H") = AddressToLink(fPath, fPath)
'Dim tictoc As Double
'tictoc = Timer
Debug.Print "[" & Now & "] "; Format(Timer - tictoc, "0.00秒")
End Sub
Sub ◆dirBファイルの解析()
Dim TextBuf As String: TextBuf = TextFileToBuf(txtファイル名取得("DIR /A-D /S /Bの解析結果ファイルの選択"))
If TextBuf = "" Then Exit Sub
Dim tictoc As Double
tictoc = Timer
'Debug.Print "[" & Now & "] "; Format(Timer - tictoc, "0.00秒")
Dim TextArray: TextArray = Split(TextBuf, Chr(10))
Dim TextLine As String, i As Long, cnt As Long: cnt = 1
Dim pPos As Long, yPos As Long, yPos2 As Long
Dim Arr(): ReDim Arr(UBound(TextArray), 5)
Arr(0, 0) = "テキスト"
Arr(0, 1) = "Folder"
Arr(0, 2) = "File"
Arr(0, 3) = "Date"
Arr(0, 4) = "Size"
Arr(0, 5) = "Ext"
On Error Resume Next 'ファイルがなかったりするので
For i = 1 To UBound(TextArray)
TextLine = Left(TextArray(i - 1), Len(TextArray(i - 1)) - 1)
Arr(i, 0) = TextLine ' "テキスト"
If Mid(TextLine, 2, 1) = ":" Then
pPos = InStrRev(TextLine, ".")
yPos = InStrRev(TextLine, "\")
yPos2 = InStrRev(Left(TextLine, yPos - 1), "\")
Arr(i, 1) = AddressToLink(Left(TextLine, yPos - 1), ファイル名(Left(TextLine, yPos - 1))) '"Folder"
Arr(i, 2) = Right(TextLine, Len(TextLine) - yPos) '"File"
Arr(i, 3) = FileDateTime(TextLine) ' "Date"
Arr(i, 4) = FileLen(TextLine) '"Size"
Arr(i, 5) = Right(TextLine, Len(TextLine) - pPos) '"Ext"
End If
Next i
'出力
Worksheets.Add
Call ArrayToCell_2d(Cells(1, 1), Arr)
Call ChangeSheetName("dirB結果")
Dim OutPutCell As Range: Set OutPutCell = Cells(1, 1)
OutPutCell.Offset(0, 3).NumberFormatLocal = "yyyy/mm/dd hh:mm"
OutPutCell.Offset(0, 4).NumberFormatLocal = "#,##0"
Call ArrayToCell_2d(OutPutCell, Arr)
Dim OutPutRange As Range: Set OutPutRange = OutPutCell.Resize(UBound(TextArray) + 1, 1)
OutPutRange.Offset(1, 3).NumberFormatLocal = "yyyy/mm/dd hh:mm"
OutPutRange.Offset(1, 4).NumberFormatLocal = "#,##0"
'Dim tictoc As Double
'tictoc = Timer
Debug.Print "[" & Now & "] "; Format(Timer - tictoc, "0.00秒")
End Sub
Private Sub ChangeSheetName(shName As String)
Dim flag As Boolean
If UsedSheetName(shName) = False Then
ActiveSheet.Name = shName
Exit Sub
End If
Dim cnt As Long: cnt = 2
Dim NewName As String: NewName = shName & " (" & cnt & ")"
Do While UsedSheetName(NewName) = True
cnt = cnt + 1
NewName = shName & " (" & cnt & ")"
Loop
ActiveSheet.Name = NewName
End Sub
Private Function UsedSheetName(shName As String) As Boolean
Dim ws As Worksheet, flag As Boolean
For Each ws In Worksheets
If ws.Name = shName Then flag = True
Next ws
UsedSheetName = flag
End Function
Private Sub ArrayToCell_1d_trns(Target As Range, oArr) '配列貼り付け便利モジュール
Dim iRowMax: iRowMax = UBound(oArr, 1) - LBound(oArr, 1) + 1
Dim iColMax: iColMax = 1 '1列データは転置が必要
Target.Resize(iRowMax, iColMax).Value = WorksheetFunction.Transpose(oArr)
End Sub
Function AddressToLink(Address As String, Caption As String) As String
AddressToLink = "=hyperlink(""" & Address & """,""" & Caption & """)"
End Function
Function Path2Link(Path As String) As String
Path2Link = "=Hyperlink(""" & Path & """,""" & Path & """)"
End Function
Function txtファイル名取得(Optional タイトル As String = "テキストファイルの選択", Optional デフォルト As String = "")
If デフォルト = "" Then
デフォルト = デスクトップパス
End If
Dim i As Long
'’https://excel-ubara.com/excelvba1/EXCELVBA376.html
Dim FSO: Set FSO = CreateObject("Scripting.FileSystemObject")
Dim FulName As String
With Application.FileDialog(msoFileDialogFilePicker)
.InitialFileName = デフォルト
.AllowMultiSelect = False
.Filters.Clear
.Filters.Add "テキストファイル", "*.txt"
.Filters.Add "すべてのファイル", "*.*"
.Title = タイトル
If .Show = True Then
FulName = .SelectedItems(1)
Else
MsgBox "キャンセルしました"
End If
End With
Set FSO = Nothing
txtファイル名取得 = FulName
End Function
Private Function ユーザー名取得()
Dim WshNetworkObject As Object
Set WshNetworkObject = CreateObject("WScript.Network")
ユーザー名取得 = WshNetworkObject.UserName
Set WshNetworkObject = Nothing
End Function
Private Function デスクトップパス()
Dim UserName As String
UserName = ユーザー名取得
デスクトップパス = "C:\Users\" & UserName & "\Desktop"
End Function
Sub ArrayToCell_2d(Target As Range, oArr, Optional ColCnt As Long = 0) '配列貼り付け便利モジュール
Dim iRowMax: iRowMax = UBound(oArr, 1) - LBound(oArr, 1) + 1 '// 1次元目の要素数を取得 '// 二次元配列の最大行数
Dim iColMax: iColMax = UBound(oArr, 2) - LBound(oArr, 2) + 1 '// 2次元目の要素数を取得'// 二次元配列の最大列数
'// Rangeオブジェクトで開始セルから貼り付けるセル範囲を拡張する場合
If ColCnt > 0 And ColCnt <= iColMax Then
Target.Resize(iRowMax, ColCnt).Value = oArr
Else
Target.Resize(iRowMax, iColMax).Value = oArr
End If
End Sub
Function 拡張子(FileName As String) As String
Dim pPos As Long
pPos = InStrRev(FileName, ".")
If pPos <> 0 Then
拡張子 = Right(FileName, Len(FileName) - pPos)
Else
拡張子 = ""
End If
End Function
Function ファイル名(FileName As String) As String
Dim yPos As Long
yPos = InStrRev(FileName, "\")
If yPos <> 0 Then
ファイル名 = Right(FileName, Len(FileName) - yPos)
Else
ファイル名 = ""
End If
End Function
Function TextFileToBuf(FullName As String) As String
If FullName = "" Then Exit Function
Dim buf As String
With CreateObject("Scripting.FileSystemObject")
With .GetFile(FullName).OpenAsTextStream
buf = .ReadAll
.Close
End With
End With
TextFileToBuf = buf
End Function
PR
フォルダ内のCSVファイルをエクセルシートに転記
2020年02月05日
今回はCSVファイルの転記について。
携帯電話の通話料に応じて費用按分をしているのだが、
回線ごとの通話料は利用明細を見ないとわからない。
仕方なく、回線ごとにシートを分けて利用明細をコピペしていたが、
いい加減めんどくさくなったので、
回線ごちゃまぜで1つのシートにまとめて
表計算で集計することにした。
ダウンロードしたデータのファイル名に回線名の記載がない。
同じデータを2つダウンロードしていても気づかないので
Dic配列を使って重複は排除することにした。
また例によって、オフィス田中氏のHPにお世話になりました。
処理の流れは以下のとおり。
まず対象のフォルダを指定。今回は一発ものなので固定。
dir関数を使ってフォルダ内のファイルを取得。
対象ファイルがなくなるまでループ。
dir関数で取得したファイルからフルパスを取得してファイルを開く。
各行のデータをDic配列に格納
(新出なら格納、既出なら値に目印を追加)
Dicの値をA列へ、キーをB列へ出力。
キー配列から区切り文字でsplitして、出力配列に格納。
C列以降に出力配列を格納。
そんな感じで、以下ソース。
携帯電話の通話料に応じて費用按分をしているのだが、
回線ごとの通話料は利用明細を見ないとわからない。
仕方なく、回線ごとにシートを分けて利用明細をコピペしていたが、
いい加減めんどくさくなったので、
回線ごちゃまぜで1つのシートにまとめて
表計算で集計することにした。
ダウンロードしたデータのファイル名に回線名の記載がない。
同じデータを2つダウンロードしていても気づかないので
Dic配列を使って重複は排除することにした。
また例によって、オフィス田中氏のHPにお世話になりました。
'ファイルの一覧を取得
'http://officetanaka.net/excel/vba/file/file07.htm
'
'テキストファイルを読み込む
'http://officetanaka.net/excel/vba/file/file08b.htm
処理の流れは以下のとおり。
まず対象のフォルダを指定。今回は一発ものなので固定。
dir関数を使ってフォルダ内のファイルを取得。
対象ファイルがなくなるまでループ。
dir関数で取得したファイルからフルパスを取得してファイルを開く。
各行のデータをDic配列に格納
(新出なら格納、既出なら値に目印を追加)
Dicの値をA列へ、キーをB列へ出力。
キー配列から区切り文字でsplitして、出力配列に格納。
C列以降に出力配列を格納。
そんな感じで、以下ソース。
Sub フォルダ内のCSVファイルを転記()
Const Path As String = "D:\Dropbox\xlsエクセル関連\IIJ 通話ログ\ログ\"
Const EXT = "*.csv"
Dim fName As String: fName = Dir(Path & EXT)
Dim Dic, buf As String
Set Dic = CreateObject("Scripting.Dictionary")
Do While fName <> ""
Open Path & fName For Input As #1
'ファイルを開いて
Do Until EOF(1)
Line Input #1, buf
If Not Dic.exists(buf) Then
Dic.Add buf, fName '新規キーを登録 値は1
Else
Dic(buf) = Dic(buf) & "†" '既出のキーは、ファイル名に印
End If
Loop
Close #1
fName = Dir()
Loop
Dim Keys, Items
Keys = Dic.Keys
Items = Dic.Items
Dim iRowMax: iRowMax = UBound(Keys) - LBound(Keys) + 1
Cells(2, 1).Resize(iRowMax, 1).Value = WorksheetFunction.Transpose(Items) 'ファイル名
Cells(2, 2).Resize(iRowMax, 1).Value = WorksheetFunction.Transpose(Keys) 'CSVデータ(生)
Cells(1, 1) = "ファイル"
Cells(1, 2) = "CSV生データ"
Dim tmp: tmp = Split(Cells(2, 2), ",")
Dim iColMax: iColMax = UBound(tmp, 1) - LBound(tmp, 1)
Dim Arr(): ReDim Arr(iRowMax, iColMax)
Dim i As Long, j As Long
For i = 0 To UBound(Keys)
tmp = Split(Keys(i), ",")
For j = 0 To iColMax
Arr(i, j) = Replace(tmp(j), """", "")
Next j
Next i
Cells(2, 3).Resize(iRowMax, iColMax + 1).Value = Arr
End Sub
選択中のセルでオートフィルタ 他
2020年02月04日
今日はオートフィルタ。
みんな大好きオートフィルタ。
パソコンの操作は、マウスよりキーボードで処理したい派なので、
エクセルのオートフィルタを使うときはカーソル動かすのがめんどくさい。
なので、よく利用するオートフィルタ操作をマクロにして、
ショートカットキー登録をすると便利。
一番使う操作は、選択セルの内容でオートフィルタをする。
コマンドはめっちゃ簡単で、こんだけ。
myRange.AutoFilter field:=fColNo, Criteria1:=selection.text
myRangeはオートフィルタのアドレス。
fieldはオートフィルタ範囲で何列目で絞り込むか。
Criteria1は絞り込む内容。
オートフィルタの範囲、絞込みの有無は 、以下で取得できる。(Office TANAKAより)
http://officetanaka.net/excel/vba/tips/tips129.htm
オートフィルタ範囲:ActiveSheet.AutoFilter.Range
2023/11/5 追記・修正 列幅が狭くて#####と表示されていてもオートフィルタができるよう ソースコード内のフィルタ条件を以下の通り変更しました。
2023/11/13 追記内容を修正。標準書式でエラーが出ないよう、 列幅が狭くて#####と表示されている場合とその他の場合を分けた。
変更前
Selection(1).Text
変更後
if Left(Selection(1).Text ,1) = "#" then
Format(Selection(1).Value, Selection(1).NumberFormatLocal)
else
Selection(1).Text
end if
詳細については、以下の記事を参照。
「選択オートフィルタの改良と標準機能について~.textの列幅エラー対策~」
みんな大好きオートフィルタ。
パソコンの操作は、マウスよりキーボードで処理したい派なので、
エクセルのオートフィルタを使うときはカーソル動かすのがめんどくさい。
なので、よく利用するオートフィルタ操作をマクロにして、
ショートカットキー登録をすると便利。
一番使う操作は、選択セルの内容でオートフィルタをする。
コマンドはめっちゃ簡単で、こんだけ。
myRange.AutoFilter field:=fColNo, Criteria1:=selection.text
myRangeはオートフィルタのアドレス。
fieldはオートフィルタ範囲で何列目で絞り込むか。
Criteria1は絞り込む内容。
オートフィルタの範囲、絞込みの有無は 、以下で取得できる。(Office TANAKAより)
http://officetanaka.net/excel/vba/tips/tips129.htm
オートフィルタ範囲:ActiveSheet.AutoFilter.Range
絞込み有無:ActiveSheet.AutoFilter.Filters(fColNo).On
使ってみて思うこと。
①すでにその列でフィルタ済みだったら解除したい。
②オートフィルタ外の列だったらオートフィルタの範囲を変えたい。
③データがないのにオートフィルタしないで欲しい。
④他の条件での絞り込みもしたい。
ア 選択したセル以外で絞込み
イ 選択したセルの内容を含むセルで絞込み
ウ クリップボードの内容を含むセルで絞込み
エ 空白でなく、0でもないセルで絞込み
今回は、上記に対応したソースを掲載します。
絞込み条件は、Criteria1で指定するだけなので、共通モジュールを用意して、
各プロシージャから呼び出す方法にしました。
使ってみて思うこと。
①すでにその列でフィルタ済みだったら解除したい。
②オートフィルタ外の列だったらオートフィルタの範囲を変えたい。
③データがないのにオートフィルタしないで欲しい。
④他の条件での絞り込みもしたい。
ア 選択したセル以外で絞込み
イ 選択したセルの内容を含むセルで絞込み
ウ クリップボードの内容を含むセルで絞込み
エ 空白でなく、0でもないセルで絞込み
今回は、上記に対応したソースを掲載します。
絞込み条件は、Criteria1で指定するだけなので、共通モジュールを用意して、
各プロシージャから呼び出す方法にしました。
Sub ▼選択オートフィルタ()
Dim fText1 As String: fText1 = Format(Selection(1).Value, Selection(1).NumberFormatLocal)
Call オートフィルタ共通モジュール(fText1)
End Sub
Sub ▼選択以外でオートフィルタ()
Dim fText1 As String: fText1 = "<>" & Format(Selection(1).Value, Selection(1).NumberFormatLocal)
Call オートフィルタ共通モジュール(fText1)
End Sub
Sub ▼検索オートフィルタ()
Dim fText1 As String: fText1 = "*" & Format(Selection(1).Value, Selection(1).NumberFormatLocal) & "*"
Call オートフィルタ共通モジュール(fText1)
End Sub
Sub ▼クリップボードオートフィルタ()
Dim fText1 As String: fText1 = "*" & GetCB & "*"
Call オートフィルタ共通モジュール(fText1)
End Sub
Sub ▼空白と0以外でオートフィルタ()
Dim fText1 As String: fText1 = "<>"
Dim fText2 As String: fText2 = "<>0"
Call オートフィルタ共通モジュール(fText1, fText2)
End Sub
Private Function GetCB() As String
'クリップボードから文字列を取得
'https://www.ka-net.org/blog/?p=7537
With CreateObject("Forms.TextBox.1")
.MultiLine = True
If .CanPaste = True Then .Paste
GetCB = .Text
End With
End Function
Private Sub オートフィルタ共通モジュール(fText1 As String, Optional fText2 As String = "")
If ActiveSheet.AutoFilter Is Nothing Then '<>""ではなく、Nothing
' MsgBox "オートフィルタを設定してください。"
' Exit Sub
If MsgBox("選択セルでオートフィルタを実行します", vbOKCancel) = vbOK Then
Selection.AutoFilter
Else
Exit Sub
End If
End If
Dim myRange As Range: Set myRange = ActiveSheet.AutoFilter.Range
Dim Flag_nodata As Boolean
If Cells(Rows.Count, Selection.Column).End(xlUp).Row = 1 And Cells(1, Selection.Column) = "" Then
Flag_nodata = True
Else
Flag_nodata = False
End If
Dim RC As Long
If Selection.Column > myRange(myRange.Count).Column Then ' 列が範囲より右なら
If Flag_nodata = True Then Exit Sub
RC = MsgBox("オートフィルタ外のため範囲を拡張します(再設定)", vbOKCancel, "オートフィルタ範囲拡張の有無")
If RC <> vbOK Then Exit Sub 'キャンセルなら拡張せずに終了
myRange.AutoFilter '一旦オートフィルタを解除
Set myRange = myRange.Resize(, Selection.Column - myRange.Column + 1) 'フィルタ列を選択範囲まで右側に拡張
myRange.AutoFilter
ElseIf Selection.Column < myRange.Column Then
If Flag_nodata = True Then Exit Sub
RC = MsgBox("オートフィルタ外のため範囲拡張します(再設定)", vbOKCancel, "オートフィルタ範囲拡張の有無")
If RC <> vbOK Then Exit Sub 'キャンセルなら拡張せずに終了
myRange.AutoFilter '一旦オートフィルタを解除
Set myRange = Cells(myRange.Row, Selection.Column).Resize(myRange.Rows.Count, myRange.Columns.Count + myRange.Column - Selection.Column) 'フィルタ列を選択範囲まで左側に拡張
myRange.AutoFilter
End If
Dim fColNo As Long: fColNo = Selection.Column - myRange.Column + 1 'フィルタ範囲の何番目の列か
'https://www.moug.net/tech/exvba/0030004.html
If ActiveSheet.AutoFilter.Filters(fColNo).On Then '選択中の列が絞り込まれているなら列フィルタ解除
myRange.AutoFilter field:=fColNo
Else
'絞り込まれてないなら
If Cells(Rows.Count, Selection.Column).End(xlUp).Row = 1 And Cells(1, Selection.Column) = "" Then
'MsgBox "列にデータがありません。終了します。"
Exit Sub
Else
'列にデータがあるのでフィルタする
If fText2 = "" Then
myRange.AutoFilter field:=fColNo, Criteria1:=fText1
Else
myRange.AutoFilter field:=fColNo, Criteria1:=fText1, Criteria2:=fText2, Operator:=xlAnd
End If
End If
End If
End Sub
2023/11/5 追記・修正 列幅が狭くて#####と表示されていてもオートフィルタができるよう ソースコード内のフィルタ条件を以下の通り変更しました。
2023/11/13 追記内容を修正。標準書式でエラーが出ないよう、 列幅が狭くて#####と表示されている場合とその他の場合を分けた。
変更前
Selection(1).Text
変更後
if Left(Selection(1).Text ,1) = "#" then
Format(Selection(1).Value, Selection(1).NumberFormatLocal)
else
Selection(1).Text
end if
詳細については、以下の記事を参照。
「選択オートフィルタの改良と標準機能について~.textの列幅エラー対策~」
アパッチのWEBアクセスログの集計⑤
2020年02月03日
アパッチのWEBアクセスログをエクセルVBAで集計したい。
今回は5回目。前回やろうとしたstep2について。
やりたいこと
やりたいこと
step1.フォルダ内の全ファイルを対象としたループを作る
step2.各ファイル内のデータを日付別にシート出力
step3.各シートで、ログデータを区切る
step4.区切られたデータを集計
過去ログ
①やりたいことの整理
②step3
③step3やり直し
④step4
前回書いたとおり、dictionary配列を使用。
この配列の利便性がやばい。
ログデータの日付文字列をキーとした配列を作って、
値として、行のデータをつなげていけばよさそう。
過去ログ
①やりたいことの整理
②step3
③step3やり直し
④step4
前回書いたとおり、dictionary配列を使用。
この配列の利便性がやばい。
ログデータの日付文字列をキーとした配列を作って、
値として、行のデータをつなげていけばよさそう。
Dim Dic, i As Long, buf As String, Keys
Set Dic = CreateObject("Scripting.Dictionary")
For i = TGyo + 1 To GyoEnd
buf = Cells(i, Col).Value ’Colは日付データの列
If Not Dic.Exists(buf) Then
Dic.Add buf, cells(i,1).value
Else
Dic(buf) = Dic(buf) & cells(i,1).value
End If
Next i
今回は、手動で作業してしまったので、ここまで。
今回は、手動で作業してしまったので、ここまで。
アパッチのWEBアクセスログの集計④
2020年01月14日
アパッチのWEBアクセスログをエクセルVBAで集計したい。
重複しないリスト作りとして5つ紹介されてますが、
高速処理なVBAという観点で考えると(1)か(2)か。
(1)Dictionaryオブジェクト(連想配列)
(2)コレクション
前回作ったログデータをもとにIPアドレスやUAなどを集計したところ、
(2)のコレクションのほうが若干速い結果になった。
条件を変えて、全セルをユニークデータにしたところ、以下のとおり逆転。
(1)10万行を10秒
(2)10万行を50秒
高速に重複チェックをできるなら、カウントも入れればstep4で使えるじゃんってことで、
先にstep4をやることにした。
列のデータを配列に格納して、ユニークデータに対するカウント数を出力するマクロ。
ソースはこちら。
step1.フォルダ内の全ファイルを対象としたループを作る
step2.各ファイル内のデータを日付別にシート出力
step3.各シートで、ログデータを区切る
step4.区切られたデータを集計
前回step3を汎用性もたせて作り直した結果遅くなった。
今回はstep2を検討したら、step4のネタが思いつきました。
日付ごとにシートを分けるための処理。
まず、ログデータの日付情報は[の後の11文字が対象。
[の位置を検索して、次の文字から11文字をmidで取ればOKだね。
問題となるのは、新出のデータなら新しいシートに、
既出のデータなら既存のシートに出力する必要があること。
シート一覧を作ろうとすると、重複のしないリストを作る処理になるので、
検索してみた。
参考ページ(いつもお世話になってるオフィスOffice TANAKA)
http://officetanaka.net/excel/vba/tips/tips80.htm
前回step3を汎用性もたせて作り直した結果遅くなった。
今回はstep2を検討したら、step4のネタが思いつきました。
日付ごとにシートを分けるための処理。
まず、ログデータの日付情報は[の後の11文字が対象。
[の位置を検索して、次の文字から11文字をmidで取ればOKだね。
問題となるのは、新出のデータなら新しいシートに、
既出のデータなら既存のシートに出力する必要があること。
シート一覧を作ろうとすると、重複のしないリストを作る処理になるので、
検索してみた。
参考ページ(いつもお世話になってるオフィスOffice TANAKA)
http://officetanaka.net/excel/vba/tips/tips80.htm
重複しないリスト作りとして5つ紹介されてますが、
高速処理なVBAという観点で考えると(1)か(2)か。
(1)Dictionaryオブジェクト(連想配列)
(2)コレクション
前回作ったログデータをもとにIPアドレスやUAなどを集計したところ、
(2)のコレクションのほうが若干速い結果になった。
条件を変えて、全セルをユニークデータにしたところ、以下のとおり逆転。
(1)10万行を10秒
(2)10万行を50秒
高速に重複チェックをできるなら、カウントも入れればstep4で使えるじゃんってことで、
先にstep4をやることにした。
列のデータを配列に格納して、ユニークデータに対するカウント数を出力するマクロ。
ソースはこちら。
Sub 列集計_配列型()
'大文字小文字区別 空欄対応
Dim tictoc As Double
tictoc = Timer
Dim Col As Long: Col = Selection.Column
Dim Gyo As Long, GyoEnd As Long, TGyo As Long, ColName As String
If Cells(1, Col) <> "" Then
TGyo = 1
Else
TGyo = Cells(1, Col).End(xlDown).Row
End If
ColName = Cells(TGyo, Col).Text
GyoEnd = Cells(Rows.Count, Col).End(xlUp).Row
If GyoEnd = 1 Then
MsgBox "データがありません"
Exit Sub
End If
Dim Dic, i As Long, buf As String, Keys
Set Dic = CreateObject("Scripting.Dictionary")
For i = TGyo + 1 To GyoEnd
buf = Cells(i, Col).Value
If Not Dic.Exists(buf) Then
Dic.Add buf, 1
Else
Dic(buf) = Dic(buf) + 1
End If
Next i
''出力
Worksheets.Add
Dim oArr() As Variant
ReDim oArr(Dic.Count + 1, 2)
oArr(0, 0) = "№"
oArr(0, 1) = ColName
oArr(0, 2) = "カウント"
Keys = Dic.Keys
For i = 0 To Dic.Count - 1
oArr(i + 1, 0) = i + 1
oArr(i + 1, 1) = Keys(i)
oArr(i + 1, 2) = Dic(Keys(i))
Next i
Set Dic = Nothing
Dim NoCell As Range
Set NoCell = Cells(2, 1)
Call ArrayToCell(NoCell, oArr)
NoCell.AutoFilter
With ActiveSheet.AutoFilter.Range
.Sort Key1:=.Range("C2"), Order1:=xlDescending, Header:=xlYes
End With
Cells(1, 1) = Format(i, "#,##0種")
Cells(1, 2) = "合計:"
Cells(1, 3) = Format(GyoEnd - TGyo, "#,##0件")
On Error Resume Next
ActiveSheet.Name = ColName & "_内訳"
Call ArrayToCell(NoCell, oArr, 1) ' №の入った1列目だけ再出力
'Debug.Print "[" & Now & "] "; Format(GyoEnd - 1, "#,##0行を") & Format(Timer - tictoc, "0.00秒")
End Sub
コメント
Sub ArrayToCell(Target As Range, oArr, Optional ColCnt As Long = 0) '配列貼り付け便利モジュール
Dim iRow '// データ設定用の行カウンタ
Dim iCol '// データ設定用の列カウンタ
Dim iRowMax '// 二次元配列の最大行数
Dim iColMax '// 二次元配列の最大列数
'// 1次元目の要素数を取得
iRowMax = UBound(oArr, 1) - LBound(oArr, 1) + 1
'// 2次元目の要素数を取得
iColMax = UBound(oArr, 2) - LBound(oArr, 2) + 1
'// Rangeオブジェクトで貼り付けるセル範囲を指定する場合
'Range(Cells(1, 1), Cells(iRowMax, iColMax)).Value = oArr
'// Rangeオブジェクトで開始セルから貼り付けるセル範囲を拡張する場合
If ColCnt > 0 And ColCnt <= iColMax Then
Target.Resize(iRowMax, ColCnt).Value = oArr
Else
Target.Resize(iRowMax, iColMax).Value = oArr
End If
End Sub
コメント
- 列データ集計は業務上使用頻度が高く、結構便利なモジュール。
- 重複のないリストを作るだけであれば、Dic.Exists(buf)の判定処理は不要だが
今回はカウント処理をするために残した。 - Dictionaryオブジェクトでは、キーデータはユニークである必要
試した結果、キーはブランクOK。大文字小文字の区別あり。