【実務・中級編】【実務中級者向け】Shift_JIS非対応の環境依存文字(機種依存文字)をFSO読み込み時に検知・無害化置換するバッチ – VBScript (Visual Basic Scripting Edition)解析バイブル

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

データの「死」を防げ。VBScriptにおける機種依存文字の検知と、堅牢なデータクレンジング設計論

業務自動化の現場で、多くの開発者が「動けばいい」という甘い考えに基づき、致命的なバグを仕込んでいる。

その代表例が、ファイル読み込み時の文字コード管理と、機種依存文字(環境依存文字)への無策だ。

「自分の環境では動いた」「CSVをExcelで開いたら文字化けしていた」「DBへのインサートでエラーが出た」――これらはすべて、設計段階で「データの不純物」を想定できていない、プロフェッショナルとは言い難い実装の成れの果てである。

本稿では、VBScriptを用いたファイル操作において、Shift_JIS非対応の文字(丸数字、特殊記号、旧字体など)がシステム全体を破壊する前に、いかにして検知し、安全に無害化(Sanitize)するか。その極限の設計思想と、プロダクションレベルのコードを伝授する。

1. なぜ、単なる `FileSystemObject` の読み込みでは不十分なのか

VBScriptにおける標準的な `FileSystemObject` (FSO) の `OpenTextFile` メソッドは、非常に便利だ。しかし、致命的な弱点がある。それは、「未知の文字」に遭遇した際の挙動が、環境や設定に依存し、かつ破壊的であるという点だ。

Shift_JIS(CP932)の範囲外にある文字(例:①、㈱、髙など)を、Shift_JISとして読み込もうとした瞬間、多くのシステムではその文字を `?`(不確定文字)に置換してしまう。一度 `?` になったデータは、二度と元の値には戻せない。これはデータの「死」を意味する。

設計における鉄則

1. 「読み込み」と「変換」を分離せよ: 読み込み時は、可能な限りUnicode(UTF-8/UTF-16)としてメモリ上に展開し、データの欠損を防ぐ。
2. 「検知」と「置換」をブラックリスト方式で行え: 何が「正しい文字」かを定義するのは困難だ。それよりも、「何がシステムを壊すか」という「危険文字リスト」を定義し、それらを安全な代替文字へマッピングする設計をとるべきである。

2. アーキテクチャ設計:データクレンジング・パイプライン

堅牢なスクリプトを構築するためには、以下の3層構造を意識せよ。

1. I/O層 (Input/Output Layer): `ADODB.Stream` を用い、文字化けを防ぎながらバイナリ/テキストとして安全にロードする。
2. Sanitization層 (Cleaning Layer): `Scripting.Dictionary` を用いた高速なマッピングエンジン。ここで機種依存文字を検知し、代替文字へ置換する。
3. Business層 (Logic Layer): クレンジング済みの「純粋なデータ」のみを受け取り、後続の処理(DB連携やファイル出力)を行う。

3. プロダクション・コード:堅牢なデータクレンジング実装例

以下に、実務でそのまま利用可能な、保守性の高いスクリプト例を示す。このコードは、FSOではなく、より高度な文字コード制御が可能な `ADODB.Stream` を核としている。

' ==============================================================================
' Script Name: DataSanitizer_Pro.vbs
' Description: 機種依存文字を検知し、安全な文字へ置換するデータクレンジングツール
' Author: Chief Architect
' ==============================================================================
Option Explicit

' --- 設定領域 ---
Const ENCODING_UTF8 = "UTF-8"
Const ENCODING_SJIS = "shift_jis"

' メイン実行プロセス
Call Main()

Sub Main()
    Dim inputFilePath, outputFilePath
    inputFilePath  = "C:\Temp\input_data.csv"  ' 入力ファイルパス
    outputFilePath = "C:\Temp\output_cleansed.csv" ' 出力ファイルパス

    On Error Resume Next
    
    ' 1. クレンジング処理の実行
    Call ProcessFile(inputFilePath, outputFilePath)

    If Err.Number <> 0 Then
        WScript.Echo "CRITICAL ERROR: " & Err.Description
        WScript.Quit 1
    End If

    WScript.Echo "SUCCESS: Data cleansing completed."
    On Error GoTo 0
End Sub

