こんにちは!VBScriptの世界へようこそ。
業務自動化の現場で、レガシーなシステムから出力される「固定長データ(テキストファイルなのに、1行の文字数がピシッと決まっている電文データなど)」を扱ったことはありますか?
「よし、〇文字目から〇文字目を切り出そう!」と、通常の `Mid` 関数を使ってコードを書いたものの、全角文字(日本語)が混ざった瞬間に位置ズレを起こしたり、文字化けして「謎の記号の羅列」に変わってしまったり……。そんな絶望を味わった先輩たちは数知れません。
なぜなら、VBScriptの通常の文字列関数(`Len`や`Mid`)は、「文字数」で数えているのに対し、メインフレームなどの固定長データの世界は「バイト数(メモリ上の大きさ)」で世界が回っているからです。
今回は、Shift-JIS環境における全角・半角の罠を完全に見切り、`ADODB.Stream` と `LenB` / `MidB` 関数を駆使して、1バイトの狂いもなく固定長データを料理する「極限の知見」を授けましょう。ここをクリアすれば、あなたもVBScriptの裏側を支配するマスターに一歩近づきますよ!
—
1. なぜ通常の `Mid` 関数では固定長データに太刀打ちできないのか?
まずは敵を知ることから始めましょう。
VBScriptの標準関数である `Mid` や `Len` は、文字の「個数」を数えます。
- 半角英数(「A」など)も: 1文字 = 1バイト
- 全角文字(「あ」や「亜」など): 1文字 = 2バイト(Shift-JISの場合)
しかし、固定長電文の仕様書はこう書かれています。
> 「1〜10バイト目:顧客コード」「11〜30バイト目:顧客名」
ここで、11文字目に全角文字が混ざると、人間が意図する「文字の何番目か」と、システムが要求する「何バイト目か」が完全にズレてしまいます。結果として、データの切り出し位置がズレて、データベースへの登録エラーや文字化けの嵐を引き起こすのです。
救世主は「B」が付く関数と `ADODB.Stream`
この問題を解決するためには、文字数ではなく「バイト単位」で文字列を操る必要があります。そこで登場するのが以下の3つの武器です。
1. `LenB` / `MidB` 関数: 文字数ではなく「バイト数」で文字列の長さを測り、切り出すVBScriptの秘伝の関数。
2. `ADODB.Stream`: バイナリデータ(バイト配列)を自由自在に読み書きするための、Windows標準の強力なオブジェクト。
—
2. 【実践】文字化けを完全に防ぐ!固定長解析スクリプト
それでは、実際の現場でそのままコピペして使える実用的なスクリプトを見ていきましょう。
今回は、Shift-JISでエンコードされた固定長ファイルを読み込み、バイト単位で正確にデータを切り出すサンプルコードを作成しました。
Option Explicit
‘ ===================================================================
‘ 処理メイン:固定長データのバイト単位切り出しサンプル
‘ ===================================================================
Sub ParseFixedLengthData()
Dim stream
Dim binaryData
Dim byteLength
Dim recordLine
‘ 1. ADODB.Streamオブジェクトの生成(バイナリ・テキスト両対応の万能選手)
Set stream = CreateObject(“ADODB.Stream”)
With stream
.Type = 2 ‘ 2 = adTypeText (テキストデータとして扱う)
.Charset = “Shift_JIS” ‘ レガシーシステムの基本、Shift-JISを指定
.Open
‘ 2. 固定長ファイルを読み込む(文字化けを防ぐためCharsetを明示)
‘ ※あらかじめ同階層に “data.txt” を用意してください
.LoadFromFile “data.txt”
‘ 1行ずつ処理するループ
Do While Not .EOS
recordLine = .ReadText(-2) ‘ -2 = adReadLine (1行読み込み)
If Trim(recordLine) <> “” Then
‘ 3. 核心:バイト単位での切り出しを実行
Call ExtractFields(recordLine)
End If
Loop
.Close
End With
Set stream = Nothing
WScript.Echo “すべての固定長データの解析が完了しました!”
End Sub
‘ ===================================================================
‘ フィールド抽出関数(バイト位置指定版)
‘ ===================================================================
Sub ExtractFields(ByVal lineStr)
Dim custCode, custName, amount
‘ 仕様書の定義(仮の例)
‘ 1~10バイト : 顧客コード (半角前提)
‘ 11~30バイト : 顧客名 (全角混在あり)
‘ 31~40バイト : 金額 (半角数値前提)
custCode = MidB_ShiftJIS(lineStr, 1, 10)
custName = MidB_ShiftJIS(lineStr, 11, 20) ‘ 11バイト目から20バイト分
amount = MidB_ShiftJIS(lineStr, 31, 10)
‘ 結果を出力(確認用)
WScript.Echo “コード: [” & Trim(custCode) & “] ” & _
“名前: [” & Trim(custName) & “] ” & _
“金額: [” & Trim(amount) & “]”
End Sub
‘ ===================================================================
‘ 【重要】Shift-JIS環境下で安全にバイト切り出しを行う自作関数
‘ ===================================================================
Function MidB_ShiftJIS(ByVal targetStr, ByVal startByte, ByVal lengthByte)
Dim adoStream
Dim byteArr
Dim subByteArr()
Dim i, destIndex
‘ VBScript標準のMidBはUnicode(UTF-16)ベースでバイトを数えるため、
‘ Shift-JISの全角文字を正確に扱えないことがある。
‘ そのため、ADODB.Streamで一度Shift-JISのバイト配列に変換して処理する。
Set adoStream = CreateObject(“ADODB.Stream”)
With adoStream
.Type = 2 ‘ adTypeText
.Charset = “Shift_JIS”
.Open
.WriteText targetStr
‘ バイナリモードに切り替えて、Shift-JIS化されたバイト配列を取り出す
.Position = 0
.Type = 1 ‘ adTypeBinary
byteArr = .Read
.Close
End With
‘ VBScriptの配列インデックスは0から始まるため、開始位置を調整 (1始まり -> 0始まり)
Dim sIndex: sIndex = startByte – 1
Dim eIndex: eIndex = sIndex + lengthByte – 1
‘ 配列の範囲内に収まるかチェックしつつ、バイトを切り出す
Dim actualEnd
If eIndex > UBound(byteArr) Then
actualEnd = UBound(byteArr)
Else
actualEnd = eIndex
End If
If sIndex > UBound(byteArr) Then
MidB_ShiftJIS = “”
Exit Function
End If
‘ 必要なバイト数分だけ新しいバイト配列にコピー
ReDim subByteArr(actualEnd – sIndex)
destIndex = 0
For i = sIndex To actualEnd
subByteArr(destIndex) = byteArr(i)
destIndex = destIndex + 1
Next
‘ 切り出したバイト配列を再度Shift-JISとして文字列に戻す
Set adoStream = CreateObject(“ADODB.Stream”)
With adoStream
.Type = 1 ‘ adTypeBinary
.Open
.Write subByteArr
.Position = 0
.Type = 2 ‘ adTypeText
.Charset = “Shift_JIS”
MidB_ShiftJIS = .ReadText
.Close
End With
Set adoStream = Nothing
End Function
‘ 実行のトリガー
Call ParseFixedLengthData()
—
3. コードのキモ:なぜこの実装が最強なのか?
初心者の方が「おっ」と思うポイント、そして現場で絶対にハマる落とし穴を解説します。
① VBScript標準の `MidB` をそのまま信じてはいけない
実は、VBScriptがデフォルトで持っている `MidB` や `LenB` は、文字列を内部で Unicode (UTF-16:1文字=2バイト) として扱った上でのバイト数を返します。
そのため、Shift-JIS特有の「半角1バイト・全角2バイトが混ざった世界」のバイト数とはズレが生じてしまうのです。
今回の自作関数 `MidB_ShiftJIS` では、`ADODB.Stream` を使って一度明示的に Shift-JIS のバイト配列(バイナリ)に変換してから、配列のインデックス(何番目のバイトか)を指定して切り出しています。これにより、環境依存の文字化けや位置ズレを完全になくすことに成功しています。
② オブジェクトのライフサイクル管理の美学
エンジニアとしてのこだわりがここに出ます。
`ADODB.Stream` は非常に強力ですが、メモリ上にストリームを開放・閉じる(`Open` / `Close`)のライフサイクルを適切に管理しないと、ループ処理の回数が多いときにメモリリーク(メモリの継ぎ足しによるリソース枯渇)を引き起こします。
今回のコードでは、関数内で使い捨てのストリームを生成し、処理が終わったら即座に閉じて `Nothing` を代入するという、ガベージコレクタに優しい堅牢な設計にしています。
—
まとめ:ここをクリアすれば、VBScriptの基本はバッチリです!
今回は、VBScriptと `ADODB.Stream` を組み合わせて、全角・半角が混ざる難しい固定長データを文字化けせずにバイト単位で切り出すテクニックを解説しました。
- 通常の `Mid` は「文字数」、固定長データは「バイト数」で考える。
- Shift-JISの正確なバイト切り出しには `ADODB.Stream` によるバイナリ変換が最強の武器。
- オブジェクトの `Open` と `Close`(メモリ管理)の作法を忘れない。
この壁を突破できたあなたなら、レガシーシステムの自動化や、面倒なCSV/固定長コンバートのバッチ処理も怖くないはずです。
業務自動化の現場で、ぜひこの知見を役立ててくださいね。それでは、次のステップへ進みましょう!
