以下は個人的に必要な事柄をまとめただけなので、網羅性とかまったくないです。
そもそもVBAとは
Visual Basic for Application、要はVisual BasicのOffice実装版。「マクロ」と呼ばれる自動処理系はVBAで記述されている。似た言葉でVBSというのもあるが、こちらはVBScript、Visual Basic風のWindows用スクリプト言語で別のもの。VBAがVisual Basicそのものであるのに対し、VBScriptはあくまでVB 風 で仕様は異なる。本記事内では両者を区別せずに扱っているが、厳密には実装の異なる部分があるはず(雑ですみません)
その他、VBS/VBA周りの用語に関して。
WSH (Windows Script Host)
Windows上でのスクリプト実行環境。言語自体ではなく、実行環境を指す名称であり、WSH上で実行可能な言語はVBScriptとJScriptがある。現状はその役割はPowerShellに譲られている。
WMI (Windows Management Instrumentation)
WSH上で操作可能な、Windowsのシステム情報を制御するCOMオブジェクト。個人的にVBSを書くときはWMIを操作する目的であることが多い。
WQL (SQL for WMI)
WMIからの情報取得用に拡張されたSQLの実装。VBScriptから実行することができるので、WMIを操作する場合はよく用いる。
さっそく文法
変数/定数
-
Dim foo As String
変数宣言 -
Const Bar As Long
定数宣言
変数は宣言しなくても使えるが、やはり宣言した方がよい。先頭でOption Explicit
と記述しておくと変数の宣言が必須になるので、厳密な記法を望む場合は記述を推奨。
変数の型はここに一覧されている。特徴的なものとして「オブジェクト型」があり、これは文字通りオブジェクト指向のそれであり、プロパティやメソッドを持っている。。例えばExcelのVBAを書く場合はWorksheetやCellといったオブジェクトが用意されており、Cellにはrowやcolumnといったプロパティ、deleteやsplitといったメソッドが用意されているといった具合。
オブジェクト型変数は扱い方が他の型と異なる部分があるため注意が必要。代入は他の型では=
を用いるが、オブジェクト型変数に関してはSet
を頭に付与しなくてはならない。またオブジェクト型変数は参照代入になる点も注意が必要。したがって望むらくは、終了時点でNothing
を代入して解放するのが良い。
Dim dataVar As Integer
dataVar = 100
Dim objVar As Object
Set objVar = Range("A1")
Set objVar = Nothing
Set
がなければ当然エラーだし、宣言されたオブジェクト型変数が必要なところで別のデータ型の変数を持ってくると受け付けてくれないなど、それなりに型については厳格(だがエラーメッセージが要領得なくて気付きにくい)。上記のObject型はあらゆるオブジェクト型を含むことができたり、さらに動的データ型であるVariant型なんてのもあるが、良識あるエンジニアであれば使用は控え、strictな型宣言をすべきかと。
演算子
だいたいは想像した通りのものだが、いくつか注意点がある。
- 除算は
/
だが、余りを除いた商を求める場合は\
を使う。剰余を求めるにはmod
演算子というのはだいぶいただけない。。。 - 代入も比較演算にも
=
を使うというのもぐぬぬ感ある。 - 等価比較はオブジェクト型変数だと
Is
を使い、データ型変数だと=
を使う。
配列と動的配列
配列は変数名の末尾に(n)を付加することで、要素数n+1(添字が0からカウントされるため)の配列として宣言できる。
Dim foo(2) As Integer
foo(1) = 12
foo(2) = 23
Dim bar(3) As String
bar = Array("a", "b", "c")
要素数が動的に変化する場合は、nを抜いてfoo()
の形で定義することも可能ではあるが、本来的には動的配列であるCollectionオブジェクトを用いるのが望ましい。なお、Collectionオブジェクトは各要素のデータ型を問わない。
Dim col As Collection
col.Add(2)
col.Add("テスト")
col.Count ' => 2
col.Item(1) ' => テスト
関数
Sub
で宣言する。スコープとしてPublic
、Private
を宣言でき、省略した場合はPublic
として扱われる。要はここで宣言した関数名がマクロ名になる。
Public Sub Sample()
End Sub
制御式
if
If foo > 0 Then
MsgBox "fooは正です"
Else
MsgBox "fooは0以下です"
End If
Loop
For i = 0 To Len(str)
MsgBox Mid(str, 1, i)
Next i ' => str変数の文字を1文字ずつ表示
For Each i In array
MsgBox i
Next i ' => array配列の要素を1つずつ表示
i = 0
Do While True
If i = 10 Then
Exit Do ' => break
End If
i = i + 1
Loop
文字列操作
Dim foo As String
Dim bar As String
foo = "あいう"
bar = "ABC"
MsgBox foo & bar ' => あいうABC
baz = Replace(foo, "あい", "かき") ' => かきう
baz = Left(foo, 2) ' => かき
baz = Right(foo, 2) ' => きう
qux = InStr(foo, "き") ' => 2
qux = InStr(foo, "い") ' => 0
array = Split("aaa,bbb,ccc", ",")
For Each i In array
MsgBox i
Next i ' => aaa, bbb, cccの順にダイアログ表示
ちなみにこれは文字列操作に限った話ではないが、非常に長い文字列を代入する場合など、1文が長くなる際には_
(アンダースコア)を使って式を改行できる。
LongMsg = "非常に長いメッセージを代入する場合には、" & vbCrLf & _
"このように途中で改行することが可能です。"
日付と時刻
Dim d As Date
Dim t As Time
d = Date ' 実行時点の日付を取得
t = Time ' 実行時点の時刻を取得
MsgBox "今日は" & Format(d, "yyyy年mm月dd日") & "です" ' => 今日は2015年08月11日です
d = DateAdd("d", -2, d) ' 2日前の日付を取得
MsgBox "2日前は" & Format(d, "mm/dd") & "です" ' => 2日前は08/09です
t = DateAdd("n", 20, t) ' 20分後の時刻を取得(月との重複避けたとは言えnはねーわ)
MsgBox "20分後は" & Format(t, "h時n分") & "です" ' => 20分後は0時20分です
正規表現
RegExpオブジェクトを使用して検索や置換ができる。検索はTest
メソッドでは真偽値を返すのみにとどまるが、Execute
を使うとマッチした文字列をMatchesコレクションに返してくれる。括弧を用いれば後方参照が可能。
Dim re As Object
Dim target As String
Dim matches As Object
Set re = CreateObject("VBScript.RegExp")
With re
.Pattern = "([a-z]+)([0-9]+)"
.IgnoreCase = False
.Global = True
End With
target = "abc123"
If re.Test(target) Then
Set matches = re.Execute(target)
MsgBox matches.Item(0).SubMatches(1) ' => 123
End If
ファイルの取り扱い
Excel VBAでセルやワークシートを扱うことが多いだろうが、ここでは一旦一般的なファイルの読み書きの話にとどめる。
ファイルをオープンする場合はパスの他に「ファイル番号」が必要となる。基本的にはその時点で使われていないファイル番号をFreeFile
関数で取得すれば問題ない。
ファイルへの書き出しは下記Print
の他にWrite
が使えるが、Write
では出力文字列がダブルクォーテーションで囲まれてしまうという点、Print
の方がオプションが豊富である点等を考えると、Print
を使うのが無難。
fileNo = FreeFile
filePath = "c:\example.txt"
Open filePath For Output As #fileNo
Print #fileNo, "テスト"
Close #fileNo
連想配列
Dictionaryオブジェクトを使用する。連想配列として必要になる機会はあまりないのだが、重複要素の排除が配列では難しいので、こちらを使って実装したりしている。
Set dic = Create.Object("Scripting.Dictionary")
dic.Add key, value
If Not dic.Exists(value) Then
dic.Add key, value
End If
環境変数
関数を用いてEnviron("HOME")
の形で参照できる。環境変数の登録順序を表す整数も引数に取れるらしいが、そんなものを使うのはループで全環境変数を取り出すときぐらいな気がする。
注意点
- 実行時に出るエラーメッセージをそのまま読み解くのは諦めた方がいい。エラーが出たらF8でステップイン実行するとか、ブレークポイントを設けるとかして該当箇所を探し、その上でメッセージをググる。
- VBAはOfficeスイートの効率化という主目的の関係上、Web上のノウハウは少し緩めのコーディング規約に従っている場合も少なくない(プログラマーのように製品開発をするわけではないので、そのこと自体が悪いとは思わない)。読みにくいけどMSDNの公式ドキュメントには当たった方がいい。