【テクニカル・上級編】VBAで「バイナリデータ」を扱う:CSV以外のファイル形式を読み書きする技術 – Excel VBA解析バイブル

スポンサーリンク

Excel VBAでバイナリデータを操る:ADODB.Streamとレガシーシステム連携の真髄

巷にあふれるVBAの入門書は、せいぜい`Range`オブジェクトの操作や、簡単な`MsgBox`での情報表示で終わる。だが、我々が日々向き合っているのは、そんな生易しいものではない。Excelというプラットフォーム上で、時には数万行、数十万行のデータを捌き、外部システムとの連携を果たす。その過程で、CSVだけでは済まない、様々な「バイナリデータ」に触れることになる。

本稿では、Excel VBAでバイナリデータを直接操作するための強力な武器、`ADODB.Stream`オブジェクトに焦点を当てる。単なるファイル読み書きの域を超え、文字コード変換、バイナリファイルの直接操作、そしてWindows APIとの連携まで、レガシーシステム保守やシステム間連携の現場で求められる、より深く、より実践的な知見を、伝説のチーフアーキテクトたる私の視点から淡々と、しかし徹底的に解説していこう。

なぜADODB.Streamなのか? CSVの限界とバイナリの現実

Excel VBAで外部ファイル、特にテキストファイルを扱う際、最も手軽なのはCSV形式だろう。`FileSystemObject`や`Worksheet`オブジェクトの`OpenText`メソッドを使えば、比較的容易に読み込める。しかし、このアプローチには明確な限界がある。

  • 文字コードの壁: CSVファイルは、保存時の文字コード(UTF-8, Shift_JIS, EUC-JPなど)に依存する。異なる文字コードのファイルをそのまま読み込もうとすると、文字化けは避けられない。`FileSystemObject`では、読み込んだ後に`StrConv`関数などで変換する手間が発生し、これがまた煩雑だ。
  • バイナリデータの扱いの限界: 画像ファイル、実行ファイル、あるいは特定のエンコードで保存されたバイナリデータなど、テキストとして解釈できないファイル群は、`FileSystemObject`のテキストモードでは扱えない。
  • パフォーマンスとメモリ: 大量のテキストデータを一行ずつ読み込み、Excelのセルに書き込む処理は、特にデータ量が多い場合にパフォーマンスのボトルネックとなりやすい。メモリ管理も考慮が必要だ。

ここで登場するのが`ADODB.Stream`オブジェクトだ。これは、ADO(ActiveX Data Objects)ライブラリの一部であり、本来はデータベースアクセスのためのものだが、その強力なストリーム操作機能は、ファイルI/Oにおいても非常に有用だ。特に、以下の点が優れている。

1. 多様なモードでの操作: テキストモードとバイナモードを明示的に切り替えられる。これにより、文字コードを意識したテキストファイルの読み書き、そしてバイナリデータそのものの直接操作が可能になる。
2. 高速なI/O: メモリ上に一度にデータを読み込み、まとめて処理できるため、大量データの読み書きにおいて`FileSystemObject`よりも高速な場合が多い。
3. 柔軟な文字コード指定: テキストモードで読み書きする際に、`Charset`プロパティで明示的に文字コードを指定できる。これにより、文字化けの問題を根本から解決できる。

ADODB.Streamの基本:テキストファイルの読み書きと文字コード変換

まずは、`ADODB.Stream`の最も基本的な使い方、テキストファイルの読み書きと文字コード変換から見ていこう。

1. ADODB.Streamオブジェクトの準備

`ADODB.Stream`オブジェクトは、COMコンポーネントとして提供されている。VBAエディタで「ツール」→「参照設定」を開き、「Microsoft ActiveX Data Objects x.x Library」にチェックを入れる必要がある。バージョンは環境によって異なるが、通常は最新のものが推奨される。

‘ — ADODB.Streamオブジェクトの宣言と生成 —
Dim objStream As Object ‘ ADODB.Streamオブジェクト
Set objStream = CreateObject(“ADODB.Stream”)

‘ — オブジェクトの解放処理 (関数やSubの最後、またはエラーハンドリングで) —
‘ Set objStream = Nothing

2. テキストモードでのファイル読み込み(文字コード指定)

例えば、UTF-8で保存されたテキストファイルを読み込み、その内容をExcelシートに書き出す場合を考える。

Sub ReadTextFileWithCharset()

Dim objStream As Object
Dim filePath As String
Dim fileContent As String

