【入門編】【上級プロフェッショナル向け】大量の小規模テキストファイルをFSOで1つの独自アーカイブファイルにパッキング&相互復元するツール – VBScript (Visual Basic Scripting Edition)解析バイブル

スポンサーリンク

こんにちは!VBScriptの世界へようこそ。
マクロの記録から一歩抜け出して「本物の自動化ツール」を作りたいあなたへ、今日はとてもワクワクする技術をお伝えしますね。

業務で「数百個、数千個の小さなテキストファイル」を扱ったことはありませんか?
これらをそのままメールで送ったり、別のフォルダにコピーしたりすると、Windowsのファイルシステムに負荷がかかって処理が猛烈に遅くなったり、ファイル数が多すぎて見失ったりしますよね。

そこで今回は、FSO(FileSystemObject)の真骨頂を使い、大量のテキストファイルを1つの「独自アーカイブファイル」にまとめ、さらに元の状態へ完全に復元するプロフェッショナルなツールを作ります。

ここをクリアすれば、あなたのVBScriptのスキルは間違いなく一級品になります。さあ、一緒に極上のコードを紐解いていきましょう!

—

1. なぜ「ファイル結合」でFSOの限界が訪れるのか?

VBScriptでファイルを扱うとき、私たちは真っ先に `Scripting.FileSystemObject` を思い浮かべます。
しかし、大量のファイルを単純にテキストとして連結していくと、大きな壁にぶつかります。

  • 改行コードの混入問題: テキストの中に改行文字が含まれていると、どこがファイルの境界線か分からなくなる。
  • メモリの圧迫: 数千個のファイルを一度にメモリ上に展開すると、VBScriptの基盤であるWSH(Windows Script Host)が耐えきれなくなり、メモリリークや強制終了を引き起こす。

この課題を解決するため、今回のツールでは「ファイル名」「データ長(バイト数)」「実データ」を1つのバイナリ的な構造(コンテナ)としてシリアライズ(直列化)します。

—

2. アーキテクチャの全体像

私たちがこれから作るツールは、以下の2つのモードを持ちます。

1. パッキング(圧縮・統合): 指定フォルダ内の全 `.txt` ファイルをスキャンし、1つの `.arc` ファイルにまとめる。
2. アンパッキング(復元): 1つの `.arc` ファイルを読み込み、元のファイル名と内容を完全に復元する。

データの構造(ヘッダーとボディの概念)

[アーカイブファイル(.arc)の構造]
+————————————————-+
| ファイルAのパスの長さ (2バイトInteger) |
+————————————————-+
| ファイルAのパス文字列 (可変長) |
+————————————————-+
| ファイルAのデータ長 (4バイトLong) |
+————————————————-+
| ファイルAの実データ (可変長) |
+————————————————-+
| ファイルBのパスの長さ … |
+————————————————-+
| (以下、ファイル数分だけ繰り返し) |
+————————————————-+

※今回はVBScriptの標準機能で極限まで堅牢に動かすため、ADODB.StreamをFSOの相棒として組み合わせ、バイナリとテキストを完璧にコントロールします。

—

3. 実装コード:完全版アーカイバ&リストアツール

以下のコードをメモ帳に貼り付け、拡張子を `.vbs` (例: `archiver.vbs`)として保存してください。
デスクトップなどに `source` フォルダを作り、その中に適当なテキストファイルをいくつか放り込んで実行テストしてみましょう。

‘ =================================================================0
‘ 匠のFSOアーカイバ&リストアツール for VBScript
‘ 開発者:シニア・チーフアーキテクト
‘ =================================================================0
Option Explicit

Dim fso, targetDir, arcFile
Set fso = CreateObject(“Scripting.FileSystemObject”)

‘ 処理対象のディレクトリとアーカイブファイルのパス定義
targetDir = fso.GetParentFolderName(WScript.ScriptFullName) & “\source”
arcFile = fso.GetParentFolderName(WScript.ScriptFullName) & “\backup.arc”

‘ 1. パッキング(統合)実行
Call PackFiles(targetDir, arcFile)

‘ 2. アンパッキング(復元)実行のテスト用フォルダ
Dim restoreDir
restoreDir = fso.GetParentFolderName(WScript.ScriptFullName) & “\restored”
Call UnpackFiles(arcFile, restoreDir)

