ExcelマクロVBA技術解説
空文字列の扱い方と処理速度について(""とvbNullString)

ExcelマクロVBAの問題点と解決策、エクセルVBAの技術的解説
最終更新日:2019-11-09

空文字列の扱い方と処理速度について(""とvbNullString)


空文字列と書きましたが、空文字列という表現がかなり曖昧な表現になっています。
ここでいう空文字列とは、文字列が入るべき場所に、何も入っていない(ように見える)状態を指しています。


VBAにおいては、空文字列の状態が2つあります。
""が入っている状態
vbNullStringが入っている状態
String型変数において、この2つの状態が存在しています。
セルの値(Value)ではなく、あくまで、String型変数についての話になります。

VBAのStringを理解する上では、そのメモリアドレスについて少しでも理解しておいた方がよいでしょう。
これを調べるには、VarPtr関数とStrPtr関数を使います。

String型変数のメモリ配置と取得する関数

String型の変数のメモリについて簡単に説明します。
メモリアドレスのかくにんほうほうについては、以下で詳しく解説しています。
VBAにおける変数のメモリアドレスについて
VBA開発においてメモリアドレスを気にすることはほとんど無いと思います。気になる場合があるとしたら、・String変数の処理が遅い ・Variant変数の処理が遅い ・ByRef,ByValの違い ・WindowsAPI使用時 このような場合に多少は気になる事があるくらいではないでしょうか。

String型変数のメモリアドレス
String型変数のメモリアドレスに直接文字が入っているわけではありません。
String型変数のアドレスには、実際に文字列が置かれているメモリのアドレスが入っています。
実際に文字列が置かれているメモリアドレスの直前には、文字列長部分があります。

VBA マクロ String vbNullstring メモリ

変数のメモリアドレスを取得する関数がVarPtr関数
実際に文字列が入っているメモリアドレスを取得する関数がStrPtr関数です。

VarPtr関数
変数のメモリアドレスを返します。

StrPtr関数
実際の文字列が入っているメモリアドレスを返します。

・値0の文字列(vbNullString、初期状態)の場合は、0を返します。
・文字列が入っている(長さ0の文字列("")も含む)の場合は、文字列情報のメモリアドレス値を返します。

この実際の文字列が入っているメモリアドレスは、変数の文字列を変更する度に別のメモリアドレスが割り当てられます。

VarPtr関数およびStrPtr関数は公式のVBAリファレンスに掲載されていない関数になります。
今回のようにStringや配列のメモリアドレスを確認する以外では、WinAPIの呼び出しくらいでしか使用しません。

空文字列について

String型変数を宣言し、

何も代入していない状態
""を代入した状態

この2つの状態には違いがあります。
それぞれの違いについて簡単に説明します。

String変数に何も代入していない状態
String型変数に何も代入していない状態は、vbNullStringとなります。
vbNullStringは、VBAに定数として用意されています。
[VBA.Constants.]vbNullString
MicroSoftのサイトでは、「値が 0 の文字列」と書かれています。

Dim s As String
Debug.Print StrPtr(s)

この結果は、イミディエイト ウインドウに「0」と出力されます。
つまり、実際に文字列が入っているアドレスが0、つまりアドレスが無いという事になります。

String変数に""を代入した状態
何も無い文字という事です。
禅問答のようですが、文字列には文字数の長さがあります。
"ABC"は3文字
"AB"は2文字
"A"は1文字
""は0文字
つまり、""は、「長さ0の文字列」という事になります。



Dim s As String
s = ""
Debug.Print StrPtr(s)

この結果は、イミディエイト ウインドウにアドレス数値が8桁や13桁(最大桁数はbit数による)で出力されます。
このアドレスが、実際に文字列が入っているメモリアドレスになります。
たとえ長さ0の文字列""でも、実際に文字列として格納されているアドレスが存在するという事です。

ただしこれら2つの状態をVBAで気にする必要性はほとんどありません。
Ifで判定したり、他の変数に代入したりするうえで、その動作に違いはありません。

Dim s1 As String
If s1 = "" Then
  MsgBox "同じ"
Else
  MsgBox "違う"
End If
Dim s2 As String
s2 = ""
If s1 = s2 Then
  MsgBox "同じ"
Else
  MsgBox "違う"
End If

この結果は。どちらも「同じ」と表示されます。

String変数に空文字列を入れる

String変数を消去する方法として、

変数 = "" : 「長さ0の文字列」を代入
変数 = vbNullString : 「値0の文字列」を代入

後者はString型変数を宣言した状態に戻していることになります。
変数の状態としては上で説明した通り違いがありますが、VBAの動作としての違いはありません。

Dim s1 As String
Dim s2 As String
s1 = ""
s2 = vbNullString
If s1 = s2 Then
  MsgBox "同じ"
Else
  MsgBox "違う"
End If

結果は「同じ」となります。
つまり、""とvbNullStringは=比較において等しいという事です。

