【データ整形バッチ】FSOで固定長テキストファイルを読み込みTSV形式へ変換・パースする泥臭く役立つデータ加工術
開発現場でレガシーな基幹システムと向き合うとき、避けて通れないのが「固定長テキストファイル」の存在だ。
「ヘッダーの10桁から25桁までは顧客名、その次から8桁は計上日……」
こうした仕様書片手に、夜な夜なExcelでデータを手加工したり、場当たり的なマクロで処理して文字化けや桁ズレの爆弾を踏み抜いたりした経験はないだろうか。
今回は、VBScriptのFile System Object(以下、FSO)と `Mid` 関数を駆使し、大規模な固定長データを一瞬で、かつ絶対に破綻しない堅牢性を持ってTSV形式へ変換するプロダクションコードを授与する。
単なる「動くコード」ではない。実務の現場で発生する「巨大ファイルのメモリバースト」「文字コードの罠」「レイアウト変更への耐性」をすべて考慮し尽くした、プロのアーキテクトの設計思想を共有しよう。
—
なぜ「readline」と「Mid関数」の組み合わせが最強なのか
世の中には、正規表現や外部ライブラリを持ち込んでテキスト解析を複雑化させるエンジニアがいる。しかし、業務自動化の領域においてVBScriptが選ばれる理由は「環境を選ばない圧倒的な軽さとシンプルさ」だ。
固定長データのパースにおいて、FSOの `OpenTextFile` でストリームを開き、`ReadLine` で1行ずつメモリ上に展開しながら `Mid` 関数(またはバイト単位の処理)で切り出す手法は、以下の圧倒的なアドバンテージを持つ。
1. メモリ効率の最適化:数GBに及ぶ巨大な固定長ファイルであっても、1行ずつ処理(ストリーミング処理)することでメモリ消費量を一定に抑えられる。
2. 実行環境の依存性ゼロ:追加のDLLやサードパーティ製ライブラリは一切不要。Windows 7から最新のWindows 11まで、OS標準機能だけで完結する。
3. 可読性と保守性の高さ:仕様書の「桁数」とコードの引数が1対1で対応するため、後任のプログラマが絶望しない。
—
堅牢なデータ加工バッチ設計の3大鉄則
プロダクション環境で動かすバッチを書く際、以下の設計思想を外すと必ず痛い目をみる。
1. 文字コード(Shift-JIS / UTF-8)の明示的制御
FSOの `OpenTextFile` の第3引数(Tristate)を甘く見てはならない。基幹システムからの出力は、いまだにShift-JIS(CP932)であることが多い。文字コードの指定を怠ると、環境依存文字や半角カタカナが盛大に文字化けする。
2. 厳格なエラーハンドリング(On Error Resume Nextの罠)
「VBScriptだからエラーは無視して進めよう」という安易な実装は、不正なデータ行に出くわした瞬間にサイレントエラーを引き起こし、DBやExcelに取り込んだ後で大惨事を引き起こす。処理の成否をログに吐き、異常系を検知できる構造が必須である。
3. 可変長レイアウトへの備え(トリム処理)
固定長データは、指定桁数に満たない場合や余白にスペース(半角/全角)がパディングされている。これをそのままTSVに流すと、後続のExcelやSQLクエリで「余計な空白スペース」に悩まされることになる。必ず `Trim`(または独自トリム関数)を通すこと。
—
【実装コード】プロダクションレベル・固定長toTSV変換バッチ
以下のコードをテキストエディタに貼り付け、拡張子を `.vbs` として保存してほしい。実務でそのまま組み込める堅牢性を備えた完全版だ。
‘ ==============================================================================
‘ Script Name : FixedLengthToTSV.vbs
‘ Description : 固定長テキストファイルを読み込み、指定レイアウトに基づきTSVへ変換する
‘ Author : Chief Architect
‘ ==============================================================================
Option Explicit
‘ — 設定エリア —
Dim strSourceFile, strTargetFile
strSourceFile = “C:\Data\input_sample.txt” ‘ 読み込む固定長ファイル
strTargetFile = “C:\Data\output_result.tsv” ‘ 出力先TSVファイル
‘ 文字コード設定 (TristateConstants)
‘ -2: システムのデフォルト, 0: ASCII, -1: Unicode (UTF-16), 3: UTF-8 (FSO標準ではUTF-8直接指定が不可な場合があるためADODB.Streamを推奨するが、今回は標準FSOでのShift-JIS想定)
Const TristateUseDefault = -2
Const ForReading = 1
Const ForWriting = 2
Call Main()
Sub Main()
Dim objFSO, objFileIn, objFileOut
Dim strLine, intLineCount
Dim timeStart
timeStart = Timer
intLineCount = 0
‘ FSOの生成
Set objFSO = CreateObject(“Scripting.FileSystemObject”)
‘ 入力ファイルの存在確認
If Not objFSO.FileExists(strSourceFile) Then
WScript.Echo “【エラー】入力ファイルが存在しません: ” & strSourceFile
Exit Sub
End If
On Error Resume Next
‘ 入力ストリームを開く (Shift-JIS前提のためTristateUseDefaultを使用)
Set objFileIn = objFSO.OpenTextFile(strSourceFile, ForReading, False, TristateUseDefault)
If Err.Number <> 0 Then
WScript.Echo “【エラー】入力ファイルを開けませんでした: ” & Err.Description
Exit Sub
End If
‘ 出力ストリームを開く (新規作成・上書き)
Set objFileOut = objFSO.CreateTextFile(strTargetFile, True, False)
If Err.Number <> 0 Then
WScript.Echo “【エラー】出力ファイルを作成できませんでした: ” & Err.Description
objFileIn.Close
Exit Sub
End If
On Error GoTo 0 ‘ エラー監視を戻す
‘ — ヘッダー行の書き込み (必要な場合) —
objFileOut.WriteLine “顧客ID” & vbTab & “顧客名” & vbTab & “計上日” & vbTab & “金額” & vbTab & “備考”
‘ — メインループ (1行ずつストリーミング処理) —
Do While Not objFileIn.AtEndOfStream
strLine = objFileIn.ReadLine
intLineCount = intLineCount + 1
‘ 空行スキップ
If Trim(strLine) <> “” Then
‘ パース処理を実行してTSV行を生成
Dim strParsedLine
strParsedLine = ParseFixedLength(strLine, intLineCount)
If strParsedLine <> “” Then
objFileOut.WriteLine strParsedLine
End If
End If
Loop
‘ クリーンアップ
objFileIn.Close
objFileOut.Close
Set objFileIn = Nothing
Set objFileOut = Nothing
Set objFSO = Nothing
WScript.Echo “処理が正常に完了しました。” & vbCrLf & _
“処理行数: ” & intLineCount & ” 行” & vbCrLf & _
“実行時間: ” & FormatNumber(Timer – timeStart, 2) & ” 秒” & vbCrLf & _
“出力先: ” & strTargetFile
End Sub
‘ ==============================================================================
‘ 関数名 : ParseFixedLength
‘ 概要 : 1行分の固定長文字列を定義された桁数で切り出し、TSV形式の文字列に結合する
‘ 戻り値 : タブ区切りされた文字列
‘ ==============================================================================
Function ParseFixedLength(ByVal lineStr, ByVal lineNum)
Dim col1, col2, col3, col4, col5
‘ 安全装置:行の長さが最低限必要な桁数に満たない場合のガード
‘ 例:全体で最低50文字必要なレイアウトの場合
If Len(lineStr) < 50 Then
' ログを出力してスキップ、または空文字を返す等の業務要件に応じたハンドリング
' ここでは簡易的にパース可能なところまで、あるいは空白埋めで処理
End If
' 【レイアウト定義】
' Mid(文字列, 開始位置, 文字数)
' ※VBScriptのMidは1文字目からスタート
col1 = Trim(Mid(lineStr, 1, 8)) ' 顧客ID (1~8桁目)
col2 = Trim(Mid(lineStr, 9, 20)) ' 顧客名 (9~28桁目)
col3 = Trim(Mid(lineStr, 29, 8)) ' 計上日 (29~36桁目)
col4 = Trim(Mid(lineStr, 37, 10)) ' 金額 (37~46桁目)
col5 = Trim(Mid(lineStr, 47, 30)) ' 備考 (47~76桁目)
' 数値項目のフォーマット調整やバリデーションが必要な場合はここで実施
' 例: 金額の先頭0トリムなど
col4 = CStr(CLng("0" & col4)) ' 数値化して再文字列化(安全な0トリム)
' タブ文字で結合して返却
ParseFixedLength = col1 & vbTab & col2 & vbTab & col3 & vbTab & col4 & vbTab & col5
End Function
---
アーキテクトからの実践的アドバイス
1. 文字コードが「完全なUTF-8(BOMなし)」を要求される場合
前述のコードで使用している標準FSOの `OpenTextFile` は、UTF-8の読み書きにおいて挙動が不安定になることがある。もし基幹システムやクラウドストレージ連携の要件で「完全なUTF-8」を指定された場合は、FSOではなく `ADODB.Stream` オブジェクト を採用せよ。
これにより、文字コードの指定(`Charset = “UTF-8″`)を完全にコントロールできる。
2. 「レイアウト変更」に強い設計にする秘訣
仕様変更はエンジニアの宿命である。「○桁目が変更になりました」と言われるたびに `Mid(lineStr, 37, 10)` の数字をすべて書き直すのは素人のやることだ。
大規模な案件であれば、開始位置と長さを別ファイルの外部設定(INIファイルやJSON、あるいは別定義シート)から読み込む構造(メタデータ駆動型アーキテクチャ)に昇華させるのが、シニアエンジニアの矜持というものである。
—
結論
固定長テキストからTSVへの変換は、地味で泥臭い作業に見えるかもしれない。しかし、ここに挙げた「堅牢なストリーミング処理」「適切なエラーハンドリング」「余白のトリム」といったエンジニアリングの基本を徹底することで、夜間バッチのトラブルや、それに伴う無駄なアラート対応をゼロにすることができる。
道具は古くとも、使い手次第で最高峰のパフォーマンスを発揮する——それがVBScriptとFSOの真価である。明日の業務自動化から、ぜひこの設計思想を取り入れてみてほしい。
