でも結局どのやり方が一番早いんだ!ってことでいろいろコード書いて計測してみました。
条件
・約3MBの可変長CSVファイルの全読み込みと一部のコードでは行数取得。
・処理時刻の計算はVBA 超高精度タイマークラスを使っています
・メモリ処理のRedim preserveは重たい処理ですが、可変長ファイルなのでメモリの一括確保はしませんでした。
ファイルサイズからメモリ容量を計算してメモリを確保するともっと早くなります。メモリマッピングのような処理です。
・For文で10回同じコードを走らせてます。ですので初回以降はキャッシュが効いて高速になります。
・時刻の単位は秒です。
・最終的な文字コードはUnicodeです
・計測用コードは以下の通り
Option Explicit Public Function ReadFileTest_LineInput(ByVal File_Target As String) '=========================================================================== 'OpenステートメントのLine Inputでの順次読み込みを行います '=========================================================================== Dim intFF As Long 'ファイル番号 Dim str_buf As String 'ただの汎用変数 Dim str_Strings() As String 'ファイルの中身全部の文字列型配列 Dim Row As Long '行数カウント Dim cls_Timer As New Cl_QueryPerformance '高精度タイマークラス Call cls_Timer.GetQueryPerformanceTime 'タイマー初期化 intFF = FreeFile Open File_Target For Input As #intFF Do While Not EOF(intFF) 'ファイルの終端かどうかを確認します Line Input #intFF, str_buf 'データ行を読み込みます '============================================================== '配列化有効時 'ReDim Preserve str_Strings(Row) As String 'str_Strings(Row) = str_buf 'Row = Row + 1 '============================================================== Loop Close #intFF Debug.Print cls_Timer.GetQueryPerformanceTime '前回タイマー初期化時からの時間 End Function Public Function ReadFileTest_InputB(ByVal File_Target As String) '=========================================================================== 'OpenステートメントのInputBでの順次読み込みを行います '=========================================================================== Dim intFF As Long 'ファイル番号 Dim var_buf 'ただの汎用変数 Dim str_Strings() As String 'ファイルの中身全部の文字列型配列 Dim Row As Long '行数カウント Dim cls_Timer As New Cl_QueryPerformance '高精度タイマークラス Dim bytSjis As String Dim str_Uni As String Call cls_Timer.GetQueryPerformanceTime 'タイマー初期化 intFF = FreeFile Open File_Target For Input As #intFF bytSjis = InputB(FileLen(File_Target), #intFF) ' SJISのテキスト読み込み Close #intFF str_Uni = StrConv(bytSjis, vbUnicode) ' SJISからUNICODEへ ' '============================================================== ' '配列化有効時 ' var_buf = Split(str_Uni, vbCrLf) '改行コードごとに区切って配列化 ' Row = UBound(var_buf) '行数取得 ' '============================================================== Debug.Print cls_Timer.GetQueryPerformanceTime '前回タイマー初期化時からの時間 End Function Public Function ReadFileTest_Open_Binary(ByVal File_Target As String) '=========================================================================== 'OpenステートメントのBinaryでの順次読み込みを行います '=========================================================================== Dim intFF As Long 'ファイル番号 Dim byt_buf() As Byte Dim var_buf Dim str_buf As String 'ただの汎用変数 Dim str_Strings() As String 'ファイルの中身全部の文字列型配列 Dim Row As Long '行数カウント Dim cls_Timer As New Cl_QueryPerformance '高精度タイマークラス Dim i As Long Dim bytSjis As String Dim str_Uni As String Call cls_Timer.GetQueryPerformanceTime 'タイマー初期化 intFF = FreeFile Open File_Target For Binary As #intFF ReDim byt_buf(LOF(intFF)) Get #intFF, , byt_buf Close #intFF str_Uni = StrConv(byt_buf(), vbUnicode) 'Unicodeに変換 '============================================================== '配列化有効時 var_buf = Split(str_Uni, vbCrLf) '改行コードごとに区切って配列化 Row = UBound(var_buf) '行数取得 '============================================================== Debug.Print cls_Timer.GetQueryPerformanceTime End Function Public Function ReadFileTest_FSO_ReadAll(ByVal File_Target As String) '=========================================================================== 'FileSystemObjectのReadAllでの一括読み込みを行います '=========================================================================== Dim var_buf Dim str_buf As String Dim Row As Long '行数カウント Dim cls_Timer As New Cl_QueryPerformance '高精度タイマークラス Dim FSO As New FileSystemObject 'FileSystemObject Call cls_Timer.GetQueryPerformanceTime 'タイマー初期化 With FSO.OpenTextFile(File_Target, ForReading) str_buf = .ReadAll '一括読み込み .Close End With ' '============================================================== ' '配列化有効時 ' var_buf = Split(str_buf, vbCrLf) '改行コードごとに区切って配列化 ' Row = UBound(var_buf) '行数取得 ' '============================================================== Set FSO = Nothing Debug.Print cls_Timer.GetQueryPerformanceTime End Function Public Function ReadFileTest_FSO_ReadLine(ByVal File_Target As String) '=========================================================================== 'FileSystemObjectとTextStreamでのReadLineを行います '=========================================================================== Dim str_buf As String 'ただの汎用変数 Dim str_Strings() As String 'ファイルの中身全部の文字列型配列 Dim Row As Long '行数カウント Dim cls_Timer As New Cl_QueryPerformance '高精度タイマークラス Dim FSO As New FileSystemObject Dim FSO_TS As TextStream ' TextStream Call cls_Timer.GetQueryPerformanceTime 'タイマー初期化 Set FSO_TS = FSO.OpenTextFile(File_Target, ForReading) With FSO_TS Do Until .AtEndOfStream str_buf = .ReadLine ' '============================================================== ' '配列化有効時 ' ReDim Preserve str_Strings(Row) As String ' str_Strings(Row) = str_buf ' Row = Row + 1 ' '============================================================== Loop .Close End With Set FSO_TS = Nothing Set FSO = Nothing Debug.Print cls_Timer.GetQueryPerformanceTime End Function
・結果はこのようになりました
順次読み込み | 一括読み込み | 一括読み込み | ||||
Line Input (配列化有) | Line Input (配列化無) | InputB (配列化有) | InputB (配列化無) | Binary (配列化有) | Binary (配列化無) | |
1回目 | 0.4208 | 0.3645 | 0.1739 | 0.1394 | 0.0998 | 0.1167 |
2回目 | 0.2878 | 0.2487 | 0.1413 | 0.1072 | 0.0928 | 0.0602 |
3回目 | 0.2821 | 0.2459 | 0.1178 | 0.0917 | 0.0979 | 0.0549 |
4回目 | 0.2817 | 0.2498 | 0.1081 | 0.0909 | 0.0992 | 0.0569 |
5回目 | 0.2808 | 0.248 | 0.1083 | 0.0893 | 0.09 | 0.0591 |
6回目 | 0.2881 | 0.2452 | 0.1181 | 0.0899 | 0.0926 | 0.0568 |
7回目 | 0.283 | 0.2457 | 0.1129 | 0.0906 | 0.0922 | 0.0573 |
8回目 | 0.2801 | 0.252 | 0.1095 | 0.0908 | 0.0925 | 0.0588 |
9回目 | 0.28 | 0.2461 | 0.1085 | 0.0917 | 0.092 | 0.059 |
10回目 | 0.2916 | 0.2455 | 0.109 | 0.0917 | 0.0945 | 0.0572 |
平均 | 0.2976 | 0.25914 | 0.12074 | 0.09732 | 0.09435 | 0.06369 |
一括読み込み | 順次読み込み | |||||
FileSystemObject ReadAll (配列化有) | FileSystemObject ReadAll (配列化無) | FileSystemObject ReadLine (配列化有) | FileSystemObject ReadLine (配列化無) | |||
1回目 | 0.4591 | 0.4369 | 0.4437 | 0.4139 | ||
2回目 | 0.2952 | 0.2922 | 0.3263 | 0.2748 | ||
3回目 | 0.2969 | 0.2787 | 0.3184 | 0.274 | ||
4回目 | 0.2973 | 0.2789 | 0.3188 | 0.2757 | ||
5回目 | 0.3001 | 0.2786 | 0.322 | 0.2763 | ||
6回目 | 0.2964 | 0.292 | 0.319 | 0.2768 | ||
7回目 | 0.295 | 0.2786 | 0.3186 | 0.2743 | ||
8回目 | 0.2988 | 0.2787 | 0.3248 | 0.2739 | ||
9回目 | 0.2992 | 0.282 | 0.3203 | 0.2754 | ||
10回目 | 0.2942 | 0.2962 | 0.3199 | 0.2772 | ||
平均 | 0.31322 | 0.29928 | 0.33318 | 0.28923 |
結果
・OpenステートメントのBinaryモードにより一括バイナリ読み込み、Unicode変換が最も高速という結果になりました。
まぁ当然ですね。配列化も簡単なので今後はこれをメインに使っていこう。
おそらくこれがVBAでAPIを使わない場合でもっとも高速です。
さらにテキストファイルの最終行の取得なんてのはさらに早くできます。
これはログを監視するようなマクロではかなり重要です。
まぁファイル開きっぱなしの方が高速ですが。
・数百MBものファイルなら一気に配列(メモリ)に読み込むなんてマネは恐ろしくてできません。
FileSystemObjectとTextStreamでの順次読み込み。同時にデータに対する処理を行うっていのがいいと思います。
これだとメモリをほとんど使わないで済みます。その変わりファイルIO中に他の作業をするので堅実なコードが必要です。
用途によって使い分けるといいものが作れそうです。
0 件のコメント:
コメントを投稿