If vbNullString = "" Then
  MsgBox "同じ"
Else
  MsgBox "違う"
End If

結果は「同じ」となります。

Stringが空文字列か判定

String型変数が空なのかどうかの判定方法は何通りかあります。

・If 変数 = "" Then
・If 変数 = vbNullString Then
・If Len(変数) = 0 Then
・If LenB(変数) = 0 Then

変数の状態が、
「長さ0の文字列」「値0の文字列」
どちらの状態でも、上記は全てTrueになります。

InputBox関数の戻り値が空文字列判定

InputBox関数で、
ダイアログボックスにメッセージとテキストボックスを表示し、ユーザーが入力した文字列を取得することが出来ます。マクロVBAの最初または途中で、ユーザーの入力によって処理を変更したい場合が出てきます。これを実現するには、マクロVBAではInputBox関数を使います。
「キャンセル」した時と、何も入力せずに「OK」した場合の違いについてWEB検索すると多数ヒットするはずです。
それらでは、StrPtr関数の戻り値が0であることで判定しているはずです。

Sub InputBox関数()
  Dim s
  s = InputBox("空文字確認")
  If StrPtr(s) = 0 Then
    MsgBox "キャンセル"
  ElseIf s = "" Then
    MsgBox "空文字"
  Else
    MsgBox s
  End If
End Sub

InputBox関数の戻り値は、
・「キャンセル」した時は、値0の文字列(vbNullString)
何も入力せずに「OK」した場合は、長さ0の文字列("")
確かにこれで判定できるようです。

ただし、この仕様はInputBox関数の公式リファレンスには書かれていません。
VBAリファレンス「InputBox関数」
「ユーザーが [キャンセル] を選択すると、この関数は長さ 0 の文字列 ("") を返します。」
このように書かれています。
この記述はかなり曖昧な記述となっていますが、
InputBox関数の戻り値が入った変数を= ""で比較した時は一致するという点では間違いとは言い切れないかもしれません。

InputBox関数のキャンセル判定についてWEB検索すると、
多数出てくる中には、StrPtr関数を使うべきではないと書いているページも見受けられます。
「キャンセル」と未入力の「OK」を区別する必要があるなら、ユーザーフォームを使うべきだと書いているページもあります。
基本的にはこの意見に賛成です。
なのですが、
VBAには便利なものが用意されていることを忘れてはいけません。

Application.InputBoxメソッド

VBAには、InputBox関数の高機能版として、Application.InputBoxメソッドがあります。
InputBoxメソッドはApplicationのメソッドで、ユーザー入力用のダイアログボックスを表示し、表示したダイアログボックスに入力された情報を受け取ることが出来ます。マクロVBAの途中でユーザーに何らかの値を入力してもらう事で、その後の処理にその値を使う場合に使用します。

Sub InputBoxメソッド()
  Dim s
  s = Application.InputBox("空文字確認")
  If s = False Then
    MsgBox "キャンセル"
  ElseIf s = "" Then
    MsgBox "空文字"
  Else
    MsgBox s
  End If
End Sub

InputBoxメソッドは、入力のデータ型をオプションで指定できます。
そして、InputBoxメソッドは「キャンセル」の時は「False」を返します。
あえてStrPtr関数を持ち出すまでもなく、このメソッドを使う事を検討すべきです。
StrPtrを使うなという事ではありませんが、あえて使う必要が本当にあるのだろうかという事です。

空文字列の処理方法による速度比較

以上、空文字列について説明してきましたが、最後に処理速度の違いを確認しておきましょう。



Sub 空文字速度比較()
  Dim t(1 To 10) As Double
  Dim i As Long
  For i = 1 To 10
    t(i) = ○○○(100000000)
  Next
  Debug.Print "○○○="; WorksheetFunction.Min(t)
End Sub

上記VBAの○○○の部分を以下のそれぞれのプロシージャーに変更してテストしています。

Function 長さ0を代入(cnt As Long) As Double
  Dim s As String
  Dim i As Long
  Dim sTime As Double
  sTime = Timer
  For i = 1 To cnt
    s = ""
  Next
  長さ0を代入 = Timer - sTime
End Function

Function 値0を代入(cnt As Long) As Double
  Dim s As String
  Dim i As Long
  Dim sTime As Double
  sTime = Timer
  For i = 1 To cnt
    s = vbNullString
  Next
  値0を代入 = Timer - sTime
End Function

Function 長さ0で判定(cnt As Long) As Double
  Dim s As String
  Dim i As Long
  Dim sTime As Double
  sTime = Timer
  For i = 1 To cnt
    If s = "" Then
    End If
  Next
  長さ0で判定 = Timer - sTime
End Function

Function 値0で判定(cnt As Long) As Double
  Dim s As String
  Dim i As Long
  Dim sTime As Double
  sTime = Timer
  For i = 1 To cnt
    If s = vbNullString Then
    End If
  Next
  値0で判定 = Timer - sTime
