【入門編】【メモリ削減】AtEndOfStreamを活用した大容量テキストファイルの1行ずつ高速読み込み処理 – VBScript (Visual Basic Scripting Edition)解析バイブル

スポンサーリンク

こんにちは!VBScriptの世界へようこそ。
業務自動化の現場で、エクセルマクロの限界を超えたファイル処理を任されると、必ずと言っていいほどぶhesitateするのが「大容量テキストファイルの扱いに伴うメモリ不足エラー」です。

「数GBもある巨大なログファイルを読み込んだ途端、スクリプトがフリーズする、あるいは強制終了してしまう……」
そんな絶望的な状況に直面したことはありませんか?

今回は、VBScriptのFileSystemObject(FSO)における最大の武器の一つである、`AtEndOfStream`を活用した「1行ずつ高速読み込み(ストリーミング処理)」の極意を伝授します。ここをクリアすれば、あなたもメモリの呪縛から解放され、どんなに巨大なファイルでも軽々とさばけるプロの仲間入りです。一緒にマスターしていきましょう!

—

1. なぜ「ReadAll」は大容量ファイルに向かないのか?

まずは、多くの初学者がやりがちな「NGパターン」からお話しします。

テキストファイルの内容を丸ごとメモリ上に読み込もうとするとき、私たちはよく `ReadAll` メソッドを使いがちです。

‘ 【NGパターン】数GBのファイルでこれをやるとメモリが爆発します
Set fso = CreateObject(“Scripting.FileSystemObject”)
Set ts = fso.OpenTextFile(“C:\Logs\huge_server.log”, 1)

‘ ファイル全体を一度に変数に読み込む(ここでメモリを大量消費!)
fileContent = ts.ReadAll

ts.Close

メモリ上で何が起きているのか?

`ReadAll` は、ファイルの最初から最後までを一瞬にしてすべてメモリ(RAM)上に展開します。
もし対象のファイルが 2GB あったとしたらどうでしょう? WindowsのVBScript実行環境(WScript.host)は32ビットプロセスの制限を受けることが多く、OSの仮想メモリを圧迫した挙句、「メモリ不足です (Out of memory)」 というエラーを吐いて無慈悲にクラッシュします。

業務自動化エンジニアとして、ファイルサイズが将来的にどれだけ膨らんでも耐えうる設計にしておくこと――これがプロの条件です。

—

2. 解決策:AtEndOfStreamによる「ストリーミング処理」

そこで登場するのが、ストリーミング処理(流し読み)です。
水道の蛇口をイメージしてください。コップ一杯ずつ水をすくうように、ファイルも「1行ずつ読み込んでは処理し、次の行へ進む」を繰り返せば、メモリ上にファイル全体を保持する必要がなくなります。

ここで主役になるのが、TextStreamオブジェクトの `AtEndOfStream` プロパティ です。

  • `AtEndOfStream` とは?

ファイルポインタ(現在の読み込み位置)がファイルの末尾(End of Stream)に到達したかどうかを `True` または `False` で返すブール値のプロパティです。

全体像のイメージ図

[巨大なファイル]
├─ 1行目読込 ➔ 処理 ➔ 捨てて次へ
├─ 2行目読込 ➔ 処理 ➔ 捨てて次へ
├─ 3行目読込 ➔ 処理 ➔ 捨てて次へ
└─ (ファイル終端へ到達 ➔ AtEndOfStream が True になりループ脱出)

これなら、ファイルが何ギガバイトあろうとも、メモリ消費量は「たった1行分」だけで済むため、エラーとは無縁の安定稼働を実現できます。

—

3. 実装コード:そのまま使えるコピペテンプレート

それでは、実際の現場で即戦力となる実用的なコードを見ていきましょう。
今回は、巨大なログファイルの中から特定のキーワード(例: “ERROR”)を含む行だけを抽出し、別のファイルに書き出す処理を書いてみます。

デスクトップなどに保存して、すぐにテスト実行可能です。

‘ =================================================================0
‘ 脚本名: StreamReadLargeLog.vbs
‘ 概要: AtEndOfStreamを用いた大容量テキストファイルの高速行単位処理
‘ =================================================================0
Option Explicit

Sub ProcessLargeFile()
Dim fso, targetPath, outputPath, tsRead, tsWrite
Dim lineCount, errorCount, currentLine
Dim keyword

‘ 読み込み先と書き出し先のパス設定
targetPath = “C:\Logs\huge_server.log” ‘ 読み込む巨大ファイル
outputPath = “C:\Logs\extracted_errors.txt” ‘ 抽出結果の出力先
keyword = “ERROR”

