はじめに
CSVファイルを読み込んで別のCSVファイルを出力する、という処理をVBScriptで書いていたところ、処理速度が遅くなり、改善のため試行錯誤することになりました。
そこで、テキストの書き込みに関して、どのような処理が遅いのか、少し調べてみます。
1行ずつ書き込む場合
Dim fso
Set fso = CreateObject("Scripting.FileSystemObject")
Dim output
output = "xxx\output.csv"
Dim write
Set write = fso.OpenTextFile(output, 2, True)
Dim i
For i = 1 To 1000
write.WriteLine("abc")
Next
write.Close
【結果】
約0.01秒/1,000行
約0.1秒/10,000行
約1.1秒/100,000行
思っていたほど遅くありません。大きなファイルでなければ特に工夫せずに1行ずつ書き込んでも問題なさそうです。
毎回ファイルを開いて書き込む場合
開く度に閉じる場合
Dim fso
Set fso = CreateObject("Scripting.FileSystemObject")
Dim output
output = "xxx\output.csv"
Dim write
Dim i
For i = 1 To 1000
Set Write = fso.OpenTextFile(output, 8, True)
write.WriteLine("abc")
write.Close
Next
【結果】
約11秒/1,000行
開いたあと閉じる処理を書かない場合
Dim fso
Set fso = CreateObject("Scripting.FileSystemObject")
Dim output
output = "xxx\output.csv"
Dim write
Dim i
For i = 1 To 1000
fso.OpenTextFile(output, 8, True).WriteLine("abc")
Next
【結果】
約10秒/1,000行
OpenTextFile
でTextStream
オブジェクトを取得する処理が遅いようです。
実際の作業では、ここにハマって失敗しました。
1つのファイルにつき1回で済ませるべきですね。
文字列を結合して一気に書き込む
Dim fso
Set fso = CreateObject("Scripting.FileSystemObject")
Dim output
output = "xxx\output.csv"
Dim write
Set Write = fso.OpenTextFile(output, 2, True)
Dim str
Dim i
For i = 1 To 1000
str = str & "abc" & vbCrLf
Next
write.WriteLine(str)
write.Close
【結果】
約0.01秒/1,000行
約0.07秒/10,000行
約9秒/100,000行
行が多くなるとかなり遅くなるという意外な結果ですが、「VBA 文字列結合 遅い」とググるといろいろ情報が出てきます。文字列結合は遅いのですね。
行数が少なければ気にしなくても良いかもしれませんが、多い場合は注意が必要です。
連想配列に文字列を格納し、ループ処理で書き込む
Dim fso
Set fso = CreateObject("Scripting.FileSystemObject")
Dim output
output = "xxx\output.csv"
Dim write
Set Write = fso.OpenTextFile(output, 2, True)
Dim dict
Set dict = CreateObject("Scripting.Dictionary")
Dim i
For i = 1 To 1000
dict.Add i, "abc"
Next
Dim key
For Each key In dict
write.WriteLine(dict(key))
Next
write.Close
【結果】
約0.01秒/1,000行
約0.15秒/10,000行
約1.8秒/100,000行
実用的な速度だと思います。
配列に文字列を格納し、ループ処理で書き込む
Dim fso
Set fso = CreateObject("Scripting.FileSystemObject")
Dim output
output = "xxx\output.csv"
Dim write
Set Write = fso.OpenTextFile(output, 2, True)
Dim arr(999)
Dim i
For i = 1 To 1000
arr(i - 1) = "abc"
Next
Dim str
For Each str In arr
write.WriteLine(str)
Next
write.Close
【結果】
約0.01秒/1,000行
約0.15秒/10,000行
約1.2秒/100,000行
これもなかなかの速度ですね。
1行ずつ書き込む処理とあまり時間が変わらないところをみると、配列への格納や配列からのデータの取り出しには時間がかからないようです。
配列に文字列を格納し、Join関数で結合して書き込む
Dim fso
Set fso = CreateObject("Scripting.FileSystemObject")
Dim output
output = "xxx\output.csv"
Dim write
Set Write = fso.OpenTextFile(output, 2, True)
Dim arr(999)
Dim i
For i = 1 To 1000
arr(i - 1) = "abc"
Next
write.WriteLine(Join(arr, vbCrLf))
write.Close
【結果】
約0.01秒/1,000行
約0.01秒/10,000行
約0.04秒/100,000行
約0.4秒/1,000,000行
おお、早い。書き込みを1回にしたからですね。
何らかの事情で、文字列をどこかに確保したあとで書き込みたい場合は、この手法を使うのが良いようです。
配列のサイズをうまく調整できるかがポイントです。
おまけ
VBSではCollection
が使えません。
→Dictionary
を使いましょう。
VBSではLike
が使えません。
→部分一致の場合はInStr
を使いましょう。そうでない場合は正規表現を使いましょう。