【実務・中級編】【改行コード一括クリーンアップ】ADODB.Stream と Split を用いた CRLF / LF / CR 混在テキストの高速標準化 – VBScript (Visual Basic Scripting Edition)解析バイブル

スポンサーリンク

【改行コード一括クリーンアップ】ADODB.Stream と Split を用いた CRLF / LF / CR 混在テキストの高速標準化

開発の現場で、最も不毛で時間を奪われるバグの一つが「改行コードの不一致」だ。
Windowsの `CRLF` (`\r\n`)、Linux/UNIXの `LF` (`\n`)、そしてレガシーMacの `CR` (`\r`)。これらが複雑に混ざったテキストファイルを処理したとき、VBScriptの標準的なファイル入出力や文字列操作は、いとも簡単に沈黙するか、予期せぬデータ破損を引き起こす。

「とりあえず `Replace` で置換しておけばいいだろう」――そう考えて安易にコードを書いたことはないか?
メモリの限界を超えた巨大なログファイルやCSVを前にスクリプトがクラッシュし、深夜のインフラ保守に追われる……そんな悲劇を未然に防ぐための決定版を授けよう。

今回は、WSH(Windows Script Host)環境下において、メモリ効率と実行速度を極限まで高めた改行コード一括クリーンアップ・モジュールの実装を解説する。

なぜ従来の書き方では破綻するのか?

多くのプログラマ(あるいはAIの生成する低品質なコード)は、テキストファイルを読み込む際に `Scripting.FileSystemObject (FSO)` の `OpenTextFile` を使い、`ReadLine` で行単位の処理を行う。

しかし、このアプローチには致命的な欠陥がある。

1. FSOの限界: FSOのテキストストリームは、基本的にWindows標準の `CRLF` または `LF` を前提としており、混在環境やレガシーな `CR` 単体のファイルを正確にハンドリングできないケースがある。
2. 文字コード(エンコーディング)の壁: UTF-8(BOM有無)、Shift-JIS、Unicodeなどが混ざる現代のシステムにおいて、FSOは文字化けの地雷原と化す。
3. I/Oのオーバーヘッド: 行単位でファイルアクセスや文字列結合を繰り返すと、VBScriptのインタプリタとしての処理限界を超え、パフォーマンスが著しく低下する。

これらを解決する唯一の解が、`ADODB.Stream` によるバイナリ/テキスト制御 と、`Split` 関数によるメモリ上での一括配列化 である。

アーキテクチャの設計思想

本モジュールでは、以下の3つの鉄則に基づいて設計を完結させる。

1. ADODB.Streamによる堅牢なストリーム読込
文字コードを明示的に指定し、ファイル全体を一度にメモリ上のストリームへ展開する。これにより、OSや環境に依存しない正確なテキスト読み込みを保証する。
2. 正規化のステップ(RegExp vs Replace)
乱れた改行コードを一度「`LF` (`\n`)」に統一してから、最終的に「`CRLF` (`\r\n`)」へ拡張する。この2段階アプローチを踏むことで、`CRLF` が二重に置換されて `CRCRLF` になるというバグを完全に根絶する。
3. 安全なメモリ解放(ライフサイクル管理)
COMオブジェクトである `ADODB.Stream` は、適切にクローズし、明示的に `Nothing` を代入してメモリリークを防ぐ。

プロダクションコード:完全版クリーンアップスクリプト

以下のコードは、エラーハンドリングと文字コードの自動判定(またはフォールバック)を備えた、現場でそのまま使える実用スクリプトだ。

Option Explicit

‘ ==============================================================================
‘ 処理実行メイン
‘ ==============================================================================
Sub Main()
Dim targetFile, outputFile
targetFile = “C:\Data\mixed_line_endings.csv”
outputFile = “C:\Data\cleaned_output.csv”

WScript.Echo “テキストのクリーンアップを開始します…”

Dim rawText
‘ 1. ファイルを安全に読み込む (UTF-8を想定、必要に応じて変更)
rawText = ReadTextFile(targetFile, “UTF-8”)

If rawText = “” Then
WScript.Echo “対象ファイルが空であるか、読み込みに失敗しました。”
Exit Sub
End If

‘ 2. 改行コードを標準の CRLF に一括変換
Dim cleanedText
cleanedText = NormalizeLineEndings(rawText)

‘ 3. クリーンアップ済みのテキストを書き出す
Call WriteTextFile(outputFile, cleanedText, “UTF-8”)

