【入門編】【上級プロフェッショナル向け】FSOとADODB.Streamを多層結合し Shift_JIS / UTF-8 / UTF-16 が混在する複数ログの一括文字コード統一パイプライン – VBScript (Visual Basic Scripting Edition)解析バイブル

スポンサーリンク
スポンサーリンク

【極限の知見】文字化けの地獄から脱出せよ:FSOとADODB.Streamによる文字コード統一パイプラインの構築

「マクロの記録」から一歩踏み出し、VBScriptを使って業務を自動化しようとしている皆さん、ようこそ。

実務の世界へ足を踏み入れると、必ずと言っていいほど直面する「壁」があります。それは、「文字化け」です。
システムAから出力されたログは `Shift_JIS`、システムBからは `UTF-8`、そして古いレガシーな装置からは `UTF-16`……。これらが一つのフォルダに混在しているとき、単純なテキスト操作スクリプトは無残にも文字化けを起こし、壊れたデータを生成してしまいます。

今日は、単なる「ファイルの読み書き」を超えた、プロフェッショナルなデータ統合の技法を伝授しましょう。FileSystemObject (FSO) によるファイル制御と、ADODB.Stream による高度な文字コード変換を多層結合させた「堅牢なパイプライン」の作り方を、丁寧に紐解いていきます。

ここをクリアすれば、あなたは「ただ動くコード」を書く人から、「信頼されるエンジニア」へと進化できるはずですよ。

1. なぜ FSO だけでは足りないのか?(道具の使い分け)

まず、私たちが手にする武器の特性を理解しましょう。ここがエンジニアとしての第一歩です。

FileSystemObject (FSO) の役割

FSOは「ファイルシステムの管理者」です。

  • 「ファイルが存在するか?」
  • 「フォルダの中に何個ファイルがあるか?」
  • 「ファイルを別の場所に移動させる」

といった、ファイルという「器(うつわ)」を操作することに特化しています。非常に高速で強力ですが、ファイルの中身(バイナリデータ)を精密に解析して文字コードを変換する能力は持っていません。

ADODB.Stream の役割

一方で、ADODB.Streamは「データの流れ(ストリーム)の精錬所」です。

  • 「このバイナリを UTF-8 として解釈せよ」
  • 「読み込んだデータを Shift_JIS に変換して書き出せ」

といった、ファイルの中身(データそのもの)を、特定の文字コードとして解釈・変換することに特化しています。

「FSOでファイルを探し出し、ADODB.Streamで中身を洗浄・変換する」
この二つのオブジェクトを連携させることこそが、文字コード混在環境を制する鍵となります。

2. 設計図:文字コード統一パイプラインの構造

バラバラのログを一つにまとめるプロセスは、以下の4つのステップで構成します。

1. 探索 (Discovery): FSOを使って、対象フォルダ内のファイルを一つずつスキャンする。
2. 判定 (Detection): ファイルの先頭数バイト(BOM: Byte Order Mark)を読み取り、文字コードを特定する。
3. 変換 (Transformation): ADODB.Streamを使い、特定した文字コードから「標準形式(例:UTF-8)」へ変換する。
4. 統合 (Consolidation): 変換後のデータを、一つのマスターログファイルへ追記していく。

3. 実践:極限の文字コード統一スクリプト

それでは、実際に動くコードを見ていきましょう。このコードは、実務でそのまま使えるレベルの堅牢さを意識して設計しています。

' ==========================================================================
' ログ文字コード統一パイプライン
' 概要: 混在する Shift_JIS / UTF-8 / UTF-16 を検出し、UTF-8 へ統一して出力する
' ==========================================================================

Option Explicit

' --- 設定エリア ---
Const TARGET_DIR = "C:\Logs\Input"       ' 調査対象のフォルダ
Const OUTPUT_FILE = "C:\Logs\Master_Log.txt" ' 統合後のファイル
Const TARGET_ENCODING = "UTF-8"          ' 最終的な統一形式

' メイン処理の実行
Call Main()

Sub Main()
    Dim fso, folder, file
    Dim targetFSO, streamIn, streamOut
    Dim currentEncoding, filePath
    
    Set fso = CreateObject("Scripting.FileSystemObject")
    
    ' 出力先ファイルを用意(新規作成または上書き)
    ' ADODB.Streamを使用して出力用ストリームを開く
    Set streamOut = CreateObject("ADODB.Stream")
    streamOut.Type = 2 ' adTypeText
    streamOut.Charset = TARGET_ENCODING
    streamOut.Open
    
    ' 入力フォルダの存在確認
    If Not fso.FolderExists(TARGET_DIR) Then
        WScript.Echo "Error: 対象フォルダが見つかりません: " & TARGET_DIR
        Exit Sub
    End If

    Set folder = fso.GetFolder(TARGET_DIR)
    
    WScript.Echo "--- 処理開始 ---"

    ' 1. FSOでフォルダ内をスキャン
    For Each file In folder.Files
        ' ログファイル(.log または .txt)のみを対象とする
        If LCase(fso.GetExtensionName(file.Name)) = "log" Or _
           LCase(fso.GetExtensionName(file.Name)) = "txt" Then
            
            filePath = file.Path
            WScript.Echo "Processing: " & file.Name

            ' 2. 文字コードの判定
            currentEncoding = DetectEncoding(filePath)
            
            ' 3. 変換と書き込み
            ' 判定した文字コードで読み込み、UTF-8として出力ストリームへ書き込む
            Call ConvertAndAppend(filePath, currentEncoding, streamOut)
            
        End If
    Next

    ' 最終的なファイルへの書き出し
    streamOut.SaveToFile OUTPUT_FILE, 2 ' adSaveCreateOverWrite
    streamOut.Close
    
    ' オブジェクトの解放(重要!)
    Set streamOut = Nothing
    Set fso = Nothing
    
    WScript.Echo "--- 処理完了 ---"
    WScript.Echo "出力先: " & OUTPUT_FILE