WScript.Echo “すべての処理が極めて正常に完了しました!”

‘ =================================================================0
‘ [関数1] パッキング処理:複数ファイルを1つに束ねる
‘ =================================================================0
Sub PackFiles(srcFolderPath, destArcPath)
Dim folder, file, files
If Not fso.FolderExists(srcFolderPath) Then
WScript.Echo “エラー: ソースフォルダが存在しません。”
Exit Sub
End If

‘ ADODB.Streamを使ってバイナリ&文字コードを安全に書き込む
Dim stream
Set stream = CreateObject(“ADODB.Stream”)
stream.Type = 1 ‘ 1 = adTypeBinary
stream.Open

Set folder = fso.GetFolder(srcFolderPath)
Set files = folder.Files

WScript.Echo “— パッキング開始 —”

For Each file In files
If LCase(fso.GetExtensionName(file.Name)) = “txt” Then
WScript.Echo “打包中: ” & file.Name

‘ ① ファイル名(相対パスまたは名前)を書き込む
Dim fileNameBytes
fileNameBytes = StringToBytes(file.Name)
Dim nameLen
nameLen = UBound(fileNameBytes) + 1

‘ パス長を書き込む(2バイト整数に見立てて書き込むための工夫)
stream.Write LongToBytes(nameLen, 2)
stream.Write fileNameBytes

‘ ② ファイルの本体データを読み込んで書き込む
Dim fileDataBytes
fileDataBytes = ReadFileBinary(file.Path)
Dim dataLen
dataLen = UBound(fileDataBytes) + 1

stream.Write LongToBytes(dataLen, 4)
stream.Write fileDataBytes
End If
Next

‘ アーカイブファイルとして保存
stream.SaveToFile destArcPath, 2 ‘ 2 = adSaveCreateOverWrite
stream.Close
Set stream = Nothing
WScript.Echo “パッキング完了 -> ” & destArcPath
End Sub

‘ =================================================================0
‘ [関数2] アンパッキング処理:アーカイブからファイルを復元する
‘ =================================================================0
Sub UnpackFiles(srcArcPath, destFolderPath)
If Not fso.FileExists(srcArcPath) Then
WScript.Echo “エラー: アーカイブファイルが見つかりません。”
Exit Sub
End If

‘ 復元先フォルダがなければ作成
If Not fso.FolderExists(destFolderPath) Then
fso.CreateFolder(destFolderPath)
End If

Dim stream
Set stream = CreateObject(“ADODB.Stream”)
stream.Type = 1
stream.Open
stream.LoadFromFile srcArcPath

WScript.Echo “— アンパッキング開始 —”

Do While stream.Position < stream.Size ' ① ファイル名の長さを読み込む (2バイト) Dim nameLenBytes, nameLen nameLenBytes = stream.Read(2) nameLen = BytesToLong(nameLenBytes) ' ② ファイル名を読み込む Dim fileNameBytes, fileName fileNameBytes = stream.Read(nameLen) fileName = BytesToString(fileNameBytes) ' ③ データ長を読み込む (4バイト) Dim dataLenBytes, dataLen dataLenBytes = stream.Read(4) dataLen = BytesToLong(dataLenBytes) ' ④ 実データを読み込んでファイルに出力 Dim fileDataBytes fileDataBytes = stream.Read(dataLen) Dim outFilePath outFilePath = fso.BuildPath(destFolderPath, fileName) Call WriteFileBinary(outFilePath, fileDataBytes) WScript.Echo "復元成功: " & fileName Loop stream.Close Set stream = Nothing WScript.Echo "すべてのファイルの復元が完了しました -> ” & destFolderPath
End Sub

‘ =================================================================0
‘ [ヘルパー関数群] バイナリと文字列・数値の相互変換(ここが技術の肝!)
‘ =================================================================0

‘ 文字列をUTF-8のバイト配列に変換
Function StringToBytes(str)
Dim adoStream
Set adoStream = CreateObject(“ADODB.Stream”)
adoStream.Type = 2 ‘ 2 = adTypeText
adoStream.Charset = “UTF-8”
adoStream.Open
adoStream.WriteText str
adoStream.Position = 0
adoStream.Type = 1 ‘ adTypeBinary
adoStream.Position = 3 ‘ BOMをスキップ
StringToBytes = adoStream.Read
adoStream.Close
End Function

