【ExcelVBA・マクロ】Val関数の使い方|文字列から数値を取り出す方法と注意点(カンマ・通貨・16進数対応)

Val関数の使い方 ExcelVBA

VBAで「文字列っぽい数値」を数値として扱いたいときに登場するのが Val関数 です。たとえば「123abc」から 123 を取り出したり、住所文字列の先頭にある番号だけを抽出したりするときに便利です。

ただしValは「何でも数値に変換できる万能関数」ではありません。カンマ(,)や通貨記号($など)を数値の一部として認識しない小数点はピリオド(.)のみなど、仕様を知らないと誤変換・取りこぼしの原因になります。

スポンサーリンク
スポンサーリンク

Val関数とは?(できること・できないこと)

  • できること:文字列の先頭から「数値として認識できる部分」だけを読み取り、数値として返す
  • できないこと(注意点):カンマや通貨記号などは認識されず、そこで読み取りが止まることがある

構文

Val(string)

string は任意の文字列式です。Valは「数値の一部として認識できない最初の文字」で読み取りを中止します。多くの人が数値の一部だと思いがちな「ドル記号、コンマ」などは、Valでは認識されません。

Val関数の基本例(まずは挙動を掴む)

Sub ValBasicExamples()
    Dim v As Variant
    v = Val("2457")          ' 2457
    Debug.Print v
    v = Val("24 and 57")     ' 24(途中で止まる)
    Debug.Print v
    v = Val(" 2 45 7")       ' 2457(空白は削除される)
    Debug.Print v
End Sub

上記の例はMicrosoftの説明例と同じ挙動です。

住所や型番など「先頭の数値だけ欲しい」ケースに強い

Valは、先頭から数値として読める部分だけ拾うので、「先頭が数値で始まる文字列」の数値抽出に向きます。

Sub ValForPrefixNumber()
    Debug.Print Val("123abc")        ' 123
    Debug.Print Val("00123-45")      ' 123(先頭ゼロも数値として解釈)
    Debug.Print Val("    1615 198th Street N.E.") ' 1615198(空白は削除される)
End Sub

Valの落とし穴:カンマ・通貨記号・小数点・%で止まる/エラーになる

カンマ(,)は数値として認識されない

Valはコンマを数値の一部として認識しません。そのため「1,234」を渡すと、途中で止まりやすく期待とズレます(環境・文字列形によっては「1」までしか読まない原因になります)。Valは「ドル記号、コンマなどは認識されない」と明記されています。

Sub ValCommaTrap()
    Debug.Print Val("1,234")    ' 期待:1234 / 実際:途中で止まって 1 になり得る
End Sub

実務で「カンマ付きの数値文字列」を扱うなら、先に Replace でカンマを消してから変換するのが安全です。

Function ToDoubleRemoveComma(ByVal s As String) As Double
    ' "1,234.56" → 1234.56
    ToDoubleRemoveComma = CDbl(Replace(s, ",", ""))
End Function

通貨記号($や¥など)が先頭にあると0になることがある

Valは通貨記号を数値として認識しません(数値の一部として見なされることが多い記号でも認識されない、と説明されています)。先頭が通貨記号だと最初の文字で止まり、結果が0になってしまうケースがあります。

Sub ValCurrencyTrap()
    Debug.Print Val("$123")   ' 0 になり得る(先頭が数値ではないため)
End Sub

通貨文字列を扱う場合は、通貨記号やスペースを除去してから CDblCCur を使うのが現実的です。

小数点は「.(ピリオド)のみ」:国際化はCDbl推奨

Valはピリオド(.)だけを有効な小数点として認識します。小数点に別記号を使う環境(国際化など)では、Microsoft公式でも CDblを使うことが推奨されています。

%が絡むと「型サフィックス扱い」でエラーになる場合がある

Valは変換前に「データ型サフィックス」を認識して、型の不一致エラーになる可能性がある、と公式に注意書きがあります。例として、Val("50.5%") がエラーになるケースが挙げられています。

Sub ValPercentTrap()
    Debug.Print Val("50%")      ' 50(期待通りになりやすい)
    ' Debug.Print Val("50.5%")  ' エラーになる可能性(公式注意)
End Sub

割合文字列は、単純にValで処理するより「%を除去してCDbl→/100」の方が安全です。

Function PercentTextToDouble(ByVal s As String) As Double
    ' "12.3%" → 0.123
    s = Replace(s, "%", "")
    PercentTextToDouble = CDbl(s) / 100