‘ ファイルパスを指定 (例: C:\temp\mydata.txt)
filePath = “C:\temp\mydata.txt” ‘ 実際のパスに置き換えてください

‘ Streamオブジェクトの生成
Set objStream = CreateObject(“ADODB.Stream”)

On Error GoTo ErrorHandler

With objStream
‘ — テキストモードで開く —
.Open

‘ — 文字コードを指定 (UTF-8の場合) —
‘ 他にも “ascii”, “unicode”, “utf-16le” など指定可能
.Charset = “utf-8”

‘ — ファイルをバイナリモードで開く (Read/Write) —
‘ テキストモードで開く場合でも、一度バイナリモードで開いてからCharsetを設定するのが一般的
‘ または、直接 .LoadFromFile filePath で読み込むことも可能だが、Charset設定が先
.LoadFromFile filePath

‘ — ファイルの内容を全て読み込む —
fileContent = .ReadText

‘ — 読み込んだ内容をExcelシートに書き出す (例) —
‘ 実際には、この後、解析や加工処理を行う
ThisWorkbook.Sheets(“Sheet1”).Cells(1, 1).Value = fileContent

‘ — クリーンアップ —
.Close
End With

MsgBox “ファイル ‘” & filePath & “‘ を読み込みました。”, vbInformation

ExitProcedure:
‘ オブジェクトの解放
If Not objStream Is Nothing Then
If objStream.State = 1 Then .Close ‘ 念のためClose
Set objStream = Nothing
End If
Exit Sub

ErrorHandler:
MsgBox “エラーが発生しました。” & vbCrLf & “エラー番号: ” & Err.Number & vbCrLf & “エラー内容: ” & Err.Description, vbCritical
Resume ExitProcedure

End Sub

解説:

  • `.Open`: ストリームを開きます。`adModeReadWrite` (3), `adModeRead` (1), `adModeWrite` (2) などのモードがありますが、テキスト読み込みの場合は明示的なモード指定は不要な場合が多いです。
  • `.Charset = “utf-8″`: ここが重要です。読み込むテキストファイルの文字コードを明示的に指定します。これにより、異なる文字コードのファイルを正確に読み込むことができます。
  • `.LoadFromFile filePath`: 指定したパスのファイルをストリームに読み込みます。
  • `.ReadText`: テキストモードで読み込んだ内容を文字列として返します。

3. テキストモードでのファイル書き込み(文字コード指定)

次に、Excelシート上のデータを、指定した文字コードでファイルに保存する例です。

Sub WriteTextFileWithCharset()

Dim objStream As Object
Dim filePath As String
Dim dataToWrite As String

‘ 保存先のファイルパスを指定 (例: C:\temp\output.txt)
filePath = “C:\temp\output.txt” ‘ 実際のパスに置き換えてください

‘ Excelシートから書き出すデータを取得 (例)
dataToWrite = ThisWorkbook.Sheets(“Sheet1”).Cells(1, 1).Value

‘ Streamオブジェクトの生成
Set objStream = CreateObject(“ADODB.Stream”)

On Error GoTo ErrorHandler

With objStream
‘ — ストリームを開く —
.Open

‘ — 書き込みモード (Read/Write) を設定 —
‘ adModeWrite = 2
.Mode = 2 ‘ 書き込みモード

‘ — 文字コードを指定 (Shift_JISの場合) —
.Charset = “shiftjis”

‘ — ファイルを新規作成/上書きモードで開く —
‘ adSaveCreateOverWrite = 2
.SaveToFile filePath, 2 ‘ 上書きモードで保存

‘ — 書き込むデータをセット —
‘ BinaryWrite はバイナリデータ用。テキストは WriteText
.WriteText dataToWrite

‘ — ファイルに保存 —
‘ SaveToFile は、ストリームの内容を直接ファイルに保存する。
‘ WriteText で書き込んだ内容を確定させるために必要。
‘ 後続のCLOSEで自動保存される場合もあるが、明示的に行うのが安全。
‘ .SaveToFile filePath, 2 ‘ これでも良いが、WriteText後にCloseするのが一般的

‘ — クリーンアップ —
.Close
End With

MsgBox “ファイル ‘” & filePath & “‘ に書き込みました。”, vbInformation

ExitProcedure:
‘ オブジェクトの解放
If Not objStream Is Nothing Then
If objStream.State = 1 Then .Close ‘ 念のためClose
Set objStream = Nothing
End If
Exit Sub

