友人から電話で質問されたネタです。「何かいいアイデアはない?」と。
上図のようなログファイルがあります。毎日一回、何かをカウントした結果を記録するログファイルです。本日の数値は何らかの方法で計算するとして、その数値と一緒に前回との差も記録したい…というのが友人のリクエストでした。つまり、下のようにしたいと。
前回の数値は、ログファイルの最終行に記録されています。差を計算するには、この最終行を取得しなければなりません。オーソドックスにLine Inputステートメントを使うなら、次のようなコードになります。
Sub Sample1() Dim buf As String Const Target As String = "C:\Count.log" Open Target For Input As #1 Do Until EOF(1) Line Input #1, buf Loop Close #1 MsgBox "最終行のデータは、" & buf End Sub
ファイルから最終行まで1行ずつ読み込めば、最後に読み込んだ行が変数bufに残ります。これが最終行のデータです。なるほど、確かに間違いはないのですが、ログファイルの行数が数千行とかになると、それなりに時間もかかりますし、何といっても"空読み込み"するという考え方は、あまり美しくありませんね。そこで、FileSystemObjectを使って全データを一括取得して、Split関数で分割する方法でやってみましょう。なお、ここでは解説を簡素化するために、上記のように4行しか入力されていないC:\Count.logを対象に話を進めます。
テキストファイルのデータを一気に取得するには次のようにします。
Sub Sample2() Dim buf As String, FSO As Object Const Target As String = "C:\Count.log" Set FSO = CreateObject("Scripting.FileSystemObject") With FSO.OpenTextFile(Target, 1) buf = .ReadAll .Close End With Debug.Print buf End Sub
FileSystemObjectに関しては、下記のページをご覧ください。
ここからはイメージの世界です。このログファイルは、実際には次のように記録されています。
各行のデータが改行コード(vbCrLf)で区切られていますので、その改行コード(vbCrLf)を区切文字としてSplit関数で分割してやると、
という配列が得られます。配列のインデックス値が「0」から始まっている点に留意してください。Excelの標準的な設定では、配列の要素は「0」から始まるからです。また、データの最後が改行で終わっていると、配列の最後が「空の要素」になります。いま欲しいのは「データの最終行」ですから、その点も頭に入れておいてください。
変数bufに格納した全データを、Split関数を使って、改行コード(vbCrLf)で分割します。分割した結果は配列になりますから、配列の最終要素を取り出します。最終要素のインデクス番号は、UBound関数で調べます。
Sub Sample3() Dim buf As String, LastRow As Long, FSO As Object Const Target As String = "C:\Count.log" Set FSO = CreateObject("Scripting.FileSystemObject") With FSO.OpenTextFile(Target, 1) buf = .ReadAll .Close End With Set FSO = Nothing LastRow = UBound(Split(buf, vbCrLf)) - 1 MsgBox Split(buf, vbCrLf)(LastRow) End Sub
これで、テキストファイルの最終行を取得できました。
友人のリクエストでは、最終行に記録されている「2007/7/4 57833」の「57833」部分も取り出さなければなりません。日付と数値データの間は半角のスペースで区切られているので、InStr関数でスペースの位置を調べて、そこから後ろを抜き出して・・・でもいいんですが、こんなときも、Split関数なら一発で取得できます。この「2007/7/4 57833」をスペースで分割して、2番目の要素を調べればいいんです。
Sub Sample4() Dim buf As String, LastRow As Long, FSO As Object Const Target As String = "C:\Count.log" Set FSO = CreateObject("Scripting.FileSystemObject") With FSO.OpenTextFile(Target, 1) buf = .ReadAll .Close End With Set FSO = Nothing LastRow = UBound(Split(buf, vbCrLf)) - 1 buf = Split(buf, vbCrLf)(LastRow) MsgBox Split(buf, " ")(1) End Sub
最後の3行は「Split(Split(buf, vbCrLf)(UBound(Split(buf, vbCrLf)) - 1), " ")(1)」と1行で書くこともできますが、可読性が悪くなるので、1ステップずつ変数に入れた方がいいでしょう。