【入門編】【中級者向け】表の各行を独立したファイルとして分割保存するドキュメント自動生成術 – Word VBA解析バイブル

スポンサーリンク
スポンサーリンク

【極限のWord VBA】1つの表から「個別ドキュメント」を量産する:オブジェクト操作の真髄

こんにちは。現場のコードを支えるアーキテクトとして、日々自動化の荒波を渡っているエンジニアです。

「マクロの記録」を使って、なんとか作業を自動化できた。そんな成功体験を持っているあなた。素晴らしい一歩です。しかし、もしあなたが「1つの大きな一覧表から、1行ずつ切り出して、別々のファイルとして保存したい」という要望に直面したとき、マクロの記録だけでは立ち往生してしまうはずです。

なぜなら、それは「操作の記録」ではなく、「データの構造(オブジェクト)を制御する設計」が必要な領域だからです。

今日は、見積書や請求書の発行業務を一瞬で終わらせるための、中級者への登竜門となるテクニック「表の行分割・個別保存術」を伝授します。ここをマスターすれば、あなたは単なる「マクロ使い」から、業務を設計する「自動化エンジニア」へと進化できるでしょう。

1. 思考の転換: 「コピー&ペースト」から「オブジェクトの生成」へ

初学者が陥りがちな考え方は、「画面上の文字をコピーして、新しいファイルに貼り付ける」という、人間と同じ動きをコードに書こうとすることです。

しかし、プロのエンジニアは違います。私たちは、Wordというアプリケーションの中に存在する「オブジェクト(物体)」を操作します。

今回の設計図(アルゴリズム)はこうです:

1. ソース(元データ)の特定: どのドキュメントの、どの表を読み取るか。
2. ループ処理(反復): 表の行を1行ずつ、上から順番に見ていく。
3. ターゲット(新ドキュメント)の生成: 行ごとに、新しい `Document` オブジェクトをメモリ上に作り出す。
4. データの転送: 元のセルの内容を、新しいドキュメントのセルへ流し込む。
5. ライフサイクルの完結: 名前をつけて保存し、開いているドキュメントを閉じる。

この「新しいものを作り、使い、捨てる(閉じる)」というオブジェクトのライフサイクルを意識することが、メモリを無駄遣いせず、高速で安定したプログラムを書く鍵となります。

2. 実装: 行分割・個別保存のマスターコード

では、実際に動くコードを見てみましょう。このコードは、単に文字を写すだけでなく、実務で遭遇する「セルの末尾文字問題」にも配慮した、堅牢な設計にしています。

Option Explicit

''' <summary>
''' 表の各行を抽出し、個別のWordファイルとして保存する
''' </summary>
Sub SplitTableRowsToFiles()
    Dim srcDoc As Document
    Dim targetDoc As Document
    Dim srcTable As Table
    Dim targetTable As Table
    Dim targetPath As String
    Dim i As Long, col As Integer
    Dim rowText As String
    Dim fileName As String
    
    ' --- 設定エリア ---
    ' 保存先フォルダ(末尾に必ず \ をつける)
    targetPath = ThisDocument.Path & "\Output\"
    
    ' 保存先フォルダが存在しない場合は作成する
    If Dir(targetPath, vbDirectory) = "" Then
        MkDir targetPath
    End If
    
    ' 現在開いているドキュメントの1番目の表を対象とする
    Set srcDoc = ActiveDocument
    If srcDoc.Tables.Count = 0 Then
        MsgBox "対象となる表が見つかりません。", vbCritical
        Exit Sub
    End If
    Set srcTable = srcDoc.Tables(1)
    
    ' 高速化のための魔法:画面更新を停止する
    Application.ScreenUpdating = False
    
    On Error GoTo ErrorHandler

    ' --- メインループ(2行目から最終行まで。1行目はヘッダーと想定) ---
    For i = 2 To srcTable.Rows.Count
        
        ' 1. 新しいドキュメントを生成
        Set targetDoc = Documents.Add
        
        ' 2. 新しいドキュメントに、元の表と同じ列数の表を作成
        ' ここでは、1行目(ヘッダー)をコピーするのではなく、
        ' 新規ドキュメントに新しい表を構築するアプローチをとります。
        Set targetTable = targetDoc.Tables.Add(Range:=targetDoc.Range, _
                          NumRows:=1, NumColumns:=srcTable.Columns.Count)
        
        ' 表のスタイルを整える(必要に応じて変更してください)
        targetTable.Borders.Enable = True
        
        ' 3. データの流し込み
        ' 列のループ
        For col = 1 To srcTable.Columns.Count
            ' 【重要】Wordのセル内のテキストには、末尾に特殊な制御文字が含まれるため
            ' そのまま取得すると、保存や結合時にエラーや予期せぬ改行が発生します。
            ' Left関数を使って、制御文字を除去した純粋なテキストのみを取得します。
            rowText = CleanTableCellText(srcTable.Cell(i, col).Range.Text)
            
            ' 新しい表のセルに書き込む
            targetTable.Cell(1, col).Range.Text = rowText
        Next col
        
        ' 4. ファイル名の決定(ここでは1列目の内容をファイル名にする例)
        ' ファイル名に使えない文字が含まれているとエラーになるため注意
        fileName = CleanFileName(CleanTableCellText(srcTable.Cell(i, 1).Range.Text))
        If fileName = "" Then fileName = "Row_" & i
        
        ' 5. 保存とクローズ
        targetDoc.SaveAs2 FileName:=targetPath & fileName & ".docx"
        targetDoc.Close SaveChanges:=False
        
        ' オブジェクトの参照を解放(メモリ管理の基本)
        Set targetDoc = Nothing
    Next i

    ' 画面更新を再開
    Application.ScreenUpdating = True
    MsgBox "処理が完了しました!" & vbCrLf & "保存先: " & targetPath, vbInformation
    Exit Sub

ErrorHandler:
    Application.ScreenUpdating = True
    MsgBox "エラーが発生しました。" & vbCrLf & "内容: " & Err.Description, vbCritical
End Sub

''' <summary>
''' Wordのセル特有の制御文字(末尾の改行マーク等)を取り除く関数
''' </summary>
Private Function CleanTableCellText(txt As String) As String
    ' セルの末尾にある特殊文字(Chr(13) & Chr(7))を除去
    ' これを行わないと、ファイル名に不正な文字が含まれたり、結合時に崩れたりします。
    Dim cleanTxt As String
    cleanTxt = txt
    If Len(cleanTxt) > 2 Then
        cleanTxt = Left(cleanTxt, Len(cleanTxt) - 2)
    End If
    CleanTableCellText = cleanTxt