ErrorHandler:
MsgBox “エラーが発生しました。” & vbCrLf & “エラー番号: ” & Err.Number & vbCrLf & “エラー内容: ” & Err.Description, vbCritical
Resume ExitProcedure

End Sub

解説:

  • `.Mode = 2` (`adModeWrite`): 書き込みモードでストリームを開きます。
  • `.Charset = “shiftjis”`: ここも重要です。出力するテキストファイルの文字コードを指定します。これにより、互換性の高いShift_JIS形式で保存できます。
  • `.SaveToFile filePath, 2`: `adSaveCreateOverWrite` (2) を指定して、ファイルを上書きモードで保存します。`WriteText`で書き込んだ内容が、このメソッドによってファイルにフラッシュされます。
  • `.WriteText dataToWrite`: 指定した文字列をストリームに書き込みます。

文字コードについて:
指定できる`Charset`は、Windowsのロケール設定やインストールされているコードページに依存します。一般的に使われるのは以下のものです。

  • `”ascii”`: 7ビットASCII
  • `”ansi”`: システムのANSIコードページ(日本語環境ならShift_JIS)
  • `”utf-8″`: UTF-8
  • `”unicode”`: UTF-16(リトルエンディアン、BOMなし)
  • `”utf-16″`: UTF-16(リトルエンディアン、BOMあり)
  • `”utf-16be”`: UTF-16ビッグエンディアン

レガシーシステムとの連携を考えると、`”shiftjis”`や`”ansi”`の指定が頻繁に登場します。

バイナリモードでのファイル操作:真のバイナリデータ制御

ここからが本題、`ADODB.Stream`の真骨頂であるバイナリモードでのファイル操作です。

1. バイナリモードでのファイル読み込み

画像ファイルや、特定のフォーマットを持つバイナリファイルをそのまま読み込みたい場合に使用します。

Sub ReadBinaryFile()

Dim objStream As Object
Dim filePath As String
Dim fileBytes() As Byte ‘ バイト配列で受け取る
Dim i As Long

‘ バイナリファイルのパスを指定 (例: C:\temp\image.jpg)
filePath = “C:\temp\image.jpg” ‘ 実際のパスに置き換えてください

‘ Streamオブジェクトの生成
Set objStream = CreateObject(“ADODB.Stream”)

On Error GoTo ErrorHandler

With objStream
‘ — バイナリモードで開く —
.Open
.Mode = 3 ‘ adModeReadWrite (読み書き両方可能)
.Type = 1 ‘ adTypeBinary (バイナリタイプ)

‘ — ファイルを読み込む —
.LoadFromFile filePath

‘ — ファイルの内容をバイト配列として取得 —
fileBytes = .Read

‘ — 読み込んだバイト列のサイズ確認 (例) —
Debug.Print “ファイルサイズ: ” & UBound(fileBytes) + 1 & ” バイト”

‘ — ここで、取得したバイト配列 fileBytes を加工・利用する —
‘ 例: 特定のバイト列を検索する、バイト列を書き換える、など
‘ 注意: Excel VBAで直接バイナリデータを操作するのはパフォーマンス上の課題がある場合もある

‘ — クリーンアップ —
.Close
End With

MsgBox “バイナリファイル ‘” & filePath & “‘ を読み込みました。”, vbInformation

ExitProcedure:
‘ オブジェクトの解放
If Not objStream Is Nothing Then
If objStream.State = 1 Then .Close ‘ 念のためClose
Set objStream = Nothing
End If
Exit Sub

ErrorHandler:
MsgBox “エラーが発生しました。” & vbCrLf & “エラー番号: ” & Err.Number & vbCrLf & “エラー内容: ” & Err.Description, vbCritical
Resume ExitProcedure

End Sub

解説:

  • `.Mode = 3` (`adModeReadWrite`): 読み書き両方のモードで開きます。読み込みだけなら `adModeRead` (1) でも構いません。
  • `.Type = 1` (`adTypeBinary`): ここが最重要です。ストリームのタイプをバイナリに設定します。
  • `.Read`: バイナリモードで読み込んだ内容を `Variant/Byte()` 型(バイト配列)として返します。

2. バイナリモードでのファイル書き込み

バイト配列を直接ファイルに書き出す場合です。

Sub WriteBinaryFile()

