コンソール・バッチ処理の「生存戦略」:StreamReader/Writerによる標準入出力の極致
VB.NETを扱う諸君。君たちが普段何気なく叩いている`Console.WriteLine`や`Console.ReadLine`。あれはあくまで「手遊び」に過ぎない。
業務システム、特にバッチ処理の最前線において、標準入出力は単なるデータ受け渡しではない。それは、OSとプロセス間を繋ぐ唯一の生命線だ。ログが欠落すれば障害調査は暗礁に乗り上げ、メモリ管理を怠れば、長時間稼働のバッチはOSの重圧となってシステムを窒息させる。
今日は、初心者から一歩踏み出し、シニアエンジニアとして「確実に書き出し、安全に受け取る」ための、VB.NET標準入出力の作法を伝授する。
—
1. なぜ「標準」を直書きしてはいけないのか
初心者は `Console.WriteLine` を多用する。だが、これはI/Oバッファの制御をOSに丸投げする行為だ。大規模なバッチ処理において、バッファのフラッシュ(書き出し)タイミングを制御できないのは致命的である。
我々が採用すべきは `System.IO.StreamWriter` と `System.IO.StreamReader` の明示的な管理だ。これにより、メモリの解放タイミング(Dispose)とバッファのフラッシュを完全に掌握できる。
実践的コード:確実なロギングの定石
Imports System.IO
Imports System.Text
Public Class Logger
‘ UTF-8 BOMなしが現代のレガシー連携の鉄則
Private Shared ReadOnly Encoding As Encoding = New UTF8Encoding(False)
Public Shared Sub WriteLog(ByVal message As String, ByVal logPath As String)
‘ Using句はDisposeを保証する。これがメモリ管理の基本中の基本。
‘ append:=True で追記モード。FileShare.Read で他プロセスからのログ閲覧を許容する
Using sw As New StreamWriter(logPath, True, Encoding)
sw.AutoFlush = True ‘ リアルタイム性を重視する場合
sw.WriteLine($”[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] {message}”)
End Using
End Sub
End Class
—
2. メモリ最適化とライフサイクルの真実
VB.NETのガベージコレクタ(GC)は優秀だが、巨大なファイルを読み込む際に `StreamReader` を野放しにすると、ヒープ領域を食いつぶす。特にバッチ処理で、巨大なCSVを1行ずつ処理する場合、「ストリームを溜め込むな、使い捨てろ」が鉄則だ。
ストリームの「使い捨て」パターン
Public Sub ProcessLargeFile(ByVal filePath As String)
‘ メモリリークを防ぐため、ストリームは常に最小スコープで管理する
Using sr As New StreamReader(filePath, Encoding.UTF8)
While Not sr.EndOfStream
Dim line As String = sr.ReadLine()
‘ ここで処理を行い、line変数は即座にGCの対象にする
If String.IsNullOrEmpty(line) Then Continue While
ProcessData(line)
End While
End Using
End Sub
- 技術的洞察: `sr.ReadLine()` は行末までの文字列をメモリに展開する。もしファイルがテラバイト級であれば、Streamを直接バッファに流し込む `MemoryMappedFile` 等の利用を検討すべきだが、通常の業務バッチならこの `Using` 構文で十分だ。
—
3. レガシー連携の極意:Windows APIとの密な関係
VBAからVB.NETへ移行する際、最も苦労するのが「コンソールが見えない」問題だ。Windowsサービスとして動作させる場合、標準入出力はデッドロックを引き起こす可能性がある。
もし君が、古い基幹システムの連携窓口を作っているなら、`AttachConsole` APIの知識が不可欠だ。
‘ 外部プロセス(VBA等)からアタッチするための定義
Public Declare Function AttachConsole Lib “kernel32.dll” (ByVal dwProcessId As Int32) As Boolean
Public Declare Function FreeConsole Lib “kernel32.dll” () As Boolean
‘ バッチの先頭で現在のコンソールを制御下におく
Sub Main()
‘ 親プロセスのコンソールを奪取してログを流し込む
AttachConsole(-1)
Console.WriteLine(“System Initialized.”)
‘ … 処理 …
FreeConsole()
End Sub
—
4. 最後に:エンジニアとしての矜持
コードを書くことは、単に動くものを作ることではない。「後から誰が読んでも、なぜそこでDisposeが必要だったか、なぜそこでエンコーディングを指定したかが分かる」状態にすることだ。
VB.NETは古臭い言語だと言う者もいる。だが、この言語で構築されたシステムは、10年、20年と動き続ける。君たちの書く数行のコードが、数年後の夜中に呼び出された保守担当者の命を救う。
I/Oを支配する者は、プロセスを支配する。プロセスを支配する者は、システムを支配する。
明日からのバッチ処理、コードの裏側にある「メモリの鼓動」を意識して書くように。
以上だ。健闘を祈る。
