和風スパゲティのレシピ

日本語でコーディングするExcelVBA

94本目:表範囲からHTMLのtableタグを作成

Excel&VBA解説サイト「エクセルの神髄」様出題の問題集、
VBA100本ノック」に対する私の回答と解説のページです。

100本ノックの出題リストはこちらから
excel-ubara.com

出題:表範囲からHTMLのtableタグを作成

#VBA100本ノック 94本目
表をHTMLの「table」に変換するFunctionを作成。
引数(セル範囲,見出行数)でHTMLで返す。
見出行数:>=1は指定行数を「thead」で出力。
セル結合:「td colspan="2" rowspan="2"」等々
空白セル:"nbsp;"を出力
※出力サンプルを参考に
※文字位置は不要、インデントは任意

変換表範囲
生成HTML

◇ 出題ページはこちら

ソースコード

サンプルファイルはこちら
[準備中]

メインモジュール

Option Explicit

' 100本ノック094:表範囲からHTMLのtableタグを作成
Function セル範囲をhtmlに変換(セル範囲 As Range, 見出し行数 As Long) As String

    Const ind = "    "

    Dim htmlテキスト As String
    
    htmlテキスト = "<table border=""1"">"
    
    Dim is見出し行あり As Boolean: is見出し行あり = (見出し行数 > 0)
    Dim isすべて見出し行 As Boolean: isすべて見出し行 = (見出し行数 = セル範囲.Rows.Count)
        
    ' 行のループ
    Dim R As Long
    For R = 1 To セル範囲.Rows.Count
        
        ' 行の開始タグ
        If R = 1 And is見出し行あり Then
            htmlテキスト = htmlテキスト & vbLf & ind & "<thead>"
        ElseIf R = 見出し行数 + 1 Then
            htmlテキスト = htmlテキスト & vbLf & ind & "<tbody>"
        End If
        
        Dim is見出し As Boolean
        is見出し = (R <= 見出し行数)
        
        ' 列のループ
        Dim C As Long
        For C = 1 To セル範囲.Columns.Count
            
            Dim 読取セル As Range
            Set 読取セル = セル範囲.Cells(R, C)
            
            Dim 出力値 As String
            If 読取セル.Value = "" Then
                出力値 = "&nbsp;"
            Else
                出力値 = 読取セル.Value
            End If
            
            ' 結合先頭セル・結合内部セル・単独セルごとに分岐
            Select Case True
            
                Case Is結合先頭セル(読取セル)
                    htmlテキスト = htmlテキスト & vbLf & ind & ind & ind & span設定HTML(読取セル, is見出し)
                    htmlテキスト = htmlテキスト & vbLf & ind & ind & ind & 出力値
                    htmlテキスト = htmlテキスト & vbLf & ind & ind & ind & IIf(is見出し, "</th>", "</td>")
                
                Case Is結合内部セル(読取セル)
                    ' なにもしない
                
                Case Else
                    htmlテキスト = htmlテキスト & vbLf & ind & ind & ind & IIf(is見出し, "<th>", "<td>")
                    htmlテキスト = htmlテキスト & vbLf & ind & ind & ind & 出力値
                    htmlテキスト = htmlテキスト & vbLf & ind & ind & ind & IIf(is見出し, "</th>", "</td>")

            End Select
    
        Next ' 列のループ
        
        ' 行の終了タグ
        htmlテキスト = htmlテキスト & vbLf & ind & ind & "</tr>"

        If R = 見出し行数 Then
            htmlテキスト = htmlテキスト & vbLf & ind & "</thead>"
        ElseIf R = セル範囲.Rows.Count And (isすべて見出し行 = False) Then
            htmlテキスト = htmlテキスト & vbLf & ind & "</tbody>"
        End If
        
    Next  ' 行のループ

    htmlテキスト = htmlテキスト & vbLf & "</table>"

    セル範囲をhtmlに変換 = htmlテキスト

End Function