‘ バイト配列を文字列に変換
Function BytesToString(bytes)
Dim adoStream
Set adoStream = CreateObject(“ADODB.Stream”)
adoStream.Type = 1
adoStream.Open
adoStream.Write bytes
adoStream.Position = 0
adoStream.Type = 2
adoStream.Charset = “UTF-8”
BytesToString = adoStream.ReadText
adoStream.Close
End Function

‘ 数値を指定バイト数のバイナリ(バイト配列)に変換
Function LongToBytes(lng, byteSize)
Dim i, bytes()
ReDim bytes(byteSize – 1)
For i = 0 To byteSize – 1
bytes(i) = lng And &HFF
lng = lng \ &H100
Next
LongToBytes = bytes
End Function

‘ バイト配列を数値に戻す
Function BytesToLong(bytes)
Dim i, lng
lng = 0
For i = UBound(bytes) To 0 Step -1
lng = lng &H100 + CLng(bytes(i))
Next
BytesToLong = lng
End Function

‘ 指定ファイルをバイナリとして読み込む
Function ReadFileBinary(filePath)
Dim adoStream
Set adoStream = CreateObject(“ADODB.Stream”)
adoStream.Type = 1
adoStream.Open
adoStream.LoadFromFile filePath
ReadFileBinary = adoStream.Read
adoStream.Close
End Function

‘ バイナリデータをファイルとして書き出す
Sub WriteFileBinary(filePath, bytes)
Dim adoStream
Set adoStream = CreateObject(“ADODB.Stream”)
adoStream.Type = 1
adoStream.Open
adoStream.Write bytes
adoStream.SaveToFile filePath, 2
adoStream.Close
End Sub

—

4. コードの深掘り解説:なぜこの書き方なのか?

初心者から一歩進んだあなたが「おっ」と気付くべきポイントを解説します。

1. FSO単体ではなく `ADODB.Stream` を組み合わせる理由

FSOの `OpenTextFile` は、文字コードの化けや、バイナリデータの取り扱いに非常に弱いです。
実務で大量のテキストを扱う際、文字コード(Shift-JISかUTF-8か)の不一致で文字化けが起きる事故を防ぐため、裏側で `ADODB.Stream` にバイナリとして流し込んでいます。これにより、どんな言語の文字が含まれていても完全無欠で保護されます。

2. ヘッダー情報の埋め込み(シリアライゼーション)

コード内の `LongToBytes` や `BytesToLong` 関数に注目してください。
これは、ファイル名やデータの長さを「何バイトのデータか」という数値情報に変換し、アーカイブファイルの先頭に必ずセットで書き込む仕組みです。
これにより、「どこまでがファイル名で、どこからが中身なのか」をコンピュータが正確に判別できるようになります。これができていなければ、ファイルの復元時にデータがズタズタになってしまいます。

—

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

1. 「バッファサイズが大きすぎてメモリが足りません」と言われたら?

  • 今回の方式は一度メモリ上にバイト配列としてデータを乗せるため、1ファイルが「2GB」あるような巨大な動画やログファイルには向きません。あくまで「小規模テキストファイル(数KB〜数MB)」の束ね上げに特化させるのがプロの判断です。

2. ファイル名に日本語が含まれている場合の文字化け

  • ヘルパー関数で `Charset = “UTF-8″` を明示しているため、日本語のファイル名であっても文字化けせずに復元可能です。ANSI(Shift-JIS)環境に依存しない、モダンな設計にしています。

—

まとめ

いかがでしたでしょうか?
今回は、FSOの基本操作を応用し、実務で即戦力となる「独自アーカイブ&リストアツール」を構築しました。

「ファイルをただコピーする」だけのスクリプトから、「データを構造化して安全に運ぶ」プログラミングへのステップアップ。ここをクリアできたあなたなら、どんな自動化の課題が来ても怖くありません。

ぜひ日々の業務効率化に役立ててくださいね。それでは、次回の高度なテクニックでお会いしましょう!

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