Excel VBAで表を取得するテクニック

プログラミング
サムネイル

・Excel VBAを使用して表の範囲を自動で判定するマクロの構築方法を知りたい
・変更が予想される表に対応できるよう柔軟なVBAマクロの作り方を知りたい
・シートに複数の表が存在する場合でも、それぞれの表の範囲を正確に取得する技術を知りたい

今回はExcelの集計においてVBAで表を取得するテクニックを紹介します。

ここでいう「表」とはシート内に書かれた値や罫線の集まりを指します

皆さんは表の集計でVBAのマクロを使ったことがあるでしょうか。その際にシートに書かれた表の範囲はどこからどこまでというのをマクロでどうやって判定するでしょうか。

今回はそれをネタにしたマクロの書き方をいろいろ紹介した内容となっています。

マクロの利用ケース

Excelを利用すると、しばしば表を集計するということがあります。表の出され方は人によって異なります。例えば以下があります。

  • 1シートに複数の表を載せて集計する
  • 1ファイルに複数のシートを載せて集計する
  • 大量のファイルを用意して集計する

2と3つ目については、WorkSheetsオブジェクトやFileオブジェクトを使って、ある程度の実装が可能です。

シートの表現次第ではピボットテーブルなどで解決できる場合がありますが、今回はそれもできないケースを想定しています。

しかし、以下のように1シートに表がたくさんあった場合に、1つのシートにどういう処理施せばよいでしょうか。

まず上記の例では表が1つで最終セルが3列目、7行目です。そのため単純にやろうものならVBAのマクロを使うときは以下になります。

 Dim rowLast, colLast As Integer
 Dim i, j As Integer

 rowLast = 3
 colLast = 7
    
 MsgBox "最終行は" & rowLast & vbCrLf & _
        "最終列は" & colLast
 For i = 2 To rowLast
     For j = 2 To colLast
         '表内データ集計
     Next
 Next

ただしこれはこのシート限定であり、直打ちで7を使うのは行が増えた時などにリスクがあります。

今回の利用ケースはVBAのマクロで表を集計するとき、表の範囲を自動で判定するケースを想定してまとめました。その方法はなにかを以降で紹介します。

広告

基本的な表の最終行と最終列の取得方法

ループで探索する

前章の表を扱う上で、まず使えそうなのは単純にVBAでループを使って探索する方法は以下です。

 Dim i, j As Integer
    
 i = 3
 j = 2
 While Cells(i, j) <> ""
     i = i + 1
 Wend
    
 MsgBox "最終行は" & i

単純にデータが上の行からどこまで入っているかを行で追うものとなっています。ロジックに悩んだらまず使えそうですね。

VBAのプロパティで最終セルを取得する

前述の方法だと表が二つになった場合、前述のループでは最終セルは取れないので次はVBAのプロパティをとってみましょう。

具体的には以下のコードになります。ただ表ごとの最終セルは取れていないことに注意ください(あくまでVBAの機能としてです)。

 Dim rowLast, colLast As Integer

 rowLast = Cells.SpecialCells(xlCellTypeLastCell).row
 colLast = Cells.SpecialCells(xlCellTypeLastCell).Column
    
 MsgBox "最終行は" & rowLast & vbCrLf & _
        "最終列は" & colLast

SpecialCells(xlCellTypeLastCell)の最終行の判定ですが、以下のように罫線が引かれた場合は、罫線込みで判定します。以下の例ですと「最終行は11 最終列は4」です。

実際のケースを想定した取得方法

上記で基礎的なことは分かったとして、実際に1つのシートに表が複数ある場合はどう扱うかを本章で述べます。例えば以下のようにシートに2つの表があった場合を想定します。

どうやって表を特定するのか、以降でまとめています。

ループを駆使して、文字検索からの表の範囲を出してみる

真っ先に思いついたのはやはりループで探索することですね。2つも表があると、各表の境界線を活用する必要があり、以下の手順で書いてみました。

① シートの最終セル番地を取得する

② ①で取得した最終セルまでの範囲で「表1」「表2」の文字列をループで探す
  (見つからないならそのまま終了)

③ 表の名前が見つかったらオフセットで一つ下にずらす(表題の下に表があるという体)

④ 表の罫線の終わりまで探索する

⑤ 罫線の終わりのセル番地を取得する

Sub 集計_Click()
    Dim rowLast, colLast As Integer
    Dim tableLastCell As range

    rowLast = Cells.SpecialCells(xlCellTypeLastCell).row
    colLast = Cells.SpecialCells(xlCellTypeLastCell).Column
    
    MsgBox "最終行は" & rowLast & vbCrLf & _
        "最終列は" & colLast

    Set tableLastCell = getTableLastCell(ActiveSheet, "表1", rowLast, colLast, 1, 0)
    MsgBox tableLastCell.row
    Set tableLastCell = getTableLastCell(ActiveSheet, "表2", rowLast, colLast, 1, 0)
    MsgBox tableLastCell.row
End Sub

'表の最後のセル番地を取得
Function getTableLastCell(ByRef ws As Variant, ByVal tableName As String, _
                        ByVal rowCount As Integer, ByVal colCount As Integer, _
                        ByVal offsetRow As Integer, ByVal offsetCol) As range
    Dim i, j As Integer
    Dim rowStart, colStart As Integer
    Dim rowLast, colLast As Integer
    
    For i = 1 To rowCount
        For j = 1 To colCount
            If ws.Cells(i, j).Value = tableName Then
                rowStart = i + offsetRow
                colStart = j + offsetCol
                GoTo BreakLabel
            End If
        Next
    Next
