Excel VBA プログラミング 「自動型変換」に関する落とし穴
2005.9.8 作成
2018.6.25 微修正
Excel で VBA プログラミングするときに陥る「自動型変換」
に関係する落とし穴について書きます。これは恐ろしい罠と
言ってもよいでしょう。
まず、次の事実を知る必要があります。
1. セルに入るデータは「数値」「文字列」「日付型」などの
型を持っている
2. 数値 10 と文字列 "10" は区別される(等しくない)
3. 1. での型と「セルを右クリック」→「セルの書式設定」→「表示形式」
で設定する事項(標準 or 数値 or 日付 or 文字列 or ....)
は「別物」である。
1. の型は次のようにして表示させることが出来ます。
ここではイミディエイトウィンドウに表示します。
Debug.Print TypeName(Range("A1").Value)
Double, String, Date などの型が得られます。ここから先は
Double 型と String 型の 2 つだけを対象として議論を
進めます。
3. はセルに物を代入するときに影響を及ぼします。場合に
よっては自動的な型変換が発生します。
Range("A1").NumberFormatLocal = "@" ' 表示形式は文字列
Range("A1").Value = 2 ' セルには数値 2 が入る
Range("A1").Value = "2" ' セルには文字列 "2" が入る
Range("A1").NumberFormatLocal = "0_ " ' 表示形式は数値
Range("A1").Value = 2 ' セルには数値 2 が入る
Range("A1").Value = "2" ' 文字列 "2" が変換されて数値 2 が入る
Range("A1").NumberFormatLocal = "G/標準" ' 表示形式は標準
Range("A1").Value = 2 ' セルには数値 2 が入る
Range("A1").Value = "2" ' 文字列 "2" が変換されて数値 2 が入る
セルの表示形式が「文字列」の場合、型変換は起こりません。
「標準」「数値」の場合、その文字列が数字だけからなる場合、
自動的に数値に変換されます。
セルの表示形式を変更しても、中に入っている値の型は不変です。
Range("A1").NumberFormatLocal = "@" ' 表示形式は文字列
Range("A1").Value = "2" ' セルには文字列 "2" が入る
Range("A1").NumberFormatLocal = "G/標準" ' 表示形式を標準に変更
Debug.Print TypeName(Range("A1").Value) ' セルの中は文字列のまま
Range("A1").NumberFormatLocal = "0_ " ' 表示形式を数値に変更
Debug.Print TypeName(Range("A1").Value) ' セルの中は文字列のまま
(注意!)ここでは数値型と文字列型だけを議論の対象と
しています。日付型の表示形式を文字列型に変更すると、
TypeName は Date → Double と変わります。難解です。
Integer 型や String 型変数への代入文では CInt(), CStr() が
適用されます。
Dim I as Integer, S as String
Range("A1").NumberFormatLocal = "@" ' 表示形式は文字列
Range("A1").Value = "2" ' 文字列 "2" が入る
I = Range("A1").Value ' 数値 2 に変換される
Range("A1").Value = 2 ' 数値 2 が入る
S = Range("A1").Value ' 文字列 "2" に変換される
If 文が曲者です。If の片方が定数の場合は、
セルの内容がその定数の型に変換されてから比較が行われます。
If Range("A1").Value = 2 Then ' セルの内容が数値に変換されてから比較
If Range("A1").Value = "2" Then ' セルの内容が文字型に変換されてから比較
しかし、
If Range("A1").Value = Range("A2").Value Then
の場合は型変換は起こりません。ゆえに
Range("A1").NumberFormatLocal = "0_ " ' 表示形式は数値
Range("A1").Value = "2" ' 自動型変換されて数値 2 が入る
Range("A2").NumberFormatLocal = "@" ' 表示形式は文字列
Range("A2").Value = "2" ' 文字列 "2" が入る
If Range("A1").Value = Range("A2").Value Then
この If 文では数値 2 と文字列 "2" を比較するので「偽」
となります。今までの議論は「セル」を「バリアント型変数」に
言い換えても成立します。
このように、数値 2 と文字列 "2" は区別されます。セルに入っている
物の型は TypeName で調べないと分かりません。
If 文の片方が型が決まっている変数の場合を見てみます。
Dim a As Double, s As String
a = 2
s = "2"
If a = s Then
Debug.Print "Yes"
Else
Debug.Print "No"
End If
上のプログラムの場合、"Yes" となります。
Double 型と String 型を比較する場合、
String 型の内容が数値に変換可能な場合は
CDbl で double 型に変換するようです。
s = "2" のかわりに s = "a" とすると If 文の実行時に
「型が一致しません」のエラーが出てプログラムはストップします。
結論として、If 文でセルの内容を比較するとき、セルの中の物の
型が不明であるときは、両方とも数値に変換するときは
Val(str) 引数を数値に変換する
文字列に変換するときは
CStr(num)
あるいは
Trim(Str(num)) ' Str は引数を文字列に変換するときに、
' 符号を表す空白が入るので、Trim で切り落とす
のような処理を入れた方が良いかも知れません。
----------------
VLookup 関数は数値と文字列を区別します。ですから、
VLookup(2,A1:C10,2)
VLookup("2",A1:C10,2)
は別の結果となります。
----------------------
このように、Excel では「セルに代入するとき」に
自動的な型変換が行われるのが落とし穴です。
「表示形式」は、自動的な型変換の作用に影響を及ぼします。
そして、「セルの中に入っている物の型」と「表示形式」
は別物です。
比較においては 10 <> "10" です。また、"10" <> "10 " です。
すなわち、文字列の比較においては空白の有無も考慮されます。
文字列の比較を行うときに空白を無視したいときは、
Trim 関数を適用してから行う必要があります。
If 文では「片方の型が決まっているとき」に自動的な型変換
が行われます。「String = Double」などのときも自動的な
型変換が行われます。
VLookup では自動的な型変換は行われません。
自動的な型変換は思わぬバグを引き起こす可能性があり、
とても怖い処理系に思えます。罠が潜んでいます。
自動的な型変換を避けるには、セルの表示形式は原則として
「文字列」に設定しておくのが良いと思われます。
-------------------
余談ですが、Fortran では長さの異なる文字列を比較する場合、
短い方の文字列の末尾に空白を加えて同じ長さにしてから
比較するという仕様でした。