見出し画像

ワードの本文とヘッダーでは表内のセルの指定の仕方が違った、の話(VBA)

1.はじめに

こんにちは。2021年11月からVBAを学び始めました♪
気づき点などをこちらに書き留めていこうと思います。
初心者の学習日誌ですので誤りなどもあるかもしれませんが、どうかご容赦ください。

2.ワードの本文とヘッダーでは表内のセルの指定の仕方が違った

さて、今回、業務で使えるコードを書いてみよう、ということで、日々やり取りする数十~数百の文書をまとめた共有フォルダから、ファイル名や文書の内容の一部をエクセルの管理表に転記する、という簡単なツールを考えてみたのですが、

画像1

上図の流れ③で、コードを書いているエクセルからワード文書の表のセルを参照する時、セルの指定の仕方がワード文書の本文とヘッダーでは違うことを知りました。

この点については本やネットで情報が少なかったのでメモしておこうと思います。

結論から言うと、ワードのヘッダーの表のセルはCells(1)というふうに書くのです。本文中の表のセルはCells(1, 1)とエクセル同様の書き方なのに不思議ですね。

例えばセクション1のヘッダーにある1つ目の表の左上のセルは、以下のように書きます。

ワードドキュメントオブジェクト.Sections(1).Headers(1).Range.Tables(1).Range.Cells(1)

3.ヘッダーの表内のセルのインデックスの順番を確かめてみた

ワードのヘッダーの表のセルは、本文中の表のセルのようにCells(1, 1)とは書かずにCells(1)というふうに書く、ということが分かりました。

このセルを指定する数字が表の左上から右下へ向かって大きくなるのは何となく予想できるのですが、ここで1つ疑問が湧いてきました。セルを縦に結合した場合、番号順はどうなるのでしょうか?

早速、以下のようなワード文書test.docxを作って検証してみました。

画像2

上のワードのヘッダー内の表では、Cells(1)からCells(17)までの数字を、ある附番ルールの仮説に基づいて入力してあります。この表のセルをfor文で回して値をDebug.Printした時、
Cells(1) Cells(2)...Cells(16) Cells(17)
と昇順にキレイに(番号が前後せずに)に出力されれば、仮説が(だいたいw)検証されることになります。

その附番ルールの仮説は次のとおりです。

上の行から1行ずつ、左から右に走査する
セルは走査の過程で最初にヒットした時にカウント(附番)される

そして、これを検証するために以下のようなコードを書いてみました。

Sub GetValueInWordHeaderTable()
   Dim objWord As Word.Application
   Set objWord = CreateObject("Word.Application")
   objWord.Visible = True
   Dim oWord As Document
   
   'オブジェクト変数oWordにドキュメントオブジェクトtest.docxを格納
   Set oWord = objWord.Documents.Open("C:\Users\hoge\test.docx", _
                                       ReadOnly:=True)
   
   'test.docxのセクション1のヘッダーの1つ目の表に対して操作を行う                                      
   With oWord.Sections(1).Headers(1).Range.Tables(1)
   
       Dim i As Long
       
       'i1~この表の最後のセルまでの間でループを作成
       For i = 1 To .Range.Cells.Count
           'Cells(i)に記載されている値を変数strに格納
           Dim str As String: str = .Range.Cells(i).Range.Text
           'strの余分な部分を整形してイミディエイトウインドウに出力
           Dim trimmedStr As String: trimmedStr = Left(str, Len(str) - 2)
           Debug.Print trimmedStr & " ";
       Next i
       
   End With
   objWord.Quit 'SaveChanges:=wdDoNotSaveChanges
   Set objWord = Nothing
End Sub

上記のコードを実行してみたところ、キレイにCells(1)からCells(17)まで昇順にDebug.Printされました~♪仮説は検証されました~(だいたいw)。めでたしめでたし。

4.ワードのヘッダーの表内のセルのインデックスの順番を利用した関数

さて、実際に業務で使うために書いたコードでは、以下のようなタスクを実行しています。

(1)作業対象とするフォルダをダイアログで選択させ、
(2)そのフォルダ内にあるファイルを巡回し、
(3)それが特定のファイル名のワード文書であれば開いて、
(4)そのワード文書のヘッダーの表内で特定の文字列を検索し、
(5)その文字列がヒットしたらその右隣のセルの値を取得してエクセルの最終行の次の行に書き込み(ワードはその後閉じる)
(6)フォルダ内の残りのファイルのファイル名をエクセルの最終行の次の行に書き込む(1セル内に全ファイル名を改行しつつ書き込む)
(7)(3)の特定ファイル名のワード文書が0個の場合はメッセージボックスで知らせる(処理は継続)
(8)(3)の特定ファイル名のワード文書が2個以上ある場合はメッセージボックスで処理の中止を確認する
(9)(3)の特定ファイル名のワード文書以外のファイルの個数をメッセージボックスに表示する

先程確かめたセルの附番ルールは、上記でいうと(5)の処理で利用しています。
具体的にいうと、表内のあるセルで検索対象の文字列がヒットした場合にその右隣のセルの値を返す以下のようなFunctionを作ったのですが、セルの附番原則が確かめられたので、「右隣のセル」をCells(i+1)と書くことができました。
右隣が+1なのはまあ当たり前かもしれませんね。でも、自分の手で確かめるとイレギュラーなセル結合がある表の場合も自信を持って書けそうです。

'与えられたキーワードを、指定されたワード文書のヘッダー内の表内で検索し、
'ヒットしたら即、そのキーワードを含むセルの1つ右隣のセルの値を返して処理を中止する関数
Function GetRightCellValue(ByVal KeyWord As String, ByVal tgtDoc As Document) As String
   Dim objTable As Object: Set objTable = tgtDoc.Sections(1).Headers(1).Range.Tables(1)
   Dim rightCel As Cell
   Dim str As String, rightStr As String, trimmedRightStr As String
   Dim i As Long
   
   With objTable
       For i = 1 To .Range.Cells.Count
           str = .Range.Cells(i).Range.Text
           
           If InStr(1, str, KeyWord) > 0 Then
               Set rightCel = .Range.Cells(i + 1)
               rightStr = rightCel.Range.Text
               trimmedRightStr = Left(rightStr, Len(rightStr) - 2)
               GetRightCellValue = trimmedRightStr
               Exit For
           End If
       Next i
  End With
​End Function

以上、Excel VBAでWORD文書のヘッダー内の表のセルを指定して値を取得するために、ヘッダー内のセルのインデックス順を確かめてみたご報告です。

5.このツールが業務デビューしていない理由

先程の(1)~(9)を行うツールを使えば、転記作業の時間が激減しそうです♪とっても楽しみ♪

ただ、まだ職場の環境ではトライしていません(涙)。

自分のPC上に作ったダミーの業務フォルダでは問題なく作動しているものの、環境や操作によって発生し得るエラーの場合分けを網羅できていないから、というのがその理由です。

早く一人前のコードに仕上げてラクしたいなぁ。。。


いいなと思ったら応援しよう!