Dim objStream As Object
Dim filePath As String
Dim dataToWrite() As Byte ‘ 書き込むバイト配列
Dim i As Long

‘ 保存先のファイルパスを指定 (例: C:\temp\output.bin)
filePath = “C:\temp\output.bin” ‘ 実際のパスに置き換えてください

‘ — 書き込むバイト配列を準備 (例: 1から10までのバイト列) —
ReDim dataToWrite(0 To 9)
For i = 0 To 9
dataToWrite(i) = i + 1
Next i

‘ Streamオブジェクトの生成
Set objStream = CreateObject(“ADODB.Stream”)

On Error GoTo ErrorHandler

With objStream
‘ — ストリームを開く —
.Open
.Mode = 3 ‘ adModeReadWrite
.Type = 1 ‘ adTypeBinary

‘ — ファイルを新規作成/上書きモードで開く —
.SaveToFile filePath, 2 ‘ adSaveCreateOverWrite

‘ — バイト配列を書き込む —
.Write dataToWrite

‘ — ファイルに保存 (Close時に自動保存されるが、明示的に行うのが安全) —
‘ .SaveToFile filePath, 2 ‘ これでも良い

‘ — クリーンアップ —
.Close
End With

MsgBox “バイナリファイル ‘” & filePath & “‘ に書き込みました。”, vbInformation

ExitProcedure:
‘ オブジェクトの解放
If Not objStream Is Nothing Then
If objStream.State = 1 Then .Close ‘ 念のためClose
Set objStream = Nothing
End If
Exit Sub

ErrorHandler:
MsgBox “エラーが発生しました。” & vbCrLf & “エラー番号: ” & Err.Number & vbCrLf & “エラー内容: ” & Err.Description, vbCritical
Resume ExitProcedure

End Sub

解説:

  • `.Type = 1` (`adTypeBinary`): バイナリモードであることを明示します。
  • `.SaveToFile filePath, 2`: ファイルを上書きモードで開きます。
  • `.Write dataToWrite`: バイト配列をストリームに書き込みます。

レガシーシステム保守とシステム間連携における極限の知見

ここまでは`ADODB.Stream`の基本的な使い方でしたが、実際の現場では、これだけでは済まない複雑な要求に直面します。

1. Windows APIとの連携によるバイナリ操作

Windows APIは、OSの低レベルな機能にアクセスするための強力な手段です。`ADODB.Stream`では直接扱えないような、メモリ管理やファイルポインタの高度な操作が必要な場面で、APIとの連携が不可欠になります。

例えば、特定のメモリ領域に書き込まれたバイナリデータをファイルに保存したい、あるいは、ファイルの一部だけを効率的に読み込みたい、といったケースです。

‘ — Windows API宣言 (一部抜粋) —
‘ Declare PtrSafe Function CreateFile Lib “kernel32” Alias “CreateFileA” ( _
‘ ByVal lpFileName As String, _
‘ ByVal dwDesiredAccess As Long, _
‘ ByVal dwShareMode As Long, _
‘ ByVal lpSecurityAttributes As Long, _
‘ ByVal dwCreationDisposition As Long, _
‘ ByVal dwFlagsAndAttributes As Long, _
‘ ByVal hTemplateFile As Long) As Long
‘
‘ Declare PtrSafe Function WriteFile Lib “kernel32” ( _
‘ ByVal hFile As Long, _
‘ ByVal lpBuffer As Any, _
‘ ByVal nNumberOfBytesToWrite As Long, _
‘ ByRef lpNumberOfBytesWritten As Long, _
‘ ByVal lpOverlapped As Long) As Long
‘
‘ Declare PtrSafe Function CloseHandle Lib “kernel32” ( _
‘ ByVal hObject As Long) As Long
‘
‘ Declare PtrSafe Function ReadFile Lib “kernel32” ( _
‘ ByVal hFile As Long, _
‘ ByVal lpBuffer As Any, _
‘ ByVal nNumberOfBytesToRead As Long, _
‘ ByRef lpNumberOfBytesRead As Long, _
‘ ByVal lpOverlapped As Long) As Long
‘
‘ Declare PtrSafe Function GlobalAlloc Lib “kernel32” ( _
‘ ByVal wFlags As Long, ByVal dwBytes As Long) As Long
‘
‘ Declare PtrSafe Function GlobalLock Lib “kernel32” ( _
‘ ByVal hMem As Long) As Long
‘
‘ Declare PtrSafe Function GlobalUnlock Lib “kernel32” ( _
‘ ByVal hMem As Long) As Long
‘
‘ Declare PtrSafe Function GlobalFree Lib “kernel32” ( _
‘ ByVal hMem As Long) As Long
‘
‘ Declare PtrSafe Function CopyMemory Lib “kernel32” Alias “RtlMoveMemory” ( _
‘ lpvDest As Any, lpvSource As Any, ByVal cbCopy As Long) As Long