‘ FileSystemObjectの生成
Set fso = CreateObject(“Scripting.FileSystemObject”)

‘ ファイルが存在するかチェック
If Not fso.FileExists(targetPath) Then
WScript.Echo “エラー: 対象ファイルが見つかりません -> ” & targetPath
Exit Sub
End If

‘ TextStreamオブジェクトを開く(第2引数 “1” は ForReading)
Set tsRead = fso.OpenTextFile(targetPath, 1, False)
‘ 書き出し用ファイルを開く(第2引数 “2” は ForWriting, 第3引数 “True” は新規作成許可)
Set tsWrite = fso.CreateTextFile(outputPath, True)

lineCount = 0
errorCount = 0

WScript.Echo “処理を開始します…”

‘ 【核心】ファイル終端(AtEndOfStream)に達するまでループし続ける
Do Until tsRead.AtEndOfStream
‘ 1行だけ読み込む
currentLine = tsRead.ReadLine
lineCount = lineCount + 1

‘ 条件判定(キーワードが含まれているか)
If InStr(currentLine, keyword) > 0 Then
‘ 抽出結果を新しいファイルに書き出す
tsWrite.WriteLine currentLine
errorCount = errorCount + 1
End If

‘ 10万行ごとに進捗をコンソールに出力(数百万行ある場合のフリーズ対策の安心設計)
If lineCount Mod 100000 = 0 Then
WScript.Echo lineCount & ” 行目を処理中…”
End If
Loop

‘ リソースの解放(オブジェクトのライフサイクル管理)
tsRead.Close
tsWrite.Close
Set tsRead = Nothing
Set tsWrite = Nothing
Set fso = Nothing

WScript.Echo “処理が完了しました!” & vbCrLf & _
“総走査行数: ” & lineCount & ” 行” & vbCrLf & _
“抽出行数: ” & errorCount & ” 行”
End Sub

‘ 処理の実行
ProcessLargeFile()

—

4. コードの深掘りとエンジニアの知見(ここが重要!)

上記のコードには、現場で長く生き残るための「エンジニアの知見」がいくつも詰まっています。ポイントを解説します。

① `ReadLine` との組み合わせが必須

`AtEndOfStream` は、文字通り「ストリーム(流れるデータ)の終わり」を監視しています。これ単体では意味がなく、必ず `ReadLine` メソッドとセットで使うことで、ファイルポインタを1行ずつ確実に進めることができます。`ReadLine` を忘れると、永遠に同じ1行目を読み続ける「無限ループ地獄」に陥るので注意してください。

② 明示的なクローズとメモリ解放(ライフサイクルの管理)

VBScriptはガベージコレクションを備えていますが、ファイルや外部リソース(FSOなど)を扱うオブジェクトは、スクリプトが終了するまでメモリやファイルロックを保持し続けることがあります。
処理が終わったら、
1. `tsRead.Close` / `tsWrite.Close` でファイルを閉じる(ロックを解除する)
2. `Set tsRead = Nothing` でメモリ上の参照を明示的に切り離す
この作法を徹底することが、安定稼働するスクリプトの絶対条件です。

—

5. よくあるつまずきポイント(トラブルシューティング)

  • Q. 実行すると「ファイルが見つかりません (Error 53)」と言われる
  • A. パスの指定ミスや、ファイル名に全角スペースや特殊文字が含まれていないか確認してください。また、他のアプリケーション(エディタなど)がそのファイルを排他ロックしている場合もエラーになります。
  • Q. 処理が途中で止まってしまったように感じる
  • A. 数千万行規模のファイルになると、どうしても物理的なディスクの読み込み速度に依存します。コード内の `lineCount Mod 100000` のように、定期的に進捗をログに出す工夫をしておくと、「今どこを処理しているか」が目視できるため安心です。

—

まとめ

今回は、大容量テキストファイルをメモリ不足エラーから守りながら高速処理するための `AtEndOfStream` の活用法について解説しました。

  • `ReadAll` は便利だが、大容量ファイルには「メモリ爆弾」になるので使わない。
  • `Do Until tsRead.AtEndOfStream` と `ReadLine` を組み合わせたストリーミング処理を実装する。
  • 使い終わったオブジェクトは `Close` と `Nothing` で綺麗に片付ける。

ここをクリアすれば、もうVBScriptのファイル操作で恐れるものは何もありません。
日々の面倒なバッチ処理やログ解析をスマートに自動化し、ワンランク上のエンジニアを目指していきましょう!お疲れ様でした!

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