【VBAリファレンス】VBA IsNumeric関数の真髄:数値判定の落とし穴を回避し、堅牢なコードを構築する究極ガイド

スポンサーリンク

VBAにおけるデータ処理の根幹をなす要素の一つが「データ型の管理」です。特に、ユーザーからの入力や外部データソースからの読み込みにおいて、値が数値であるかどうかを正確に判定することは、アプリケーションの安定性と信頼性を確保する上で不可欠となります。本稿では、その中心的な役割を担う`IsNumeric`関数について、その基本的な使い方から、多くの開発者が見落としがちな「落とし穴」、そして実務で堅牢なコードを構築するためのベストプラクティスまで、ベテラン講師の視点から徹底的に解説します。

概要

`IsNumeric`関数は、VBAが提供するデータ型判定関数群の一つで、指定された式が数値として評価できるかどうかを判定し、ブール値(TrueまたはFalse)を返します。一見すると単純な関数に見えますが、その内部的な挙動はVBAのデータ型システム、特に`Variant`型の特性と密接に結びついており、単に「数字かどうか」を判断するだけではない奥深さを持っています。この関数を正しく理解し活用することは、実行時エラーの回避、データの整合性保持、そしてユーザーフレンドリーなアプリケーション開発に直結します。

多くのVBA開発者が`IsNumeric`を「文字列に数字だけが含まれているか」をチェックする目的で利用しますが、実はこの関数はそれ以上の意味を持ちます。例えば、日付や時刻、ブール値といった一見数値とは異なるデータも、特定の条件下で`IsNumeric`がTrueを返すことがあります。このような挙動の背景にあるVBAの設計思想を理解することが、真に堅牢なコードを書くための第一歩となるでしょう。

詳細解説

`IsNumeric`関数の基本的な構文は以下の通りです。

`IsNumeric(Expression)`

ここで`Expression`は、数値として評価するかどうかをテストする任意の式(変数、リテラル、関数の戻り値など)です。戻り値は、`Expression`が数値として評価できる場合は`True`、それ以外の場合は`False`となります。

では、「数値として評価できる」とは具体的に何を意味するのでしょうか。`IsNumeric`が`True`を返す主なケースと、多くの開発者が混乱しやすい「落とし穴」を掘り下げていきましょう。

IsNumericがTrueを返すケース

  • 明示的な数値型: `Integer`, `Long`, `Single`, `Double`, `Currency`, `Decimal` など、VBAの数値型は当然ながら`True`を返します。
  • 数値形式の文字列: `IsNumeric(“123”)` や `IsNumeric(“3.14”)` は `True` を返します。これは、文字列が数値に変換可能であれば`True`となるという`IsNumeric`の重要な特性を示しています。
  • 指数表記の文字列: `”1.23E+5″` のような指数表記の文字列も、数値として解釈できるため`True`を返します。
  • 日付/時刻型: `IsNumeric(CDate(“2023/01/01”))` は `True` を返します。VBA内部では、日付と時刻は1900年1月1日を起点としたDouble型の数値として保持されているためです。これは`IsNumeric`の大きな「落とし穴」の一つであり、日付の判定には`IsDate`関数を使うべきです。
  • ブール型: `IsNumeric(True)` は `True` を、`IsNumeric(False)` も `True` を返します。VBAでは`True`が内部的に`-1`、`False`が`0`として扱われるためです。これもまた、ブール値の判定には`IsBoolean`関数を使うべきであることを示唆しています。
  • Empty値: `Variant`型の変数が初期値の`Empty`状態である場合、`IsNumeric(Empty)` は `True` を返します。`Empty`は数値コンテキストでは`0`として扱われるためです。ただし、`IsNull(Empty)`は`False`、`IsEmpty(Empty)`は`True`である点に注意が必要です。

IsNumericがFalseを返すケース

  • 非数値の文字列: `”abc”`, `”123a”`, `”Hello World”` など、数値に変換できない文字列は`False`を返します。
  • 空文字列: `IsNumeric(“”)` は `False` を返します。これは`Empty`が`True`を返すのとは対照的であり、特にユーザー入力の検証において重要な違いとなります。
  • Null値: `IsNumeric(Null)` は `False` を返します。`Null`は「データがない」ことを意味し、数値としては解釈されません。
  • オブジェクト: VBAのオブジェクト(例: `Range`オブジェクト、`Worksheet`オブジェクト)は数値として評価できないため、`False`を返します。
  • エラー値: `CVErr(xlErrDiv0)` のようなエラー値も`False`を返します。

「数値として評価できる」≠「数値型」の理解

`IsNumeric`関数を理解する上で最も重要なポイントは、「`IsNumeric`が`True`を返す」ことと「その値が数値型の変数に格納されている」ことは同義ではない、という点です。例えば、`IsNumeric(“123”)` は `True` を返しますが、`”123″` は依然として`String`型です。この`String`型の値を直接数値演算に用いると、VBAは暗黙的に型変換を試みますが、もし文字列が数値以外の文字を含んでいた場合、実行時エラーが発生する可能性があります。

このため、`IsNumeric`は、`CInt`, `CLng`, `CDbl` などの明示的な型変換関数を使用する前に、値が安全に数値に変換可能であるかを確認するための「ゲートキーパー」として活用されるべきです。

サンプルコード

以下に、`IsNumeric`関数の様々な挙動と、実務での活用例を示すサンプルコードを提示します。