‘ — ADODB.StreamとAPIを組み合わせた例 (概念) —
‘ Sub WriteMemoryToBinaryFile()
‘ Dim hFile As Long
‘ Dim hMem As Long
‘ Dim lpBuffer As Long
‘ Dim bytesWritten As Long
‘ Dim fileHandle As Long
‘ Dim fileStream As Object
‘ Dim dataSize As Long
‘
‘ ‘ 1. 目的のデータが格納されているメモリ領域を特定
‘ ‘ (これは通常、他のAPI呼び出しや、既にVBAで確保したメモリ領域になる)
‘ ‘ ここでは仮に、VBAで確保したメモリ領域を想定
‘ dataSize = 1024 ‘ 仮のデータサイズ
‘ hMem = GlobalAlloc(&H2, dataSize) ‘ GMEM_FIXED | GMEM_ZEROINIT
‘ If hMem = 0 Then Exit Sub
‘ lpBuffer = GlobalLock(hMem)
‘ If lpBuffer = 0 Then
‘ GlobalFree hMem
‘ Exit Sub
‘ End If
‘
‘ ‘ 2. ここで lpBuffer に実際にデータが書き込まれる処理を行う
‘ ‘ (例: CopyMemory を使ってVBA配列からコピーするなど)
‘ ‘ CopyMemory ByVal lpBuffer, VBAArrayVariable(0), dataSize
‘
‘ ‘ 3. ADODB.Stream をバイナリモードで開く
‘ Set fileStream = CreateObject(“ADODB.Stream”)
‘ fileStream.Open
‘ fileStream.Mode = 3 ‘ adModeReadWrite
‘ fileStream.Type = 1 ‘ adTypeBinary
‘
‘ ‘ 4. ファイルを新規作成/上書きモードで開く (CreateFile APIを使用)
‘ ‘ ※ ADODB.Stream の SaveToFile でも良いが、APIを使う例として
‘ fileHandle = CreateFile( _
‘ “C:\temp\memory_dump.bin”, _
‘ GENERIC_WRITE, FILE_SHARE_READ, 0, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0)
‘
‘ If fileHandle = INVALID_HANDLE_VALUE Then
‘ ‘ エラー処理
‘ GlobalUnlock hMem
‘ GlobalFree hMem
‘ Exit Sub
‘ End If
‘
‘ ‘ 5. メモリ上のデータをファイルに書き込む (WriteFile APIを使用)
‘ Dim bytesToWrite As Long
‘ bytesToWrite = dataSize ‘ 書き込むバイト数
‘ If WriteFile(fileHandle, ByVal lpBuffer, bytesToWrite, bytesWritten, 0) = 0 Then
‘ ‘ エラー処理
‘ End If
‘
‘ ‘ 6. ファイルハンドルを閉じる
‘ CloseHandle fileHandle
‘
‘ ‘ 7. メモリを解放
‘ GlobalUnlock hMem
‘ GlobalFree hMem
‘
‘ ‘ 8. Streamオブジェクトも解放 (必要であれば)
‘ ‘ WriteFileで直接書き込んだ場合は、Streamは不要な場合もある
‘ fileStream.Close
‘ Set fileStream = Nothing
‘
‘ End Sub

注意: 上記API連携コードは概念的なものであり、実際の利用にはAPI定数やエラーハンドリングの拡充が必要です。特にメモリ操作は、VBAのメモリ管理とは異なるため、細心の注意が求められます。`GlobalAlloc`, `GlobalLock`, `CopyMemory` などは、VBAの配列やVariant型と連携させる際に、バイト単位での正確なデータ転送を保証します。

2. メモリ最適化とオブジェクトの明示的解放

VBAでは、COMオブジェクト(`ADODB.Stream`や`FileSystemObject`など)は、参照カウントがゼロになると自動的に解放されます。しかし、大量のオブジェクトを生成・破棄する処理や、複雑な参照関係がある場合、メモリリークやパフォーマンス低下の原因となることがあります。

