2005年11月24日 木曜日

続・なんでもかんでもExcel症候群

Unixな機械からテキストファイルを直接持ってくると、改行がWindowsと異なるので、Excelで扱うのがちょっとめんどくさい。FTPできるときは、ASCIIで持ってくればDOS/Windowsなテキストファイルになるのだけど、世の中FTPできなくて専用のクライアントを共用されることもあって涙が出てくることがもうしょっちゅうあって困る。で、そんな_だめだめクライアント_に泣く泣くつきあわなければならないのだが、この際_なんでもかんでもExcelにお任せ_なのだ。
これからはASCIIモード転送のことは忘れて、すべてBINARYモード転送にしてしまいましょう。DOS/Windowsの世界ではUnixで作成したテキストは何行あっても、_たった1行のテキストファイル_と見なせるから、これを全部1つの文字列に取り込んで、ばらす方向で考える。ソースを示す。

Sub TextRead(FileName As Variant)
' TextReadプロシジャ: FileNameで指定されたファイルを読み込む
' FileName: 入力するファイルのファイル名
    Dim RE As Object
    Dim StreamString As String
    Dim textlines As Object
    Dim textline As Object
    Dim tmp(2) As String
    Dim myDate As Date
    Dim PATTERN as String
    Set RE = CreateObject("VBScript.RegExp")
    
    With RE
        .Pattern = ".+\n"
        .IgnoreCase = False
        .Global = True
    End With
    Open FileName For Input As #1
    Line Input #1, StreamString
    Close #1
    
    Set textlines = RE.Execute(StreamString)
        
    For Each textline In textlines
                
	' textlineはUnixにおける行と同じ。ここに処理したいことを書く
    
    Next
    
    Set RE = Nothing
End Sub

こんなことをやると、textlinesコレクションに入る各要素はUnixで言うところの行の集合、その要素は行と同じものになったりします。Executeメソッドって便利だねぇ。イメージ的にはperlやrubyでやっているストリームに対するwhileループのようなことができるのだけど若干表現が冗長。VBAで可変長Stringはヘルプによると2GBまで取れるらしいので、まぁ普通のテキストファイルの処理をする場合は一つのファイルを1つの文字列に取り込んで処理して何の問題もないだろう。そんなわけで、Unixからファイルを持ってきてExcelでごにょごにょするときはASCII転送しちゃいけません。BINARY転送でOK。
さらに()を用いてグループ化した正規表現を書いて括弧にマッチした文字列を抽出したいのだけどどうするか? これも簡単。めんどくさいのでこんなプロシジャを書いておこう。

Public Sub REGetValue(ByVal Pattern As String, ByVal Stream As String, ParamArray Args())
' REGetValueプロシジャ: 正規表現Patternの中で()を用いてグルーピングされた値を抜き出す
' Pattern: 正規表現
' Stream:  検索を行う文字列
' Args:    パターンマッチした値を格納する変数
    Dim RE As Object
    Dim Results As Object
    Dim Result As Object
    Dim i As Integer
    
    Set RE = CreateObject("VBScript.RegExp")
    With RE
        .Pattern = Pattern
        .IgnoreCase = False
        .Global = True
    End With
    
    Set Results = RE.Execute(Stream)
    For Each Result In Results
        
        For i = LBound(Args) To UBound(Args)
            Args(i) = Result.SubMatches(i)
        Next i
    
    Next Result
    Set RE = Nothing
    
End Sub

パターンとマッチした文字を入れる変数を渡すと、パターンマッチを行って変数にマッチした文字を入れるプロシジャーだ。変数の個数は可変長で与えられる。正規表現が間違っているときや、個数があわないときは何か変なことが起こるかもだけどもその辺は適当に例外処理するなり、エラー処理するなり考えよう。(そんなところまで書いてられないわい。) とりあえず正規表現周りはこの辺でおしまい。あとはフクロウ本を読みましょ。