空文字列の扱い方と処理速度について(""とvbNullString)
空文字列と書きましたが、空文字列という表現がかなり曖昧な表現になっています。
ここでいう空文字列とは、文字列が入るべき場所に、何も入っていない(ように見える)状態を指しています。
""が入っている状態
vbNullStringが入っている状態
String型変数において、この2つの状態が存在しています。
セルの値(Value)ではなく、あくまで、String型変数についての話になります。
これを調べるには、VarPtr関数とStrPtr関数を使います。
String型変数のメモリ配置と取得する関数
メモリアドレスの確認方法については、以下で詳しく解説しています。
String型変数のメモリアドレス
String型変数のアドレスには、実際に文字列が置かれているメモリのアドレスが入っています。
実際に文字列が置かれているメモリアドレスの直前には、文字列長部分があります。
実際に文字列が入っているメモリアドレスを取得する関数がStrPtr関数です。
・文字列が入っている(長さ0の文字列("")も含む)の場合は、文字列情報のメモリアドレス値を返します。
VarPtr関数およびStrPtr関数は公式のVBAリファレンスに掲載されていない関数になります。
今回のようにStringや配列のメモリアドレスを確認する以外では、WinAPIの呼び出しくらいでしか使用しません。
空文字列について
・""を代入した状態
それぞれの違いについて簡単に説明します。
vbNullStringは、VBAに定数として用意されています。
[VBA.Constants.]vbNullString
MicroSoftのサイトでは、「値が 0 の文字列」と書かれています。
Dim s As String
Debug.Print StrPtr(s)
この結果は、イミディエイト ウインドウに「0」と出力されます。
つまり、実際に文字列が入っているアドレスが0、つまりアドレスが無いという事になります。
禅問答のようですが、文字列には文字数の長さがあります。
"ABC"は3文字
"AB"は2文字
"A"は1文字
""は0文字
つまり、""は、「長さ0の文字列」という事になります。
Dim s As String
s = ""
Debug.Print StrPtr(s)
この結果は、イミディエイト ウインドウにアドレス数値が8桁や13桁(最大桁数はbit数による)で出力されます。
このアドレスが、実際に文字列が入っているメモリアドレスになります。
たとえ長さ0の文字列""でも、実際に文字列として格納されているアドレスが存在するという事です。
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変数に空文字列を入れる
・変数 = vbNullString : 「値0の文字列」を代入
変数の状態としては上で説明した通り違いがありますが、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
結果は「同じ」となります。
セルに空文字列を入れる
Columns("B").NumberFormat = "@"
Range("B2").Value = ""
Range("B3").Value = vbNullString
簡易的に=""で済ませてしまう場合も結構あります。
そのような場合に、対象セルが文字列に設定してある場合は注意が必要になります。
Stringが空文字列か判定
・If 変数 = vbNullString Then
・If Len(変数) = 0 Then
・If LenB(変数) = 0 Then
「長さ0の文字列」「値0の文字列」
どちらの状態でも、上記は全てTrueになります。
InputBox関数の戻り値が空文字列判定
それらでは、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の文字列("")
確かにこれで判定できるようです。
「ユーザーが [キャンセル] を選択すると、この関数は長さ 0 の文字列 ("") を返します。」
このように書かれています。
この記述はかなり曖昧な記述となっていますが、
InputBox関数の戻り値が入った変数を= ""で比較した時は一致するという点では間違いとは言い切れないかもしれません。
InputBox関数のキャンセル判定についてWEB検索すると、
多数出てくる中には、StrPtr関数を使うべきではないと書いているページも見受けられます。
「キャンセル」と未入力の「OK」を区別する必要があるなら、ユーザーフォームを使うべきだと書いているページもあります。
基本的にはこの意見に賛成です。
なのですが、
VBAには便利なものが用意されていることを忘れてはいけません。
Application.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 |
比較においては、「LenB関数」が速い
念の為、Excel2010(32bit)でも測定しましたが、数値に差異はあっても傾向は同じ結果になりました。
最初に説明したStringのアドレスを考えると、この結果は納得いくものになります。
Len関数やLenB関数は、実際の文字列を参照する必要が無く、文字列長の部分だけを見れば取得できるからです。
この時間差は、VBAの記述において気にするような違いではありません。
あくまで、これらが違う事をしているのだという説明の為の確認テストになります。
少なくとも、通常のVBAでは=""で何も問題は無いことは繰り返し言っておきます。
同じテーマ「マクロVBA技術解説」の記事
クリップボードに2次元配列を作成してシートに貼り付ける
ユーザー定義型の制限とクラスとの使い分け
シングルクォートの削除とコピー(PrefixCharacter)
空文字列の扱い方と処理速度について(""とvbNullString)
VBAにおける変数のメモリアドレスについて
Evaluateメソッド(文字列の数式を実行します)
Rangeオブジェクトの論理演算(差集合と排他的論理和)
VBAで写真の撮影日時や音楽動画の長さを取得する
VBAでWindowsMediaPlayerを使い動画再生する
VBAでWEBカメラ操作する
VBAで電光掲示板を作成
新着記事NEW ・・・新着記事一覧を見る
TRIMRANGE関数(セル範囲をトリム:端の空白セルを除外)|エクセル入門(2024-08-30)
正規表現関数(REGEXTEST,REGEXREPLACE,REGEXEXTRACT)|エクセル入門(2024-07-02)
エクセルが起動しない、Excelが立ち上がらない|エクセル雑感(2024-04-11)
ブール型(Boolean)のis変数・フラグについて|VBA技術解説(2024-04-05)
テキストの内容によって図形を削除する|VBA技術解説(2024-04-02)
ExcelマクロVBA入門目次|エクセルの神髄(2024-03-20)
VBA10大躓きポイント(初心者が躓きやすいポイント)|VBA技術解説(2024-03-05)
テンキーのスクリーンキーボード作成|ユーザーフォーム入門(2024-02-26)
無効な前方参照か、コンパイルされていない種類への参照です。|エクセル雑感(2024-02-17)
初級脱出10問パック|VBA練習問題(2024-01-24)
アクセスランキング ・・・ ランキング一覧を見る
1.最終行の取得(End,Rows.Count)|VBA入門
2.繰り返し処理(For Next)|VBA入門
3.セルのコピー&値の貼り付け(PasteSpecial)|VBA入門
4.変数宣言のDimとデータ型|VBA入門
5.RangeとCellsの使い方|VBA入門
6.ブックを閉じる・保存(Close,Save,SaveAs)|VBA入門
7.セルのクリア(Clear,ClearContents)|VBA入門
8.メッセージボックス(MsgBox関数)|VBA入門
9.条件分岐(Select Case)|VBA入門
10.ブック・シートの選択(Select,Activate)|VBA入門
- ホーム
- マクロVBA応用編
- マクロVBA技術解説
- 空文字列の扱い方と処理速度について(""とvbNullString)
このサイトがお役に立ちましたら「シェア」「Bookmark」をお願いいたします。
記述には細心の注意をしたつもりですが、
間違いやご指摘がありましたら、「お問い合わせ」からお知らせいただけると幸いです。
掲載のVBAコードは動作を保証するものではなく、あくまでVBA学習のサンプルとして掲載しています。
掲載のVBAコードは自己責任でご使用ください。万一データ破損等の損害が発生しても責任は負いません。