BreakLabel:
    i = rowStart
    j = colStart
    '表の最後のセルの行番号を取得する
    While ws.Cells(i, colStart).Borders(xlEdgeBottom).LineStyle <> xlLineStyleNone
        rowLast = i
        i = i + 1
    Wend

    '表の最後のセルの列番号を取得する
    While ws.Cells(rowStart, j).Borders(xlEdgeBottom).LineStyle <> xlLineStyleNone
        colLast = j
        j = j + 1
    Wend
    Set getTableLastCell = ws.Cells(rowLast, colLast)
End Function

ただし、これでは文字探索のループが非常に効率悪いので、まだ工夫の余地があります。

VBAの標準関数・プロパティ(Find、CurrentRegion)を使ってみる

前述ではループを使って文字探索してましたが、さすがにVBAの機能として文字探索とかはあるだろうと思い、VBAの標準関数やプロパティを使ってみました。

まず使ったのがFind関数で、これは文字列探索で使えます。次にCurrentRegionですが、これは指定したセル番地から表になっている範囲を取得します。

CurrentRegionでは罫線のみの行は取得しないです。

その試行錯誤の結果、以下の結果となりました。

Sub 集計_Click()
    Dim rowLast, colLast As Integer
    Dim tableRange As range

    rowLast = Cells.SpecialCells(xlCellTypeLastCell).row
    colLast = Cells.SpecialCells(xlCellTypeLastCell).Column

    Set tableRange = getTableRange(ActiveSheet, "表1", rowLast, colLast, 1, 0)
    MsgBox "最初のセル番地は、行:" & tableRange.row & "、列:" & tableRange.Column & vbCrLf _
            & "最後のセル番地は、行:" & tableRange.Rows(tableRange.Rows.Count).row _
            & "、列:" & tableRange.Columns(tableRange.Columns.Count).Column
    Set tableRange = getTableRange(ActiveSheet, "表2", rowLast, colLast, 1, 0)
    MsgBox "最初のセル番地は、行:" & tableRange.row & "、列:" & tableRange.Column & vbCrLf _
            & "最後のセル番地は、行:" & tableRange.Rows(tableRange.Rows.Count).row _
            & "、列:" & tableRange.Columns(tableRange.Columns.Count).Column
End Sub

'表の範囲を取得
Function getTableRange(ByRef ws As Variant, ByVal tableName As String, _
                        ByVal rowCount As Integer, ByVal colCount As Integer, _
                        ByVal offsetRow As Integer, ByVal offsetCol) As range
    Dim rowStart, colStart As Integer
    Dim rowLast, colLast As Integer
    Dim myRange As range
    
    '文字列探索
    Set myRange = ws.range(Cells(1, 1), Cells(rowCount, colCount)).Find(tableName, LookAt:=xlPart)
    If myRange Is Nothing Then
        Set myRange = Nothing
    Else
        '表の最初のセル番地を取得する
        rowStart = myRange.row + offsetRow
        colStart = myRange.Column + offsetCol
        '表の最後のセル番地を取得する
        rowLast = ws.Cells(rowStart, colStart).CurrentRegion.row + ws.Cells(rowStart, colStart).CurrentRegion.Rows.Count - 1
        colLast = ws.Cells(rowStart, colStart).CurrentRegion.Column + ws.Cells(rowStart, colStart).CurrentRegion.Columns.Count - 1
        Set myRange = ws.range(Cells(rowStart, colStart), Cells(rowLast, colLast))
    End If
    
    Set getTableRange = myRange
End Function

コードはすっきりしましたが、わりと技術的には複雑ですね。効率的に組むなら高度な技術が必要な場合もあります。

広告

パーフェクトExcel VBA [ 高橋宣成 ]

価格:3608円
(2024/4/6 20:13時点)
感想(3件)

たった1秒で仕事が片づくExcel自動化の教科書【増強完全版】【電子書籍】[ 吉田拳 ]

価格:2178円
(2024/4/6 20:17時点)
感想(0件)

まとめ

この記事では、Excelの集計作業において、VBAを用いて表の範囲を自動で判定するマクロの構築方法について解説しました。表の形式が異なっていたり、追加が見込まれる状況でも柔軟に対応できるように、以下の技術を紹介しました。

  1. ループで探索する
    初歩的な探索法として、ループを使って直接、表の最終行や列を探る方法を説明しました。
  2. VBAのプロパティで最終セルを取得する
    SpecialCellsメソッドを使って表の最終セルを簡単に取得する方法を紹介しましたが、この方法はあくまで全シートの最終セルであり、特定の表ごとの範囲は取得できません。
  3. ループを駆使する細かな表範囲の取得
    ループと条件分岐を駆使し、特定の表の名前を検索してからその表の範囲を特定する詳細な方法を示しました。
  4. Find関数とCurrentRegionプロパティの利用
    VBAの組み込み機能を活用して、Find関数で表の名前を検索し、CurrentRegionで検索したセルを起点に表全体の範囲を取得する最も効率的な方法を説明しました。

各手法はケースに応じて適切なものを選ぶ必要があり、複雑なシートの構造には高度な技術も必要です。しかし、VBAを理解して適切なマクロを構築すれば、Excelの集計作業が大幅に効率化されます。初心者から上級者まで、VBAを使いこなして作業の自動化を図りましょう。

参考文献

コメント

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