以前、文字列を異なるデリミタで切り分けるテクニックを紹介した。
thom.hateblo.jp
直近でこのテクニックを利用したCSVファイルの処理を作成したのでメモしておく。
※今回はデータ中にカンマが無いシンプルなファイル処理とする。
一般的にはCSVをテキストファイルとして読み込む場合、一行ずつ読みながらカンマでSplitする。
コードにすると以下のようになる。
※FileSystemObjectとTextStreamを使用するには、あらかじめMicrosoft Scripting Runtimeを参照設定しておく必要がある。
Sub 一行ずつ読み込み() Dim FSO As New FileSystemObject Dim TS As TextStream Set TS = FSO.OpenTextFile("C:\work\sample.csv", ForReading, False) Dim Record As String Do Until TS.AtEndOfLine Record = TS.ReadLine Debug.Print Split(Record, ",")(0), Split(Record, ",")(2) Loop '処理の最後でファイルをクローズする。 TS.Close Set TS = Nothing Set FSO = Nothing End Sub
このコードで肝になるのは次の部分だ。
Do Until TS.AtEndOfLine Record = TS.ReadLine Debug.Print Split(Record, ",")(0), Split(Record, ",")(2) Loop
和訳するとこんな感じ。
ファイルの最終行まで、 Record = ファイルからの行読込 表示 カンマで区切った0番目, カンマで区切った2番目 繰り返し
しかしこれだと、ファイルを一行ずつ読み込みながら処理をするので、処理が終わるまでファイルを開きっぱなしになる。
たとえ読み取り専用であっても、ファイルアクセス時間は短いほうが良い。
そこで冒頭で述べた、2段階Splitの出番だ。
以下のコードでは、TS.ReadAllでCSVを一気に読み取り、その場でSplitを使って動的配列に格納し、すぐにファイルをクローズしている。
Sub 一括読み込み() Dim FSO As New FileSystemObject Dim TS As TextStream Set TS = FSO.OpenTextFile("C:\work\sample.csv", ForReading, False) Dim Records() As String Records = Split(TS.ReadAll, vbLf) TS.Close '一括読込後、すぐにファイルクローズできる。 Set TS = Nothing Set FSO = Nothing For i = LBound(Records) To UBound(Records) If Records(i) <> "" Then Debug.Print Split(Records(i), ",")(0), Split(Records(i), ",")(2) End If Next End Sub
そして動的配列RecordsをForループで回しながら、ひとつめのマクロと同じように出力している。
500レコードのCSVを用いて計測してみたとこ、一行ずつ読み取るマクロではファイルオープンの時間は約1.6秒、一括読み込みの場合は0.017秒だった。
デバッグプリントが時間を食うので総合的には同じくらいであるが、後者の方法だとファイルをつかんでいる時間が圧倒的に短くなるので、拘る方は活用してみてほしい。
※今回のCSVはたまたま改行コードがLFだったのでvbLfでSplitしているが、CSVを作成した環境で変わるので注意。
改行コードについては以下の記事を参照
thom.hateblo.jp
また、時間の計測については以下の記事を参照
thom.hateblo.jp
以上。