
ワードの本文とヘッダーでは表内のセルの指定の仕方が違った、の話(VBA)
1.はじめに
こんにちは。2021年11月からVBAを学び始めました♪
気づき点などをこちらに書き留めていこうと思います。
初心者の学習日誌ですので誤りなどもあるかもしれませんが、どうかご容赦ください。
2.ワードの本文とヘッダーでは表内のセルの指定の仕方が違った
さて、今回、業務で使えるコードを書いてみよう、ということで、日々やり取りする数十~数百の文書をまとめた共有フォルダから、ファイル名や文書の内容の一部をエクセルの管理表に転記する、という簡単なツールを考えてみたのですが、
上図の流れ③で、コードを書いているエクセルからワード文書の表のセルを参照する時、セルの指定の仕方がワード文書の本文とヘッダーでは違うことを知りました。
この点については本やネットで情報が少なかったのでメモしておこうと思います。
結論から言うと、ワードのヘッダーの表のセルは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を作って検証してみました。
上のワードのヘッダー内の表では、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
'iが1~この表の最後のセルまでの間でループを作成
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上に作ったダミーの業務フォルダでは問題なく作動しているものの、環境や操作によって発生し得るエラーの場合分けを網羅できていないから、というのがその理由です。
早く一人前のコードに仕上げてラクしたいなぁ。。。