LoginSignup
16
23

More than 5 years have passed since last update.

VBA/VBSはじめの一歩

Last updated at Posted at 2015-08-11

以下は個人的に必要な事柄をまとめただけなので、網羅性とかまったくないです。

そもそも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で宣言する。スコープとしてPublicPrivateを宣言でき、省略した場合は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の公式ドキュメントには当たった方がいい。
16
23
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
16
23