データの「死」を防げ。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は古い言語だ。しかし、その特性(メモリ管理や文字コードの扱い)を深く理解し、適切にコントロール下に置くことができれば、極めて強力な武器になる。
「文字化け」や「システムエラー」を、単なる不運として片付けるな。それは、君たちの設計がデータのライフサイクルを掌握できていないことの証明である。
設計に魂を込めろ。コードに責任を持て。それがプロフェッショナルだ。