' span設定HTML
Function span設定HTML(結合代表セル As Range, is見出し As Boolean) As String

    Dim 結合行数 As Long: 結合行数 = 結合代表セル.MergeArea.Rows.Count
    Dim 結合列数 As Long: 結合列数 = 結合代表セル.MergeArea.Columns.Count

    If is見出し Then
        Select Case True
            Case 結合行数 = 1: span設定HTML = "<th colspan=""" & 結合列数 & """>"
            Case 結合列数 = 1: span設定HTML = "<th rowspan=""" & 結合行数 & """>"
            Case Else:             span設定HTML = "<th rowspan=""" & 結合行数 & """ colspan=""" & 結合列数 & """>"
        End Select
    Else
        Select Case True
            Case 結合行数 = 1: span設定HTML = "<td colspan=""" & 結合列数 & """>"
            Case 結合列数 = 1: span設定HTML = "<td rowspan=""" & 結合行数 & """>"
            Case Else:             span設定HTML = "<td rowspan=""" & 結合行数 & """ colspan=""" & 結合列数 & """>"
        End Select
    End If

End Function

 

汎用関数

' 結合セル判定
Function Is結合先頭セル(判定セル As Range) As Boolean
    Is結合先頭セル = (判定セル.MergeCells And 判定セル.Address = 判定セル.MergeArea.Cells(1, 1).Address)
End Function
Function Is結合内部セル(判定セル As Range) As Boolean
    Is結合内部セル = (判定セル.MergeCells And 判定セル.Address <> 判定セル.MergeArea.Cells(1, 1).Address)
End Function
Function Is結合セル全体(判定エリア As Range) As Boolean
    Is結合セル全体 = (判定エリア.MergeCells And 判定エリア.Address = 判定エリア.Cells(1, 1).MergeArea.Address)
End Function

' 改行文字の置き換え
Function 文中改行を指定文字に置き換える(元テキスト As String, Optional 置換テキスト As String = " ") As String

    Dim 結果テキスト As String
    結果テキスト = Replace(元テキスト, vbCrLf, 置換テキスト)
    結果テキスト = Replace(結果テキスト, vbCr, 置換テキスト)
    結果テキスト = Replace(結果テキスト, vbLf, 置換テキスト)

    文中改行を指定文字に置き換える = 結果テキスト

End Function

 

解説

とくにひねりなく、愚直にHTMLタグを挿入していくコードです。

外側の行ループ時に<tbody><thead>を前後に挿入し、
内側の列ループ時に各データに<td><th>を前後に挿入しています。


セル結合の対応は、

  • 結合先頭セルであればspanタグを入れる
  • 結合内部セルであればなにもしない
  • 単独セルであれば通常のタグを入れる

という分岐になるためそんなに複雑なロジックにはなりませんでした。


問題は見出しとデータの判定の方で、
すべてがデータ、すべてが見出しという表もあり得ることを考慮すると、
どこの分岐で、どんな判定で組むがかなり分かれるポイントです。


今回は

  • 行ループの開始直後に<tbody><thead>
  • 行ループの終了直前に</tbody></thead>

に統一するため、

' 行の開始タグ
If R = 1 And is見出し行あり Then
    htmlテキスト = htmlテキスト & vbLf & ind & "<thead>"
ElseIf R = 見出し行数 + 1 Then
    htmlテキスト = htmlテキスト & vbLf & ind & "<tbody>"
End If

このようなIf~Else文で表現をしました。

<thead>は最初の出力のためループの外でやるのも手なのですが、
<tbody><thead>の出力箇所を揃えるためにこうしています。


このロジックを綺麗に書くために、コード冒頭で

Dim is見出し行あり As Boolean: is見出し行あり = (見出し行数 > 0)
Dim isすべて見出し行 As Boolean: isすべて見出し行 = (見出し行数 = セル範囲.Rows.Count)

 
この説明変数を用意しています。

これによりIf ~ And文が綺麗にかけていますので参考にしてみてください。