End Function

Function Len関数で判定(cnt As Long) As Double
  Dim s As String
  Dim i As Long
  Dim sTime As Double
  sTime = Timer
  For i = 1 To cnt
    If Len(s) = 0 Then
    End If
  Next
  Len関数で判定 = Timer - sTime
End Function

Function LenB関数で判定(cnt As Long) As Double
  Dim s As String
  Dim i As Long
  Dim sTime As Double
  sTime = Timer
  For i = 1 To cnt
    If LenB(s) = 0 Then
    End If
  Next
  LenB関数で判定 = Timer - sTime
End Function

1億回の繰り返しを10回行い、その最速値を出力しています。
差異が僅かなので、これを、さらに複数回実行した最速値が以下になります。

プロシージャー 秒数
代入 長さ0を代入 3.21875
値0を代入 0.85938
判定 長さ0で判定 1.30469
値0で判定 1.34375
Len関数で判定 0.82031
LenB関数で判定 0.72656
※Windows10+Excel2019(64bit)

この結果を見る限り、

代入においては、「値0を代入」が速い
比較においては、「LenB関数」が速い

測定自体に誤差はつきものですが、何度やってもこの傾向は変わりません。
念の為、Excel2010(32bit)でも測定しましたが、数値に差異はあっても傾向は同じ結果になりました。
最初に説明したStringのアドレスを考えると、この結果は納得いくものになります。
Len関数やLenB関数は、実際の文字列を参照する必要が無く、文字列長の部分だけを見れば取得できるからです。

ただし、これは1億回の結果であることも忘れないでください。
この時間差は、VBAの記述において気にするような違いではありません。
あくまで、これらが違う事をしているのだという説明の為の確認テストになります。
少なくとも、通常のVBAでは=""で何も問題は無いことは繰り返し言っておきます。



同じテーマ「マクロVBA技術解説」の記事

Applicationを省略できるApplicationのメソッド・プロパティ一覧
PowerQueryの強力な機能をVBAから利用する方法
ShapesとDrawingObjectsの相違点と使い方
新規挿入可能なシート名の判定
VBAにおける配列やコレクションの起点について
VBAのマルチステートメント(複数のステートメントを同じ行に)
クリップボードに2次元配列を作成してシートに貼り付ける
ユーザー定義型の制限とクラスとの使い分け
シングルクォートの削除とコピー(PrefixCharacter)
空文字列の扱い方と処理速度について(""とvbNullString)
VBAにおける変数のメモリアドレスについて


新着記事NEW ・・・新着記事一覧を見る

VBAにおける変数のメモリアドレスについて|VBA技術解説(11月8日)
空文字列の扱い方と処理速度について(""とvbNullString)|VBA技術解説(1月7日)
Errオブジェクトとユーザー定義エラー|VBA入門(11月5日)
シングルクォートの削除とコピー(PrefixCharacter)|VBA技術解説(11月4日)
ユーザー定義型の制限とクラスとの使い分け|VBA技術解説(11月3日)
クリップボードに2次元配列を作成してシートに貼り付ける|VBA技術解説(11月1日)
VBAクラスを使ったイベント作成(Event,RaiseEvent,WithEvents)|VBA技術解説(10月31日)
VBAクラスのAttributeについて(既定メンバーとFor Each)|VBA技術解説(10月19日)
VBAの用語について:ステートメントとは|VBA技術解説(10月16日)
VBAのマルチステートメント(複数のステートメントを同じ行に)|VBA技術解説(10月14日)


アクセスランキング ・・・ ランキング一覧を見る

1.最終行の取得(End,Rows.Count)|VBA入門
2.セルのコピー&値の貼り付け(PasteSpecial)|VBA入門
3.RangeとCellsの使い方|ExcelマクロVBA入門
4.Range以外の指定方法(Cells,Rows,Columns)|VBA入門
5.変数宣言のDimとデータ型|ExcelマクロVBA入門
6.繰り返し処理(For Next)|ExcelマクロVBA入門
7.マクロって何?VBAって何?|ExcelマクロVBA入門
8.ひらがな⇔カタカナの変換|エクセル基本操作
9.空白セルを正しく判定する方法(IsEmpty,IsError,HasFormula)|VBA技術解説
10.セルに文字を入れるとは(Range,Value)|VBA入門



  • >
  • >
  • >
  • 空文字列の扱い方と処理速度について(""とvbNullString)

  • このサイトがお役に立ちましたら「シェア」「Bookmark」をお願いいたします。


    記述には細心の注意をしたつもりですが、
    間違いやご指摘がありましたら、「お問い合わせ」からお知らせいただけると幸いです。
    なお、掲載のVBAコードは自己責任で使ってください。万一データ破損等の損害が発生しても責任は負いません。




    このサイトがお役に立ちましたら「シェア」「Bookmark」をお願いいたします。
    本文下部へ