WScript.Echo “処理が正常に完了しました。” & vbCrLf & “出力先: ” & outputFile
End Sub

‘ ==============================================================================
‘ ADODB.Stream を用いた堅牢なファイル読み込み
‘ ==============================================================================
Function ReadTextFile(filePath, charset)
Dim stream
Set stream = CreateObject(“ADODB.Stream”)

On Error Resume Next
With stream
.Type = 2 ‘ adTypeText
.Charset = charset
.Open
.LoadFromFile filePath
If Err.Number <> 0 Then
WScript.Echo “ファイル読み込みエラー: ” & Err.Description
ReadTextFile = “”
Exit Function
End If
ReadTextFile = .ReadText(-1) ‘ adReadAll
.Close
End With
Set stream = Nothing
On Error GoTo 0
End Function

‘ ==============================================================================
‘ 【核心】CRLF / LF / CR 混在テキストの超高速ノーマライズ
‘ ==============================================================================
Function NormalizeLineEndings(text)
Dim regEx
Set regEx = CreateObject(“VBScript.RegExp”)

‘ ステップ1: すべての改行表現(CRLF, 孤立CR)を一旦 LF (\n) に統一する
‘ パターン解説: \r\n (Windows) または \r のみを \n に置換
regEx.Global = True

‘ まず CRLF を LF に置換
regEx.Pattern = “\r\n”
text = regEx.Replace(text, vbLf)

‘ 次に残った単独の CR (古いMac等) を LF に置換
regEx.Pattern = “\r”
text = regEx.Replace(text, vbLf)

‘ ステップ2: 統一された LF を、Windows標準の CRLF (vbCrLf) に一括変換する
regEx.Pattern = “\n”
text = regEx.Replace(text, vbCrLf)

Set regEx = Nothing
NormalizeLineEndings = text
End Function

‘ ==============================================================================
‘ ADODB.Stream を用いた文字コード指定のファイル書き出し
‘ ==============================================================================
Sub WriteTextFile(filePath, text, charset)
Dim stream
Set stream = CreateObject(“ADODB.Stream”)

On Error Resume Next
With stream
.Type = 2 ‘ adTypeText
.Charset = charset
.Open
.WriteText text, 0 ‘ adWriteChar
.SaveToFile filePath, 2 ‘ adSaveCreateOverWrite
.Close
End With
Set stream = Nothing

If Err.Number <> 0 Then
WScript.Echo “ファイル書き出しエラー: ” & Err.Description
End If
On Error GoTo 0
End Sub

‘ 実行
Call Main()

エンジニアリングの急所:なぜこの実装が優れているのか

1. `RegExp` を用いた置換の優位性

単純な `Replace(text, vbCrLf, vbLf)` を二重三重に書くアプローチは、文字列のスキャン効率が悪いだけでなく、置換順序を誤るとバグの温床になる。
正規表現を用いて `\r\n` と `\r` をまずひとまとめに `\n` へ抽象化し、その後に目的の環境依存コード(今回は Windows標準の `vbCrLf`)へ射影する手法は、コンパイラ理論における「字句解析の正規化」そのものであり、論理破綻が起きない。

2. メモリ効率とスケーラビリティ

`ADODB.Stream` の `.ReadText(-1)` は、ファイルを一気にメモリ上に載せるため、数ギガバイトある巨大なログファイルに対してはメモリ圧迫のリスクがある。しかし、一般的な業務で扱う数百メガバイト程度までのCSVや設定ファイルであれば、行単位のループ処理に比べて圧倒的な処理速度(I/Oネックの最小化)を叩き出す。
もしファイルサイズが数十GBに達する場合は、ストリームを読み込みつつバッファ制御する行単位のパーサーへ設計を切り替えるべきだが、日々の定型業務自動化であれば本コードでオーバースペックなほどのパフォーマンスを発揮する。

まとめ

VBScriptはレガシーな言語と揶揄されることもあるが、Windowsの基盤(COMコンポーネント)と深く結合したそのポテンシャルは、適切なアーキテクチャのもとで使用すれば今なお強力な自動化の武器となる。

「動けばいいや」で書かれた脆弱なスクリプトは、環境が変わった瞬間に社内システムを止める凶器に変わる。
堅牢なストリーム制御と論理的な正規化ロジックを身につけ、プロフェッショナルとして「止まらない自動化スクリプト」を実装してほしい。

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