' ファイル処理のコアロジック
Sub ProcessFile(strInPath, strOutPath)
    Dim objStreamIn, objStreamOut
    Dim strContent, strSanitizedContent
    Dim dictMap

    ' 2. 置換用マッピング辞書の生成(ここをメンテナンスする)
    Set dictMap = CreateMappingDictionary()

    ' 3. ADODB.StreamによるUnicode読み込み(文字化け防止)
    Set objStreamIn = CreateObject("ADODB.Stream")
    objStreamIn.Type = 2 ' adTypeText
    objStreamIn.Charset = "UTF-8"
    objStreamIn.Open
    objStreamIn.LoadFromFile strInPath

    strContent = objStreamIn.ReadText
    objStreamIn.Close

    ' 4. Sanitization(無害化処理)
    strSanitizedContent = SanitizeString(strContent, dictMap)

    ' 5. 結果の書き出し(後続システムが要求するShift_JIS形式で出力)
    Set objStreamOut = CreateObject("ADODB.Stream")
    objStreamOut.Type = 2 ' adTypeText
    objStreamOut.Charset = ENCODING_SJIS
    objStreamOut.Open
    objStreamOut.WriteText strSanitizedContent
    objStreamOut.SaveToFile strOutPath, 2 ' adSaveCreateOverWrite
    objStreamOut.Close

    Set objStreamIn = Nothing
    Set objStreamOut = Nothing
    Set dictMap = Nothing
End Sub

' 文字列の無害化エンジン
' @param strInput: 元の文字列
' @param dictMap: 置換ルールを格納したDictionary
' @return: クレンジング済みの文字列
Function SanitizeString(strInput, dictMap)
    Dim key
    Dim strResult
    strResult = strInput

    ' Dictionaryのキー(危険文字)を走査し、一括置換
    ' 複雑な正規表現よりも、明示的なマッピングの方が保守性が高い
    For Each key In dictMap.Keys
        strResult = Replace(strResult, key, dictMap(key))
    Next

    SanitizeString = strResult
End Function

' 置換ルール(ブラックリスト)の定義
' ここに「システムが受け付けられない文字」を追加していく
Function CreateMappingDictionary()
    Dim dict
    Set dict = CreateObject("Scripting.Dictionary")

    ' 丸数字の置換例
    dict.Add "①", "(1)"
    dict.Add "②", "(2)"
    dict.Add "③", "(3)"
    dict.Add "④", "(4)"
    dict.Add "⑤", "(5)"
    
    ' 特殊記号・単位の置換例
    dict.Add "㈱", "(Kabushiki)"
    dict.Add "㈲", "(Yugen)"
    dict.Add "㍉", "mm"
    dict.Add "㌔", "kg"
    dict.Add "㌘", "g"
    dict.Add "㍉", "mm"
    
    ' 現場で頻発する「環境依存文字」の追加
    ' ※ 実際の実務では、エラーログから収集した文字をここに追加していく
    
    Set CreateMappingDictionary = dict
End Function

4. 実務における「プロの作法」:運用上の注意点

このスクリプトをプロダクション環境に投入する際、君たちが守るべきルールが3つある。

① 辞書(Mapping Dictionary)の継続的アップデート

「一度作れば終わり」という考えは捨てろ。システム連携でエラーが出た際、そのエラーの原因となった文字を特定し、即座に `CreateMappingDictionary` に追加するプロセスを運用フローに組み込め。

② ログ出力の設計

上記のコードでは簡略化しているが、実務では「どの文字が、どの行で、どの値から置換されたか」をログに残すべきだ。これがないと、データが書き換わったこと自体に気づかず、後続の業務で「データが意図せず変わっている」というクレームに繋がる。

③ パフォーマンスの考慮

ファイルサイズが数GBに及ぶ場合、`strContent = objStreamIn.ReadText` による一括読み込みはメモリ不足(Out of Memory)を引き起こす。その場合は、行単位(`ReadLine`)で読み込み、ストリーム処理を行う設計へ昇格させよ。

結論

VBScriptは古い言語だ。しかし、その特性(メモリ管理や文字コードの扱い)を深く理解し、適切にコントロール下に置くことができれば、極めて強力な武器になる。

「文字化け」や「システムエラー」を、単なる不運として片付けるな。それは、君たちの設計がデータのライフサイクルを掌握できていないことの証明である。

設計に魂を込めろ。コードに責任を持て。それがプロフェッショナルだ。

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