End Function

''' <summary>
''' ファイル名として使えない禁止文字を置換する関数
''' </summary>
Private Function CleanFileName(txt As String) As String
    Dim invalidChars As Variant
    Dim v As Variant
    invalidChars = Array("\", "/", ":", "*", "?", """", "<", ">", "|")
    CleanFileName = txt
    For Each v In invalidChars
        CleanFileName = Replace(CleanFileName, v, "_")
    Next v
End Function

3. ここがプロの分かれ道:陥りやすい「3つの罠」

このコードを使いこなすために、私が実戦で学んだ「罠」を共有しておきます。

① セル末尾の「見えない文字」問題

Word VBAにおいて、`Cell.Range.Text` で取得できる文字列には、必ずと言っていいほど「セル終了マーカー(End-of-Cell Marker)」という特殊な文字が含まれています。これは、見た目には何も見えませんが、`Chr(13)`(キャリッジリターン)と `Chr(7)`(ベル文字)の組み合わせです。
これをそのままファイル名に使おうとすると、「ファイル名が無効です」というエラーで止まります。上記のコードでは `CleanTableCellText` 関数を作り、これを確実に除去しています。

② パフォーマンスの重み: `ScreenUpdating`

大量の行(例えば100行以上)を処理する場合、画面がチカチカと更新されるのを放置すると、処理速度が劇的に低下します。
`Application.ScreenUpdating = False` を宣言することで、Wordは「画面の描き換え」という無駄な作業をスキップし、計算に集中できるようになります。これはプロのコードにおける「お作法」です。

③ オブジェクトの解放(メモリ管理)

ループの中で `Documents.Add` を繰り返すと、メモリ上に大量のドキュメント情報が蓄積されます。処理が終わったら `Set targetDoc = Nothing` とすることで、使い終わったオブジェクトを明示的に解放する習慣をつけてください。これが、長時間動かしてもPCが重くならない「安定したシステム」を作る秘訣です。

最後に:次は「テンプレート」へ

今回のコードは、新しいドキュメントに「真っ白な状態から表を作る」手法をとりました。しかし、実務では「決まった書式の請求書フォーマットに、データだけを流し込みたい」というケースが多いはずです。

その場合は、`Documents.Add` の代わりに `Documents.Open(“Template.docx”)` を使い、あらかじめ用意したテンプレートの特定のセルへ値を流し込む形へと進化させてください。

ここをクリアすれば、Word VBAの基本はバッチリですよ。
一歩ずつ、確実に「自動化のアーキテクト」への階段を登っていきましょう。応援しています!

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