和風スパゲティのレシピ

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

ワークシートが存在するかチェックする関数

ある名前のワークシートが、ブックの中に存在するかを調べる関数を紹介します。

Worksheets(シート名)でシートを指定するときに、
「インデックスが有効範囲にありません」エラーを回避するために使いますね。

やり方はとても単純です。
シートを1つずつ見ていき、目的のシート名と一致しているシートがあるか調べます。

ソースコード

Function Isシートが存在する(判定シート名 As String, 指定ブック As Workbook) As Boolean

    ' ブック内の全シートを走査
    Dim ws As Worksheet
    For Each ws In 指定ブック.Worksheets

        ' シート名が一致したらTrueを返してExit
        If ws.Name = 判定シート名 Then
            Isシートが存在する = True
            Exit Function
        End If

    Next

End Function

使い方

    If Isシートが存在する("売上データ", wb処理ブック) Then
        Set ws集計シート = wb処理ブック.Worksheets("売上データ")
    Else
        Exit Sub
    End If

コードの解説

教本のような「コレクションに対するFor Each」ですね。


For Each でブック内のすべてのシートを走査し、
シート名が判定シート名と一致したらTrueを返してExitします。

Exitせずにループが終わったら、一致したシートがなかったということで、
Booleanの初期値であるFalseが返るしくみです。


もし、この初期値の利用がイメージしづらかったら、関数の最後を

    Next

    Isシートが存在する = False
End Function

と書くと理解しやすいです。
For Each ~ Next をExitせずに突破出来たら、FalseにしてEndです。

エラーを利用した別の判定方法

お忙しい方は、↑の関数をコピペして早速使ってください。

お時間に余裕がある方は、↓のテクニックも読んでみてください。


※ 内容はブックを現在開いているかチェックする関数で解説したものと同じです。
 すでにこちらを読まれている方は、ここから先を読む必要はありません。

エラーが発生するかどうかで判定を行う

冒頭の関数では、単純なループ処理で丁寧に判定しましたが、
実はループ処理を使わずに書くこともできます。

目的の「指定ブック.Worksheets(判定シート名)」を関数内で指定してしまって、
それがエラーになるかどうかを調べる方法です。

Function Isシートが存在する(判定シート名 As String, 指定ブック As Workbook) As Boolean
    On Error Resume Next ' この関数内ではエラーをスキップ
    
    ' 適当な変数に、目的のシートをセットしてみる
    Dim tmp As Worksheet
    Set tmp = 指定ブック.Worksheets(判定シート名) ' シートがなければこの文はスキップされる

    ' 変数へのセットがスキップされていないかで判定する
    If Not tmp Is Nothing Then
        Isシートが存在する = True
    End If
        
End Function

シートが存在しない場合は、変数へのSetが「インデックスが有効範囲にありません」エラーになります。
それをOn Error Resume Nextでスキップしているわけですね。


スキップされた場合は、tmpは初期値であるNothingのままなので、
次のIf文でそこを判定しています。


なお、On Error Resume Next は、その関数の中だけで有効です。
「この関数を呼んだ後は、呼び出し元のエラーも無視されるようになる」ことはありません。

ただし、On Error Resume Nextを使った関数の中で、別の関数をさらに呼び出す場合は、その呼び出した関数でもエラーが無視されるので気を付けてください。


子でOn Error Resume Nextしても、親でOn Error Resume Nextされませんが、
親でOn Error Resume Nextすると、子もOn Error Resume Nextされます。


On Error Resume Next で、スキップしながら目的の処理をやってみて、
エラーが出るかどうかで状態を判定するのは、結構便利です。

いろいろな場面で活躍するので、テクニックとして覚えておきましょう。

さらに短く書ける

先ほどはエラーの活用を説明するために、丁寧な関数にしましたが、
せっかくループしなくて済むOn Error Resume Nextを使うのですから、
もっと短い行数で終わらせてみましょう。


まず、分かりやすい様に

    If Not tmp Is Nothing Then
        Isシートが存在する = True
    End If

こう書きましたが、これは

Isシートが存在する = Not tmp Is Nothing

こう書くこともできます。

「条件を満たすなら、結果はTrue」は、
「結果 = 条件を満たすかどうか」と書けますからね。

中身が1本しかないIf文は、慣れたらこっちで書きましょう。
単純な処理はなるべく短い行数で書いて、その分複雑な処理を丁寧に書くと、コードが読みやすくなります。


あとは、一時変数tmpも消すと、最終的にはこのコードになります。

Function Isシートが存在する(判定シート名 As String, 指定ブック As Workbook) As Boolean
    On Error Resume Next
    Isシートが存在する = Not 指定ブック.Worksheets(判定シート名) Is Nothing
End Function

短い!

どっちの書き方がいいの?

どちらも一長一短で、ケースバイケースです。


1万回呼び出す関数なら、行数が短い方が、処理が速くていいかもしれません。

でも、今回の「シート名」の判定は、マクロのスタート時にIf文で1回使う程度のことが多いので、速度は考えない方が良いですね。


あとで関数を書き替えたり、コピペして別の関数に使いたい場合は、
丁寧にループ処理を書いておいた方が便利です。


例えば、シート名には、「日報0401」「日報0402」や、「売上202001」など、
識別用のパラメータ(日付とか)がついているやつも結構ありますよね?

この「シート名の部分一致を判定する」関数は、
「丁寧なループで作った関数」のIf ~ Then部分を、

If ws.Name = 判定シート名 Then
' ↓書き換え
If InStr(ws.Name, 判定シート名の前半部) = 1 Then

このように書き換えるだけで作成できます。


エラー技の方は、こういった活用はできません。

※エラー技のテンプレートとして使うことはできるので、
 On Error Resume Next の参考コードにしたい場合に、こっちを採用するのはアリ。


長々解説しておいてあれですが、
コピペして別の関数を作るメリットの方が大きそうなので、
今回は↑の丁寧なループ処理関数がおすすめです。

惜しくも不採用となってしまいましたが、
On Error Resume Next でスキップしながらやりたい処理をやってみて、
エラーが出るかどうかを関数で試すのは結構便利です。

いろいろな場面で活躍するので、テクニックとして覚えておきましょう。