【入門編】【大容量ファイル分割・結合】FileSystemObject を駆使したテキストの特定行数分割とヘッダー維持結合 – VBScript (Visual Basic Scripting Edition)解析バイブル

スポンサーリンク

こんにちは!現場でバリバリ自動化を進めていると、時々「数GBもあるような巨大なCSVファイルやログファイル」に直面して頭を抱えることありませんか?

Excelで開こうものなら「メモリ不足です」と冷たく突き放され、通常のテキストエディタですら固まってしまう……。そんな絶望的な状況をスマートに打破するのが、今回解説する「FileSystemObject (FSO)」を駆使した大容量ファイルの分割・結合テクニックです。

「VBScriptなんて古い技術でしょ?」なんて侮ってはいけません。WSH(Windows Script Host)環境さえあれば、追加のインストールも不要で、OSの標準機能だけで爆速かつ堅牢なファイル処理を実現できる、まさに縁の下の力持ちなのです。

今回は、プログラミング初学者や「マクロの記録から一歩抜け出したい!」と願うあなたに向けて、VBScriptの本質と、現場でそのまま使える実用スクリプトを優しく、かつ深く伝授します。ここをクリアすれば、あなたの自動化スキルは間違いなくワンランク上のステージに到達しますよ!

1. なぜ巨大ファイル処理でVBScript(FSO)なのか?

まず、私たちが対峙する「巨大ファイル」の性質を理解しましょう。数GBのテキストファイルを一気にメモリ(RAM)上に読み込もうとすると、PCのメモリを食い潰し、最悪の場合はOSがクラッシュします。

ここで重要になるのが、「ストリーム処理(一行ずつ、あるいはチャンク単位で読み書きする)」という概念です。

VBScriptの `Scripting.FileSystemObject` が提供する `OpenTextFile` メソッドは、このストリーム処理の王様です。ファイルを少しずつメモリに読み込み、処理しては捨てるを繰り返すため、どれほど巨大なファイルであっても、メモリ消費量を一定に抑えたまま安定して処理し続けることができます。

0から分かる!VBScriptの基本のキ

VBScriptを実行するには、メモ帳などのテキストエディタでコードを書き、拡張子を `.vbs` にして保存するだけです。ダブルクリックすれば、Windows標準のエンジン(`wscript.exe` または `cscript.exe`)が即座に解釈して実行してくれます。

コンパイルの必要もなく、書いたコードがダイレクトにOSを動かす――この軽快さこそがVBScriptの最大の魅力です。

2. 実装:指定行数での「大容量ファイル分割」スクリプト

それでは、実践的なコードを見ていきましょう。
以下のスクリプトは、数GBある巨大なテキスト(またはCSV)ファイルを指定した行数(例: 10,000行ごと)に分割するものです。

‘ =================================================================0
‘ 巨大テキストファイル指定行数分割スクリプト (SplitLargeFile.vbs)
‘ =================================================================0
Option Explicit

Dim fso, sourceFile, targetFolder
Dim inputPath, outputDir, linesPerFile
Dim lineCounter, fileCounter, currentStream
Dim lineText, baseName, ext

‘ — 設定エリア —
inputPath = “C:\Data\HugeLog.csv” ‘ 分割したい巨大ファイルのパス
outputDir = “C:\Data\SplitFiles\” ‘ 分割したファイルの出力先フォルダ
linesPerFile = 10000 ‘ 1ファイルあたりの最大行数
‘ ——————

Set fso = CreateObject(“Scripting.FileSystemObject”)

‘ 入力ファイルの存在チェック
If Not fso.FileExists(inputPath) Then
WScript.Echo “エラー: 入力ファイルが見つかりません: ” & inputPath
WScript.Quit
End If

‘ 出力先フォルダの存在チェック(なければ作成)
If Not fso.FolderExists(outputDir) Then
fso.CreateFolder(outputDir)
End If

‘ ファイル名と拡張子を分離(例: HugeLog.csv -> BaseName: HugeLog, Ext: .csv)
baseName = fso.GetBaseName(inputPath)
ext = “.” & fso.GetExtensionName(inputPath)

‘ 読み取り専用で巨大ファイルを開く (ForReading = 1)
Set sourceFile = fso.OpenTextFile(inputPath, 1, False)

lineCounter = 0
fileCounter = 1
Set currentStream = Nothing

‘ ファイルの終端 (AtEndOfStream) に達するまで1行ずつループ処理
Do While Not sourceFile.AtEndOfStream
lineText = sourceFile.ReadLine

‘ 新しいファイルを作成すべきタイミングの判定
If lineCounter Mod linesPerFile = 0 Then
‘ すでに開いているストリームがあれば閉じる
If Not currentStream Is Nothing Then
currentStream.Close
End If

‘ 新しい分割ファイルを作成 (ForWriting = 2, Create = True)
Dim currentPath
currentPath = outputDir & baseName & “_” & Right(“000” & fileCounter, 4) & ext
Set currentStream = fso.CreateTextFile(currentPath, True, False)

fileCounter = fileCounter + 1
End If

‘ 分割ファイルに書き込み
currentStream.WriteLine lineText
lineCounter = lineCounter + 1
Loop

‘ 最後に開いていたストリームとソースファイルをクローズ
If Not currentStream Is Nothing Then
currentStream.Close
End If
sourceFile.Close

WScript.Echo “分割処理が完了しました!総行数: ” & lineCounter & ” 行”

