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

ExcelマクロVBAの問題点と解決策、VBAの技術的解説
最終更新日:2022-08-08

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


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


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

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

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

String型の変数のメモリについて簡単に説明します。
メモリアドレスの確認方法については、以下で詳しく解説しています。
VBAにおける変数のメモリアドレスについて
・メモリアドレスを取得する関数とメモリコピーのWindowsAPI ・メモリアドレスを確認するために使用したVBA ・文字列型Stringのメモリアドレス ・Integer, Long, Single, Double, Dateのメモリアドレス ・Variantのメモリアドレス ・配列のメモリアドレス ・オブジェクトのメモリアドレス ・ByRef,ByValのメモリアドレス

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

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

セルに空文字列を入れる

この記事を読んだ方からメールを頂いたので、以下は後日追記しました。
VBA マクロ String vbNullstring

セルの表示形式が「文字列」の場合だけ両者に違いが出ます。

Columns("B").NumberFormat = "@"
Range("B2").Value = ""
Range("B3").Value = vbNullString

VBA マクロ String vbNullstring

実務的にはClearやClearContentsメソッドを使ってクリアする場合が多いですが、
簡易的に=""で済ませてしまう場合も結構あります。
そのような場合に、対象セルが文字列に設定してある場合は注意が必要になります。

参考
空白セルを正しく判定する方法
・セルの値が空白の判定 ・計算式が入っていない判定 ・エラー値の判定 ・IsEmpty関数:空白を判定するVBA関数 ・TypeName関数:データ型を判定するVBA関数 ・RangeオブジェクトのFormulaプロパティ ・空白セルを正しく判定する方法続編
空白セルを正しく判定する方法2
空白セルの判定について、いろいろな方から意見を頂きました。やはり、空白判定は奥が深く結構難しいものとなっています。ここでは、各プロパティや関数が、セルの状態によって返す値を再確認してみます。元記事は、空白セルを正しく判定する方法(IsEmpty,IsError,HasFormula) 上記の記事では、


Stringが空文字列か判定

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

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

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

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

InputBox関数で、
・InputBox関数の構文 ・名前付き引数 ・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メソッド ・InputBoxメソッドの使用例 ・最後に

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技術解説」の記事

クリップボードに2次元配列を作成してシートに貼り付ける
ユーザー定義型の制限とクラスとの使い分け
シングルクォートの削除とコピー(PrefixCharacter)
空文字列の扱い方と処理速度について(""とvbNullString)
VBAにおける変数のメモリアドレスについて
Evaluateメソッド(文字列の数式を実行します)
Rangeオブジェクトの論理演算(差集合と排他的論理和)
VBAで写真の撮影日時や音楽動画の長さを取得する
VBAでWindowsMediaPlayerを使い動画再生する
VBAでWEBカメラ操作する
VBAで電光掲示板を作成


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

数字(1~50)を丸付き数字に変換するVBA|VBA技術解説(2022-11-15)
TEXTAFTER関数(テキストの指定文字列より後ろの部分を返す)|エクセル入門(2022-11-14)
TEXTBEFORE関数(テキストの指定文字列より前の部分を返す)|エクセル入門(2022-11-14)
TEXTSPLIT関数(列と行の区切り記号で文字列を分割)|エクセル入門(2022-11-12)
LAMBDA以降の新関数はVBAで使えるか|VBA技術解説(2022-11-11)
WRAPCOLS関数(1次元配列を指定数の列で折り返す)|エクセル入門(2022-11-08)
WRAPROWS関数(1次元配列を指定数の行で折り返す)|エクセル入門(2022-11-08)
EXPAND関数(配列を指定された行と列に拡張する)|エクセル入門(2022-11-07)
TAKE関数(配列の先頭/末尾から指定行/列数を取得)|エクセル入門(2022-11-06)
DROP関数(配列の先頭/末尾から指定行/列数を除外)|エクセル入門(2022-11-06)


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

1.最終行の取得(End,Rows.Count)|VBA入門
2.RangeとCellsの使い方|VBA入門
3.変数宣言のDimとデータ型|VBA入門
4.繰り返し処理(For Next)|VBA入門
5.セルのコピー&値の貼り付け(PasteSpecial)|VBA入門
6.Excelショートカットキー一覧|Excelリファレンス
7.並べ替え(Sort)|VBA入門
8.エクセルVBAでのシート指定方法|VBA技術解説
9.マクロって何?VBAって何?|VBA入門
10.ExcelマクロVBAの基礎を学習する方法|エクセルの神髄




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


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



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