AccessVBAのIsNumericは「09-」も数値と判定する:入力チェックの落とし穴

IsNumeric("09-")がTrueになる落とし穴のイメージ AccessVBA

IsNumericで数値チェックしたのに、エラーになる。

Accessで作った入力画面を使っていただいていたところ、面積を入力して次の項目へ移動したときにエラーが出る、という連絡をいただきました。

実行時エラー ‘3075’: クエリ式 ’09-‘ の 構文エラー : 演算子がありません。

保存処理では、入力値が数値かどうかを IsNumeric で判定してからSQLに埋め込んでいました。ところが「09-」という入力が数値と判定されて通り抜け、そのままSQLに埋め込まれてエラーになっていました。

シチュエーション

数量・面積・金額などの入力欄
非連結のテキストボックスに入力させ、VBAで保存している

入力チェックにIsNumericを使っている
数値でなければメッセージを出して止めている

入力値をSQLに組み立てて実行している
"UPDATE ... SET 数量=" & Me.Txt_Suryo のように連結している

IsNumericが「数値」と判定する文字列

IsNumericは「数値に変換できるかどうか」を判定する関数です。「数字だけでできているか」を判定するものではありません。実際にいろいろな文字列で試してみました。あわせて、Val と CDbl で変換した結果も載せています。

入力IsNumericValCDbl
123True123123
-123True-123-123
1.5True1.51.5
09-True9-9
1-True1-1
+5True55
5+True55
1d2True100100
1e3True10001000
&H10True1616
&O17True1515
123(全角)True0123
12 (前後に空白)True1212
12,345True1212345
1,2,3True1123
\100True0100
(5)True0-5
1.2.3False1.2エラー
1-2False1エラー
-False0エラー
.False0エラー
(空文字)False0エラー

※ Windows 11・Access 2021(日本語)で確認しました。カンマや「\」の扱いは、Windowsの地域の設定によって変わる可能性があります。

気をつけたいのは次の点です。

– 末尾のマイナス・プラス(09-、5+)も数値と判定されます。CDbl("09-") は -9 になります

– d や e を使った指数表記(1d2 = 1×10²)、&H(16進数)・&O(8進数)も数値です

– カンマ・円記号・かっこも通ります。CDbl("(5)") は会計表記の負の数として -5 になります

– Val は数字以外の文字が出たところで読むのをやめるので、12,345 は 12、全角の 123 は 0 になります。エラーにならない代わりに、黙って違う値になります

そのままSQLに埋め込むとエラー3075

IsNumericを通った値を、そのままSQLに連結すると次のようになります。

CurrentDb.Execute "UPDATE T_受注明細 SET 数量=" & "09-" & " WHERE ID=1", dbFailOnError
埋め込んだ値結果
09-エラー3075「クエリ式 ’09-‘ の 構文エラー : 演算子がありません。」
1d2エラー3075(同上)
&H10エラー3075(同上)

VBAでは数値として読めても、SQLでは読めない書き方があるためです。

数字・マイナス・小数点だけを許可するチェック関数

標準モジュールに以下のコードを貼り付けます。

'=========================================================
'  プロシージャ名:gf_NUM_CHECK
'  機能  :入力値が数値かどうかを判定します。
'            許可するのは「先頭のマイナス」「数字」「小数点1つ」だけです。
'            全角の数字は半角に直してから判定します。
'
'  【引数】
'    strValue :判定する文字列
'
'  【戻り値】
'    True :数値 / False :数値ではない
'
'  【使用例】
'    gf_NUM_CHECK("12.5")  → True
'    gf_NUM_CHECK("09-")   → False
'=========================================================
Public Function gf_NUM_CHECK(ByVal strValue As String) As Boolean
    Dim l_str As String           ' 判定する文字列
    Dim l_pos As Long             ' 文字の位置
    Dim l_ch As String            ' 1文字
    Dim l_dot As Boolean          ' 小数点が出てきたか
    gf_NUM_CHECK = False
    '=== 全角→半角、前後の空白を除去 ===
    l_str = Trim(StrConv(strValue, vbNarrow))
    If l_str = "" Then Exit Function
    '=== 先頭のマイナスだけ許可 ===
    If Left(l_str, 1) = "-" Then l_str = Mid(l_str, 2)
    If l_str = "" Then Exit Function
    '=== 1文字ずつ確認 ===
    For l_pos = 1 To Len(l_str)
        l_ch = Mid(l_str, l_pos, 1)
        If l_ch = "." Then
            If l_dot Then Exit Function       ' 小数点が2つ目
            l_dot = True
        ElseIf l_ch < "0" Or l_ch > "9" Then
            Exit Function                     ' 数字以外
        End If
    Next l_pos
    '=== 「.5」「5.」のような形は不可 ===
    If Left(l_str, 1) = "." Or Right(l_str, 1) = "." Then Exit Function
    gf_NUM_CHECK = True
End Function

上の表の文字列でこの関数を試すと、Trueになるのは 123、-123、1.5、123(全角)、 12 (前後に空白)の5つだけでした。09- や 1d2、12,345 などはすべてFalseになります。

カンマ区切りを許可したい場合
金額欄などで「12,345」を受け付けたい場合は、判定の前に l_str = Replace(l_str, ",", "") を入れます。ただしその場合は、保存するときにもカンマを取り除いてからSQLに渡してください。

実行方法

入力欄の「更新前処理」で呼び出し、数値でなければ Cancel = True で更新を取り消します。

Private Sub Txt_Menseki_BeforeUpdate(Cancel As Integer)
    '=== 空欄は許可(必須にしたい場合はここで止める)===
    If Len(Me.Txt_Menseki.Text) = 0 Then Exit Sub
    If Not gf_NUM_CHECK(Me.Txt_Menseki.Text) Then
        MsgBox "数値で入力してください。", vbExclamation
        Cancel = True
    End If
End Sub

Cancel = True にすると、カーソルはその入力欄に残ったままになります。Escキーを押すと、入力前の値に戻ります。

入力中の文字列は .Text で取得しています。フォーカスがある入力欄の、画面に表示されている文字列そのものを判定するためです。

保存するときは、全角で入力された場合に備えて StrConv(Me.Txt_Menseki.Text, vbNarrow) で半角に直してからSQLに渡します。

最後に

以上がAccessVBAのIsNumericの落とし穴と、入力チェック関数の作り方です。

IsNumericは「VBAで数値に変換できるか」を見る関数なので、09- や 1d2 のような、人が見ると数値に見えない文字列も通してしまいます。入力値をSQLに埋め込んだり、計算に使ったりする場合は、許可する文字を決めて1文字ずつ確認するチェックにしておくと安全です。

コメント

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