コードのここがポイント!

  • `Mod` 演算子によるバッチ制御: `lineCounter Mod linesPerFile = 0` という条件式で、正確に指定行数ごとにファイルを切り替えています。
  • ゼロ埋めファイル名 (`Right(“000” & fileCounter, 4)`): ファイル名が `file_1.csv`, `file_2.csv` だとエクスプローラーでのソートが崩れます。`file_0001.csv` のようにゼロ埋めすることで、後続の結合処理で順番が狂うのを防ぐプロの技です。

3. 実装:ヘッダーを保持した「高速ファイル結合」スクリプト

次に、分割したファイルを元の1つに戻す処理です。
ここで実務上よくある大きな課題が「結合したときに、2ファイル目以降のヘッダー(1行目の項目名)が何度も挿入されてしまう問題」です。これをスマートに回避し、1行目のヘッダーは死守しつつ、2行目以降のデータだけを美しく繋ぎ合わせるスクリプトを作成します。

‘ =================================================================0
‘ ヘッダー維持型ファイル結合スクリプト (MergeFilesWithHeader.vbs)
‘ =================================================================0
Option Explicit

Dim fso, targetFolder, sourceFolder, outputFile
Dim inputDir, outputPath
Dim folderObj, fileObj, textStream, outStream
Dim isFirstFile, isFirstLine, lineText

‘ — 設定エリア —
inputDir = “C:\Data\SplitFiles/\” ‘ 分割ファイルが格納されているフォルダ
outputPath = “C:\Data\Merged_Result.csv” ‘ 結合後の出力ファイルパス
‘ ——————

Set fso = CreateObject(“Scripting.FileSystemObject”)

If Not fso.FolderExists(inputDir) Then
WScript.Echo “エラー: 入力フォルダが見つかりません: ” & inputDir
WScript.Quit
End If

‘ 結合先の出力ファイルを作成 (ForWriting = 2, Create = True)
Set outStream = fso.CreateTextFile(outputPath, True, False)

Set folderObj = fso.GetFolder(inputDir)
isFirstFile = True

‘ フォルダ内のファイルを走査
For Each fileObj In folderObj.Files
‘ 拡張子が csv のファイルのみを対象とする(必要に応じて変更)
If LCase(fso.GetExtensionName(fileObj.Name)) = “csv” Then

Set textStream = fso.OpenTextFile(fileObj.Path, 1, False)
isFirstLine = True

Do While Not textStream.AtEndOfStream
lineText = textStream.ReadLine

‘ 各ファイルの「1行目(ヘッダー)」の処理
If isFirstLine Then
If isFirstFile Then
‘ 1つ目のファイルのみ、ヘッダーを出力する
outStream.WriteLine lineText
isFirstFile = False
End If
‘ 2つ目以降のファイルのヘッダーはスキップする!
isFirstLine = False
Else
‘ 2行目以降のデータ行はそのまま書き込み
outStream.WriteLine lineText
End If
Loop

textStream.Close
End If
Next

outStream.Close
WScript.Echo “すべてのファイルの結合が完了しました!出力先: ” & outputPath

4. 現場で陥りやすい罠とエラー回避の知見

VBScriptやFileSystemObjectを扱う上で、実務で必ずハマる「罠」と、その回避策を先輩エンジニアとして伝授しておきます。

① 文字コードの罠(Shift-JIS vs UTF-8)

`OpenTextFile` メソッドの第3引数や第4引数(Format)は、実は癖があります。デフォルトではシステム既定(日本語版Windowsなら Shift-JIS / ANSI)で開かれます。
もし対象のCSVが UTF-8(BOM付き)UTF-8(BOMなし) の場合、文字化けを起こしたり、最初の文字にゴミ(BOM)が混入します。

  • 対策: 本格的なUTF-8ファイルを扱う場合は、`ADODB.Stream` オブジェクトを使用するのが正解です。FSOはANSI/Shift-JISの高速処理と割り切り、UTF-8が必要な現場では `ADODB.Stream` への移行を検討しましょう。

② オブジェクトの解放(メモリリーク対策)

ループ処理の中で `CreateObject` を何度も行ったり、開いた `TextStream` を `.Close` せずに放置すると、メモリリーク(メモリの解放漏れ)を引き起こし、PCが重くなります。

  • 対策: 使い終わったストリームは必ず `textStream.Close` を呼び出し、スクリプトの最後には `Set fso = Nothing` と明示的にオブジェクトを破棄する習慣をつけましょう。

まとめ

今回は、VBScriptとFileSystemObjectを駆使して、数GBクラスの大容量テキストファイルを安全かつスマートに「分割・結合」する手法を解説しました。

  • 大容量ファイルは一括読み込みせず、`OpenTextFile` で1行ずつストリーム処理する
  • ファイル名にゼロ埋め (`Right`) を活用して順序を担保する
  • 結合時はフラグ管理(`isFirstFile` / `isFirstLine`)でヘッダーの重複を華麗に防ぐ

この基本パターンさえマスターしてしまえば、日々の泥臭いファイル整理やログ解析の自動化はあなたのものです。「マクロの記録」の枠を超え、OSの根幹を自在に操るエンジニアとしての第一歩を、ぜひこのコードと共に歩み出してください。

ここをクリアすれば、VBScriptの基本はバッチリですよ!次の自動化の現場でも、自信を持ってコードを書いていきましょう。

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