End Function

Valの強み:&H(16進数)と&O(8進数)を認識する

Valは基数プレフィックス &O(8進数)と &H(16進数)を認識します。

Sub ValHexOct()
    Debug.Print Val("&HFFFF")   ' -1(公式例)
    Debug.Print Val("&H10")     ' 16
    Debug.Print Val("&O10")     ' 8
End Sub

Val("&HFFFF") が -1 を返す例は公式ページに掲載されています。[Source]

Valと型変換関数(CDbl/CLngなど)の使い分け

Valは「先頭から読める数値だけ拾う」ため、入力が汚いデータ(混在文字列)には便利な一方で、黙って0になったり途中で止まったりして気づきにくいのが弱点です。用途で使い分けましょう。

目的推奨理由
「文字列の先頭にある数値」を抽出したいVal先頭から数値として読める部分だけ返す
「文字列全体」を厳密に数値へ変換したいCDbl / CLng / CDec変換できない入力をエラーで検知しやすい
小数点記号が環境で異なる可能性があるCDblValは.(ピリオド)のみ小数点として認識するため
カンマ付き数値(1,234.56)を扱うReplaceで前処理+CDblValはコンマを認識しない
通貨・%など記号付き文字列前処理+CDbl/CCurValは記号で止まる/エラーになることがある

実務テンプレ:安全に数値化する(Valを使う/使わない判断込み)

「入力が汚い(単位や文字が混ざる)」ならValで先頭数値を拾い、「入力が正しい前提」ならCDbl/CLngで厳密変換して落とす、が実務の基本です。

先頭数値だけ拾う(Valを使う)

Function GetLeadingNumber(ByVal s As String) As Double
    ' 例: "12.3kg" → 12.3 / "ABC123" → 0
    GetLeadingNumber = Val(s)
End Function

厳密に数値へ(CDblを使う:失敗はFalseで返す)

Function TryToDouble(ByVal v As Variant, ByRef result As Double) As Boolean
    On Error GoTo Fail
    If IsNull(v) Or IsError(v) Then GoTo Fail
    If Trim$(CStr(v)) = "" Then GoTo Fail
    If Not IsNumeric(v) Then GoTo Fail
    result = CDbl(v)
    TryToDouble = True
    Exit Function
Fail:
    TryToDouble = False
End Function

カンマ・通貨・%を前処理してから数値へ

Function NormalizeNumberText(ByVal s As String) As String
    ' よくある前処理例(必要に応じて増やす)
    s = Replace(s, ",", "")
    s = Replace(s, "¥", "")
    s = Replace(s, "$", "")
    s = Replace(s, " ", "")
    s = Replace(s, vbTab, "")
    NormalizeNumberText = s
End Function
Function MoneyTextToCurrency(ByVal s As String) As Currency
    s = NormalizeNumberText(s)
    MoneyTextToCurrency = CCur(s)
End Function

よくある質問(FAQ)

Val関数は「文字列の途中の数値」も取れますか?

基本的には先頭から数値として読める範囲だけを返します。たとえば Val("ABC123") は 0 になりやすく、途中の123は拾いません。途中の数値を取りたい場合は、正規表現(RegExp)やReplace/Midなどの文字列処理で抽出してからCDbl/CLngする方が確実です。

ValとCDblの違いは何ですか?

Valは「先頭から読める数値だけ返す」ため入力が汚いデータ向きですが、誤変換に気づきにくいのが弱点です。一方CDblは「数値として解釈できない入力」をエラーで検知しやすく、厳密変換向きです。なお、Valは小数点としてピリオド(.)のみ認識するため、国際化などではCDblが推奨される旨が公式に記載されています。

Valで16進数(&H)を扱えるのは便利ですか?

扱えます。ログや外部システムから16進数で渡される値を10進数に直したいときに便利です(&Oの8進数も認識)。

まとめ(VBA Val関数の結論)

  • Valは「文字列の先頭から読める数値部分」を数値として返す
  • 数値の一部として認識できない最初の文字で読み取りを中止する(ドル記号・コンマなどは認識しない)
  • 小数点は.(ピリオド)のみ。別の小数点記号の可能性があるならCDblが推奨
  • &H(16進数)/&O(8進数)を認識する
  • 実務では「Valで雑に拾う」か「CDbl/CLngで厳密に落とす」かを用途で使い分ける

参考リンク

スポンサーリンク
スポンサーリンク
ExcelVBA
いがぴをフォローする

コメント

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