鉄則は「不要になったオブジェクトは、できるだけ早く、明示的に解放する」ことです。

  • `Set obj = Nothing`: これが最も基本的な解放方法です。参照カウントを減らし、COMオブジェクトが解放されるのを促します。
  • エラーハンドリングでの解放: `On Error GoTo` でエラー処理ブロックに遷移する前に、必ずオブジェクトの解放処理を記述します。さもないと、エラー発生時にオブジェクトが解放されず、リソースを圧迫する可能性があります。
  • ループ処理での解放: ループ内でオブジェクトを生成・破棄する場合、ループの終了時ではなく、ループの各イテレーションの終わりに解放するのが効果的です。

Sub ProcessLargeDataEfficiently()

Dim objStream As Object
Dim i As Long
Const BUFFER_SIZE As Long = 65536 ‘ 64KBバッファ (適宜調整)

Set objStream = CreateObject(“ADODB.Stream”)

On Error GoTo ErrorHandler

With objStream
.Open
.Mode = 3 ‘ Read/Write
.Type = 1 ‘ Binary

‘ ファイルが存在しない場合は作成
.SaveToFile “C:\temp\large_binary_file.bin”, 2 ‘ Overwrite

‘ 64KBずつ書き込むループ
Dim currentOffset As Long
Dim buffer() As Byte
Dim bytesToWrite As Long

For i = 1 To 1000 ‘ 例: 1000回書き込み (合計約64MB)
ReDim buffer(0 To BUFFER_SIZE – 1)
‘ ここで buffer に書き込むバイナリデータを生成・取得
‘ 例: FileSystemObject を使って別のバイナリファイルから一部を読み込む
‘ または、計算結果をバイト列に変換するなど

‘ 便宜上、ここでは単純なバイト列を生成
Dim j As Long
For j = 0 To BUFFER_SIZE – 1
buffer(j) = (i + j) Mod 256
Next j

‘ 書き込み
.Write buffer

‘ オブジェクトの解放は不要 (ループ終了時に一括解放)
‘ Debug.Print “書き込み完了 (イテレーション: ” & i & “)”
Next i

.Close
End With

MsgBox “大量のバイナリデータを効率的に書き込みました。”, vbInformation

ExitProcedure:
‘ — オブジェクトの明示的解放 (最重要) —
If Not objStream Is Nothing Then
If objStream.State = 1 Then objStream.Close
Set objStream = Nothing ‘ ここで解放
End If
Exit Sub

ErrorHandler:
MsgBox “エラーが発生しました。” & vbCrLf & “エラー番号: ” & Err.Number & vbCrLf & “エラー内容: ” & Err.Description, vbCritical
Resume ExitProcedure

End Sub

この例では、`BUFFER_SIZE`で指定したチャンクごとにデータを読み書きすることで、メモリ使用量を一定に保ち、パフォーマンスの向上を図っています。`objStream`オブジェクトはループの外で一度だけ生成し、最後に解放しています。

3. VB.NETへの移行を視野に入れた設計

Excel VBAは、その手軽さから小規模な自動化には向いていますが、大規模なシステムや、より高度なバイナリ処理、リソース管理が求められる場面では、限界が見えてきます。このような場合、VB.NETへの移行は現実的な選択肢となります。

`ADODB.Stream`はVB.NETでも利用可能ですが、VB.NETでは `System.IO` 名前空間のクラス(`FileStream`, `BinaryReader`, `BinaryWriter` など)がより強力で、パフォーマンスや機能面で優れています。

VBAで`ADODB.Stream`を用いたバイナリ処理を実装する際には、以下の点を意識すると、将来的なVB.NETへの移行がスムーズになります。

  • 入出力の分離: ファイルI/Oのロジックを、Excelの操作(セルの読み書きなど)と明確に分離する。
  • バイト配列中心の処理: バイナリデータは常にバイト配列 (`Byte()`) で扱う。
  • API呼び出しの最小化: VBAでしか実現できない、あるいはAPI呼び出しが不可欠な処理に限定する。
  • 構造化されたコード: 処理ごとにモジュールや関数を分け、可読性と保守性を高める。

VBAで`ADODB.Stream`を使って、あるバイナリファイルを読み込み、その一部を加工して、別のバイナリファイルとして保存する VB.NET のコード例を以下に示します。

.net
‘ — VB.NET コード例 —
Imports System.IO
Imports System.Text

Public Class BinaryFileProcessor