End Sub

' --------------------------------------------------------------------------
' 関数: DetectEncoding
' 役割: ファイルの先頭バイト(BOM)を解析して文字コードを推定する
' --------------------------------------------------------------------------
Function DetectEncoding(filePath)
    Dim binStream, bom
    Set binStream = CreateObject("ADODB.Stream")
    binStream.Type = 1 ' adTypeBinary
    binStream.Open
    binStream.LoadFromFile filePath
    
    ' 先頭3バイト程度を読み込む
    If binStream.Size >= 3 Then
        binStream.Position = 0
        bom = binStream.Read(3)
        
        ' BOM判定ロジック
        ' UTF-8: EF BB BF
        ' UTF-16 LE: FF FE
        ' UTF-16 BE: FE FF
        If AscB(MidB(bom, 1, 1)) = &HEF And AscB(MidB(bom, 2, 1)) = &HBB And AscB(MidB(bom, 3, 1)) = &HBF Then
            DetectEncoding = "UTF-8"
        ElseIf AscB(MidB(bom, 1, 1)) = &HFF And AscB(MidB(bom, 2, 1)) = &HFE Then
            DetectEncoding = "UTF-16"
        ElseIf AscB(MidB(bom, 1, 1)) = &HFE And AscB(MidB(bom, 2, 1)) = &HFF Then
            DetectEncoding = "UTF-16BE"
        Else
            ' BOMがない場合は、実務上は Shift_JIS と仮定するのが一般的
            DetectEncoding = "Shift_JIS"
        End If
    Else
        DetectEncoding = "Shift_JIS"
    End If
    
    binStream.Close
    Set binStream = Nothing
End Function

' --------------------------------------------------------------------------
' 関数: ConvertAndAppend
' 役割: 特定の文字コードで読み込み、共通ストリームへ追記する
' --------------------------------------------------------------------------
Sub ConvertAndAppend(srcPath, encoding, destStream)
    Dim reader
    On Error Resume Next ' 読み取りエラー(使用中ファイル等)対策
    
    Set reader = CreateObject("ADODB.Stream")
    reader.Type = 2 ' adTypeText
    reader.Charset = encoding
    reader.Open
    reader.LoadFromFile srcPath
    
    If Err.Number <> 0 Then
        WScript.Echo "  [Error] 読み込み失敗: " & Err.Description
        Err.Clear
    Else
        ' ファイルの内容をテキストとして取得
        Dim content
        content = reader.ReadText
        
        ' 出力ストリームに追記(改行を挟んで)
        destStream.WriteText vbCrLf & "--- File: " & srcPath & " ---" & vbCrLf
        destStream.WriteText content
    End If
    
    reader.Close
    Set reader = Nothing
    On Error GoTo 0
End Sub

4. プロフェッショナルのための「極限の知見」解説

このコードをただコピペするだけでは、あなたはまだ半分です。なぜこの書き方をしているのか、その「意図」を理解してください。

① オブジェクトのライフサイクル管理

コードの随所に `Set obj = Nothing` という記述があります。
VBScriptにおいて、ADODB.StreamのようなCOMオブジェクトは、メモリ上に残り続け、時に「ファイルが使用中です」というエラー(ファイルロック)を引き起こす原因になります。
「使い終わったら、即座に解放する」。これは、長時間稼働する自動化ツールにおいて、最も重要な規律です。

② バイナリ・モードによる「真実」の判定

`DetectEncoding` 関数を見てください。ここでは `Type = 1` (adTypeBinary) を使用しています。
文字コードの判定は、テキストとして読み込んではいけません。テキストとして読み込んだ瞬間に、VBScriptが勝手に解釈しようとして、判定そのものが狂ってしまうからです。
「中身を判定する時は、文字としてではなく、生のバイナリ(バイト列)として見る」。これが、文字化けを回避する鉄則です。

③ エラーハンドリングの「戦略的」な使用

`On Error Resume Next` は、使い方を誤ると「エラーを隠蔽するだけの危険な呪文」になります。
しかし、今回のスクリプトのように「特定のファイルが他のプロセスで開かれていて読み込めない」といった、回避不能な外部要因に対しては、エラーを検知してログに残し、次のファイルの処理へ進むための「盾」として活用するのがプロの技です。

最後に:ここをクリアすれば、基本はバッチリですよ

いかがでしたか?
「ファイルを探す(FSO)」と「中身を操る(ADODB.Stream)」を組み合わせ、さらに「バイナリによる正確な判定」を加える。この多層構造を理解できれば、VBScriptによる自動化のレベルは劇的に上がります。

最初は難しく感じるかもしれません。でも、コードの1行1行が「何のために存在しているのか」を意識しながら読み解いていけば、必ず自分のものになります。

一歩ずつ、着実に。
あなたが書くコードが、現場の混乱を鎮める強力な武器になることを願っています。応援していますよ!

タイトルとURLをコピーしました