Sub IsNumeric徹底検証()
Dim v As Variant
Dim s As String
Dim d As Date
Dim b As Boolean
Dim obj As Object

Debug.Print "--- 基本的な数値と文字列 ---"
v = 123
Debug.Print "IsNumeric(123): " & IsNumeric(v) ' True
v = 3.14
Debug.Print "IsNumeric(3.14): " & IsNumeric(v) ' True
v = "456"
Debug.Print "IsNumeric(""456""): " & IsNumeric(v) ' True (数値形式の文字列)
v = "-789.01"
Debug.Print "IsNumeric(""-789.01""): " & IsNumeric(v) ' True
v = "1.23E+5"
Debug.Print "IsNumeric(""1.23E+5""): " & IsNumeric(v) ' True (指数表記)
v = "abc"
Debug.Print "IsNumeric(""abc""): " & IsNumeric(v) ' False
v = "123a"
Debug.Print "IsNumeric(""123a""): " & IsNumeric(v) ' False (非数値文字を含む)

Debug.Print vbCrLf & "--- 落とし穴となりやすいデータ型 ---"
' 日付/時刻
d = CDate("2023/10/27 15:30:00")
Debug.Print "IsNumeric(日付): " & IsNumeric(d) ' True (内部的にはDouble)
Debug.Print "IsDate(日付): " & IsDate(d) ' True (日付の判定にはIsDateを使うべき)

' ブール型
b = True
Debug.Print "IsNumeric(True): " & IsNumeric(b) ' True (内部的には-1)
b = False
Debug.Print "IsNumeric(False): " & IsNumeric(b) ' True (内部的には0)
Debug.Print "IsBoolean(True): " & IsBoolean(True) ' True (ブール値の判定にはIsBooleanを使うべき)

Debug.Print vbCrLf & "--- 特殊な値 ---"
' 空文字列
s = ""
Debug.Print "IsNumeric(""""): " & IsNumeric(s) ' False

' Null値
On Error Resume Next ' Nullを直接代入するとエラーになるため
v = Null
On Error GoTo 0
Debug.Print "IsNumeric(Null): " & IsNumeric(v) ' False

' Empty値 (Variant型の初期値)
Dim vEmpty As Variant
Debug.Print "IsNumeric(Empty): " & IsNumeric(vEmpty) ' True (数値コンテキストでは0として扱われる)
Debug.Print "IsEmpty(Empty): " & IsEmpty(vEmpty) ' True

' オブジェクト
Set obj = ThisWorkbook.Sheets(1)
Debug.Print "IsNumeric(Sheetオブジェクト): " & IsNumeric(obj) ' False
Set obj = Nothing

Debug.Print vbCrLf & "--- 実用的な入力検証例 ---"
Dim userInput As String
userInput = InputBox("数値を入力してください:")

If IsNumeric(userInput) Then
' IsNumericがTrueでも、空文字列はFalseなので、ここでは考慮不要だが、
' IsEmpty(userInput) が True の場合は IsNumeric も True になるケース (Empty) があるため、注意深く扱う。
' 今回の InputBox の戻り値は String 型なので、Empty になることはない。
If userInput <> "" Then ' 空文字列ではないことを確認
Dim numValue As Double
numValue = CDbl(userInput)
Debug.Print "入力された値は数値です: " & numValue
' さらに数値の範囲チェックなどを行う
If numValue >= 0 And numValue <= 100 Then Debug.Print "入力された数値は0~100の範囲内です。" Else Debug.Print "入力された数値は0~100の範囲外です。" End If Else Debug.Print "何も入力されませんでした。" End If Else Debug.Print "入力された値は数値ではありません。" End If ' より厳密な数値判定ルーチン (日付やブール値を除外) Function IsStrictlyNumeric(ByVal expression As Variant) As Boolean IsStrictlyNumeric = False If Not IsEmpty(expression) And Not IsNull(expression) Then If IsNumeric(expression) Then ' 日付やブール値を除外する If Not IsDate(expression) And Not IsBoolean(expression) Then ' 文字列が数値形式だが、日付やブール値として解釈されない場合 ' 例: "123", "3.14", "1.23E+5" ' ただし、"2023/10/27" のような文字列は IsDate も True になるため、 ' IsNumeric(expression) が True かつ IsDate(expression) が True の場合は、 ' 数値文字列としてではなく日付文字列として扱いたい場合にこのロジックが有効。 ' ここでは "厳密な数値" を判定したいので、日付やブール値として解釈されなければTrue。 IsStrictlyNumeric = True ElseIf VarType(expression) = vbInteger Or _ VarType(expression) = vbLong Or _ VarType(expression) = vbSingle Or _ VarType(expression) = vbDouble Or _ VarType(expression) = vbCurrency Or _ VarType(expression) = vbDecimal Then ' 既に数値型である場合 (IsDate/IsBooleanがFalseであるため) IsStrictlyNumeric = True End If End If End If End Function Debug.Print vbCrLf & "--- 厳密な数値判定の例 ---" Debug.Print "IsStrictlyNumeric(123): " & IsStrictlyNumeric(123) ' True Debug.Print "IsStrictlyNumeric(""456""): " & IsStrictlyNumeric("456") ' True Debug.Print "IsStrictlyNumeric(CDate(""2023/01/01"")): " & IsStrictlyNumeric(CDate("2023/01/01")) ' False Debug.Print "IsStrictlyNumeric(""20

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