2016-10-11

ADODB.Stream: Read write-locked file

あるシステムのログ集計を、Excel マクロでやろうとしたときのこと。

ログは UTF-8 で書かれており、ググれば ADODB.Stream を使えば良いことは直ぐに分かる。ほどなくマクロは完成し、本番投入の直前、その問題は発覚した。

ADODB.Stream.LoadFromFile は、書き込みロックが掛かったファイルを読み込めない!

それはつまり、システムが開いている当日分のログを集計できないことを意味する。しかし、今回の要件としてそれは受け入れられない。何てこった。そんなこと Google 先生も教えてくれなかったよ。

Sub Test1()
    With CreateObject("ADODB.Stream")
        .Type = 2   ' 2=adTypeText
        .Charset = "UTF-8"
        .Open
        .LoadFromFile "WriteLocked.txt"
        ' ReadText...
        .Close
    End With
End Sub

ならば、アクセスモード(Stream.Mode)を読み取り専用(adModeRead)にすれば開けるはず・・・、

Sub Test2()
    With CreateObject("ADODB.Stream")
        .Mode = 1   ' 1=adModeRead
        .Type = 2   ' 2=adTypeText
        .Charset = "UTF-8"
        .Open
        .LoadFromFile "WriteLocked.txt"
        ' ReadText...
        .Close
    End With
End Sub

どういうことなの。

恐らく、アクセスモードは Stream に対してのものであり、Stream が読み取り専用となったために Stream への書き込みメソッドである LoadFromFile がブロックされたのだと思われる。そうすると LoadFromFile に対してアクセスモードを指定する術がない以上、この方向での解決策は絶望的に思える。

ではファイルの読み込みは別の手段で行い、それを Stream に食わせたらどうか?

じゃそれ FileSystemObject で! と思ったら、FileSystemObject はバイナリファイルを読み込めないときた。ダメ元で無理やり読み込ませてみたら、化け化けになった。泣きたい。

結局、最も基本的な方法で読み込ませることで上手くいった。

Sub Test3()
    Dim fd As Long, _
        buf() As Byte
    fd = FreeFile
    Open "WriteLocked.txt" For Binary As #fd
    ReDim buf(LOF(fd))
    Get #fd, , buf
    Close #fd

    With CreateObject("ADODB.Stream")
        .Type = 1   ' 1=adTypeBinary
        .Open
        .Write buf
        .Position = 0
        .Type = 2   ' 2=adTypeText
        .Charset = "UTF-8"
        ' ReadText...
        .Close
    End With
End Sub

この方法の欠点は、ファイル全体をメモリに読み込んでしまうこと。できれば 1 行ずつ読み込ませたいが、バッファリング制御の実装とか大変そう過ぎて、やってみる気すら起きない。

0 件のコメント:

コメントを投稿

注: コメントを投稿できるのは、このブログのメンバーだけです。