‘ UTF-8のBOMを考慮して読み込む
Public Function ReadUtf8FileWithBom(filePath As String) As String
Using reader As New StreamReader(filePath, Encoding.UTF8, True) ‘ detectEncodingFromByteOrderMarks:=True
Return reader.ReadToEnd()
End Using
End Function

‘ Shift_JISで保存する
Public Sub SaveAsShiftJis(filePath As String, content As String)
Using writer As New StreamWriter(filePath, False, Encoding.GetEncoding(“shift_jis”))
writer.Write(content)
End Using
End Sub

‘ バイナリファイルを読み込み、バイト配列で返す
Public Function ReadBinaryFile(filePath As String) As Byte()
Using fs As New FileStream(filePath, FileMode.Open, FileAccess.Read)
Dim buffer(fs.Length – 1) As Byte
fs.Read(buffer, 0, buffer.Length)
Return buffer
End Using
End Function

‘ バイト配列をバイナリファイルとして保存する
Public Sub WriteBinaryFile(filePath As String, data As Byte())
Using fs As New FileStream(filePath, FileMode.Create, FileAccess.Write)
fs.Write(data, 0, data.Length)
End Using
End Sub

‘ 特定のバイト列を検索して置換する (例)
Public Function ReplaceBytes(originalData As Byte(), searchBytes As Byte(), replaceBytes As Byte()) As Byte()
If originalData Is Nothing Or searchBytes Is Nothing Or replaceBytes Is Nothing Then
Return originalData
End If

Dim ms As New MemoryStream()
Dim i As Integer = 0
While i < originalData.Length ' 検索バイト列との一致を確認 If i + searchBytes.Length <= originalData.Length Then Dim match As Boolean = True For j As Integer = 0 To searchBytes.Length - 1 If originalData(i + j) <> searchBytes(j) Then
match = False
Exit For
End If
Next
If match Then
‘ 一致した場合、置換バイト列を書き込む
ms.Write(replaceBytes, 0, replaceBytes.Length)
i += searchBytes.Length ‘ 検索バイト列の分だけ進める
Continue While
End If
End If
‘ 一致しない場合は、元のバイトをそのまま書き込む
ms.WriteByte(originalData(i))
i += 1
End While
Return ms.ToArray()
End Sub

End Class

‘ — 呼び出し例 —
‘ Sub Main()
‘ Dim processor As New BinaryFileProcessor()
‘
‘ ‘ テキストファイル処理
‘ Dim textContent As String = processor.ReadUtf8FileWithBom(“C:\temp\input.txt”)
‘ processor.SaveAsShiftJis(“C:\temp\output_sjis.txt”, textContent)
‘
‘ ‘ バイナリファイル処理
‘ Dim binaryData As Byte() = processor.ReadBinaryFile(“C:\temp\image.jpg”)
‘ Dim searchPattern As Byte() = {&HFF, &HD8} ‘ JPEGのSOIマーカー (例)
‘ Dim replacePattern As Byte() = {&HFF, &HD9} ‘ JPEGのEOIマーカー (例)
‘ Dim modifiedData As Byte() = processor.ReplaceBytes(binaryData, searchPattern, replacePattern)
‘ processor.WriteBinaryFile(“C:\temp\image_modified.jpg”, modifiedData)
‘
‘ End Sub

VBAの`ADODB.Stream`でバイナリデータを操作する際の考え方は、VB.NETの`FileStream`などと共通する部分が多いです。`ADODB.Stream`は、VBAという制約の中で、これらの高度なファイル操作を実現するための、まさに「秘伝の書」と言えるでしょう。

まとめ:バイナリデータ操作の先へ

Excel VBAで`ADODB.Stream`を使いこなすことは、単なるファイルI/Oの自動化を超え、レガシーシステムとの連携、データ変換、そして高度なファイル操作といった、より複雑で価値の高いタスクを可能にします。

我々が直面するシステムは、時に古く、時に複雑怪奇です。しかし、その奥底には、確かなロジックと、それを支えるデータが存在します。`ADODB.Stream`のようなツールを使いこなし、バイナリデータの真の姿を理解することで、我々はそれらのシステムに息吹を吹き込み、新たな価値を創造することができるのです。

この技術は、一朝一夕に習得できるものではありません。しかし、その扉を開いた者だけが、VBAシステム開発の、そしてシステム間連携の、深遠なる世界へと足を踏み入れることができるのです。

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