2011年3月24日木曜日

VBA 高速にテキストファイル末尾を取得する

Excel、Access VBAでのファイル末尾取得する方法です
この前、InputBでのファイル読み込みが高速なことに気がついたので、
ついでにテキストファイル末尾をそこそこ高速に取得するコードを考えました。

なお、CSVファイルなんかはファイル末尾の行が改行コードのみ。
なんてことも多々ありますので、取得したファイル末尾がデータ無し、または改行コードのみなら
データのある行のデータを自動で取得する機能もつけました。
Optional引数で指定できます。

条件

・約3MBの可変長CSVファイルのファイル末尾取得。
・処理時刻の計算はVBA 超高精度タイマークラスを使っています
・時刻の単位は秒です。

ソースコード


Option Explicit

Public Function TailTextFile(ByVal File_Target As String, Optional UntilExisting As Boolean = True) As String
'===========================================================================
'ファイルの最終行を取得します
'UntilExistingがTrueなら最終行が空の場合、データが存在する行までさかのぼり、そのデータを取得します
'===========================================================================

Dim intFF As Long 'ファイル番号
Dim cnt_StrFirst As Long, cnt_StrLast As Long
Dim str_Result As String, str_Sjis As String, str_Uni As String

Dim cls_Timer As New Cl_QueryPerformance '高精度タイマークラス

Call cls_Timer.GetQueryPerformanceTime 'タイマー初期化

intFF = FreeFile
Open File_Target For Input As #intFF
str_Sjis = InputB(FileLen(File_Target), #intFF) ' SJISのテキスト読み込み
Close #intFF

Debug.Print cls_Timer.GetQueryPerformanceTime '前回タイマー初期化時からの時間

str_Uni = StrConv(str_Sjis, vbUnicode) ' SJISからUNICODEへ

cnt_StrFirst = InStrRev(str_Uni, vbCrLf) 'テキストの最後から最初の改行コードまで
cnt_StrLast = Len(str_Uni) 'テキストすべての文字数

Debug.Print cls_Timer.GetQueryPerformanceTime '前回タイマー初期化時からの時間

If UntilExisting = False Then '最終行が空でもおかまいなし
str_Result = Mid(str_Uni, cnt_StrFirst, cnt_StrLast - cnt_StrFirst) '最終行取得
Else
Do Until cnt_StrLast < 1
str_Result = Mid(str_Uni, cnt_StrFirst, cnt_StrLast - cnt_StrFirst) '指定文字数区間取得
If str_Result <> "" And str_Result <> Chr(13) Then Exit Do '文字無し、改行コードのみでなかったらループ終了
cnt_StrFirst = InStrRev(str_Uni, vbCrLf, cnt_StrFirst) '次の行の開始文字数を取得
cnt_StrLast = InStr(cnt_StrFirst + 1, str_Uni, vbCrLf) '次の行の終了文字数を取得
Loop
End If

Debug.Print cls_Timer.GetQueryPerformanceTime '前回タイマー初期化時からの時間

Debug.Print str_Result

TailTextFile= str_Result

End Function


結果はこんな感じ。

0.0483  ← ファイル読み込みにかかった時間
0.0384  ← Unicodeへのコンバートにかかった時間
0.0007  ← 最終行取得抽出にかかった時間

"1999.10.26","13:18","1.05740","1.05790","1.05720","1.05770","10" ←取得したデータ

合計 87.4ミリ秒

ファイル読み込み、Unicodeコンバートに時間がかかっています。
Loop文でファイル末尾抽出をやってるのはただ高速だからです。
改行コードでSplitするほうが簡単ですが、メモリも食いますし、遅いです。

今回3MBのファイルでしたが、これが数百MBになるとこの関数は使えません。
一度メモリに全部のテキストを読み込んでることになるのでメモリが足りなくなります。

せいぜい10MBくらいのファイルに使用してやってください。

0 件のコメント:

コメントを投稿