Excel VBAの基本
2021/02/21更新
目次
はじめに
Excel VBAのごく基本的な部分をチートシートとしてまとめた。私自身と同じく「他のプログラミング言語でプログラミングの経験があるが、Excel VBAの経験は無いので、まずは触りだけ理解したい」という方を想定読者としているため、プログラミングのしくみや基礎的な用語の説明など、プログラミング初心者向けの内容は記載していない。
ここに記載の内容を習得するだけでも、VBAを使わない場合に比べればいろいろなことができるようになると思われるが、市販のExcel VBAの解説書の厚さを見て分かる通り、実際にはもっと遥かに複雑で高等なことが可能である。このページで不足している点や触れられていない点については、適宜解説書や他のリファレンスサイトを参照していただきたい。
基本
記述方法
VBAは、VBE(Visual Basic Editor)を使って記述する。VBEを表示するには、事前に「オプション」>「リボン」のユーザー設定で「開発」タブを表示しておく必要がある。開発タブ上の「Visual Basic」ボタンをクリックするとVBEが起動する。
VBEの左上の「プロジェクト」には、現在開いているブックとそこに含まれるシートやモジュールが表示される。ブックを右クリックして「挿入」>「標準モジュール」を選択すると、標準モジュールを追加できる。この標準モジュールに、後述のSubプロシージャやFunctionプロシージャを記述していく。一つの標準モジュールには複数のプロシージャを記述できる。また、複数の標準モジュールを追加することもできる。
モジュールには、標準モジュールの他にも「Microsoft Excel Objects」「フォームモジュール」「クラスモジュール」がある。
実行方法
Subプロシージャを記述した後、上のツールバーにある再生マークのボタンを押すと、表示中の標準モジュールに含まれるプロシージャを実行できる。標準モジュールにSubプロシージャが1つしか含まれていない場合は、そのSubプロシージャが実行されるが、複数含まれている場合は選択画面が表示される。また、引数が必要なSubプロシージャやFunctionプロシージャはここでは実行できない。
また、後述の方法で、ワークシート上に配置したボタンをクリックしてプロシージャを実行することもできる。
デバッグ
VBEの「表示」>「イミディエイトウィンドウ」を選択すると、イミディエイトウィンドウが表示される。ここにはプロシージャ内でDebug.Printによって出力した値が表示されるため、変数の確認などに便利である。
基本文法
プロシージャ
戻り値を返さないサブルーチンのことをSubプロシージャという。通常マクロというと、この一つ一つのSubプロシージャのことを言う。Subプロシージャは以下のように記述する。
Sub myProcedure()
…
End Sub
同じモジュール内の他のプロシージャから呼び出すには、Call myProcedureまたは単にmyProcedureと書けばよい。
引数を受け取るには以下のようにする。()の中に後述の変数宣言と似た記法で引数名と型を指定すればよい。なお、このように記述すると引数は参照渡しとなるので注意が必要である。値渡しにするには、それぞれの引数名の前にByValというキーワードを付ける必要がある。逆に、参照渡しであることを明示するためにByRefというキーワードも存在する。
Sub dialog(name As String, age As Integer)
MsgBox name & vbLf & age
End Sub
'呼び出し側
Sub myProcedure()
dialog "Excel太郎", 30
End Sub
Subプロシージャを途中で終了するには、Exit Subと記述する。
Sub myProcedure()
Dim ans As Integer
ans = MsgBox("実行してよろしいですか?", vbOKCancel + vbQuestion)
If ans = vbCancel Then Exit Sub
…
End Sub
Subプロシージャに対して、戻り値を返すユーザー定義関数のことをFunctionプロシージャという。Functionプロシージャは以下のように記述する。
Function sum(a As Integer, b As Integer) As Integer
sum = a + b
End Function
'呼び出し側
Sub myProcedure()
Dim x As Integer
x = sum(2, 3)
MsgBox x
End Sub
1行目の(~)の後に戻り値の型を指定する。Function名は同時にプロシージャ内で戻り値となる変数名にもなっているのが独特である。また、Subプロシージャと同様にByVal、ByRefキーワードが使用でき、デフォルトは参照渡しである。
SubプロシージャとFunctionプロシージャの呼び出し方法を見ると、Functionプロシージャには()を付けて呼び出しているのが分かる。このように、VBAで関数やメソッドを実行する場合、戻り値を使う場合は()をつけて、使わない場合は()をつけない、というルールがある。分かりやすい例として、後述のMsgBoxなどがある。()に関しては、以下の外部リンクが詳しい。
コメント
'から行末までがコメントとなる。
'ここはコメント
コードの改行
VBAでは、C言語やJavaScriptにおける;に相当するような行区切り文字がないため、基本的に1行に1つの文を書く。1行の途中で改行するには、末尾に_を書く。
msg = "とても長い行とても長い行とても長い行とても長い行" _
& "とても長い行とても長い行とても長い行とても長い行" _
& "とても長い行とても長い行とても長い行とても長い行"
変数宣言
変数はDimで宣言し、Asで型を指定する。
'Integer型の変数hogeを宣言。 Dim hoge As Integer '2個以上の変数をまとめて宣言することもできるが、変数ごとにAsの指定は必要。 Dim fuga As String, piyo As String
変数名には半角英数字の他、日本語も使うことができる。記号は_のみ使用可能で、先頭は数字と_以外でなければならない。大文字小文字は区別されない。
基本的な型には以下のようなものがある。
型名 | 意味 |
|---|---|
Boolean | 真偽値( |
Integer | 符号付き2バイト整数(-32768~32767) |
Long | 符号付き4バイト整数(-2147483648~2147483647) |
Double | 倍精度浮動小数点数 |
String | 文字列 |
これ以外にも、オブジェクトを受け取る場合は、そのオブジェクト名を型として指定する。また、セルの値など、任意の値を受け取る可能性があるものはVariantという特殊な型を使うことができる。
変数への値の代入は=を使う。
'変数hogeに値1234を代入。 hoge = 1234
Workbook、Worksheet、Rangeなどのオブジェクトの場合は、参照渡しにするため、Setを使う必要がある。
'Workbookオブジェクトを代入。
Set myWorkbook = Workbooks("myWorkbook.xlsx")
変更されない(再代入されない)値については、以下のような定数宣言を行なうこともできる。
Const MaxVal As Integer = 1000
数値
数値の四則演算には、他の多くの言語と同じく+、-、*、/が使えるほか、a \ bでa÷bの商、a Mod bでa÷bの余りを取得できる。
C言語やJavaScriptなどにおけるi++やi--のようなインクリメントとデクリメントの演算子は存在しない。代入文と加減演算子を使ってi = i + 1やi = i - 1とそれぞれ書く必要がある。
基本的な数学関数としては、以下のようなものが使用できる。
関数 | 意味 |
|---|---|
|
|
|
|
|
|
|
|
|
|
| eの |
|
|
| 0以上1未満の乱数を返す。 |
|
|
|
|
|
|
|
|
文字列
文字列リテラルは、ダブルクォート"~"で囲って記述する。また、文字列の結合は&を使う。
myStr = "これは" & "文字列" 'myStrは「これは文字列」となる。
文字列リテラルの中でダブルクォート自体は""と記述する。
myStr = "文字列は""hoge""と記述して下さい。" 'myStrは「文字列は"hoge"と記述して下さい。」となる。
改行やタブ文字は、定数として定義されているので、それを連結して使う。
'改行文字(0x0A) myStr = "ここで" & vbLf & "改行します。" 'タブ文字(0x09) myStr = "タブ文字は" & vbTab & "です。"
「0x0D 0x0A」に対応したvbCrLfも存在するが、ダイアログに表示するメッセージの改行やセル内の改行には基本的にvbLfを使う。
基本的な文字列操作としては、以下のようなものが可能である。
'文字列の分割
Dim items As Variant
items = Split("hoge/fuga/piyo", "/")
MsgBox items(0) ' "hoge"
MsgBox items(1) ' "fuga"
'文字列の検索
MsgBox InStr("hogefuga", "fuga") ' 5
MsgBox InStr("hogefuga", "piyo") ' 0
'文字列の置換
MsgBox Replace("hogefuga", "hoge", "piyo") ' "piyofuga"
条件分岐
if文は以下のように記述する。
If ○○ Then
…
ElseIf ○○ Then
…
Else
…
End If
○○には条件式を記述する。比較演算子は、他の多くの言語と同じく<、<=、>、>=が使えるが、等価演算子は=(等しい)と<>(等しくない)である。また、論理演算子はAnd、Or、Notである。
If x = 10 And y <> 0 Then
…
End If
Like演算子を使うと文字列に対してワイルドカード等を用いたパターンマッチを行なうことができる。
If str Like "abc?" Then ' abcの後に1文字 If str Like "abc*" Then ' abcの後に0文字以上 If str Like "abc#" Then ' abcの後に数字1文字 If str Like "abc[x-z]" Then 'abcの後にx、y、zのいずれか1文字 If str Like "abc[!x-z]" Then 'abcの後にx、y、z以外の1文字
ElseIf、Elseがない場合で、しかも間の文が1つしかないときは、1行で記述することもできる。
If i = 5 Then Exit For
'以下と同じ
If i = 5 Then
Exit For
End If
くり返し
基本的なくり返しには以下のような記法がある。
'iを1から10まで変化させてくり返し
Dim i As Integer
For i = 1 To 10
…
'くり返しから抜ける
If i = 5 Then Exit For
Next i
'条件が真である間くり返し
Do While x < 10
…
'くり返しから抜ける
If x = 5 Then Exit Do
Loop
For i = 1 To 10の部分をFor i = 1 To 10 Step 2などとするとカウンタ変数の増分を指定できる。Next iはカウンタ変数を省略してNextだけでもよい。Do While ~のWhile ~を省略してDoだけにすると、無限ループとなる。
次のループへ移る命令はVBAでは存在しないため、GoToを使って以下のように記述するしかない。また、多重ループを抜ける場合もこのようにする。
For i = 1 To 10
'ループ末尾へジャンプすることで、次のループへ移る。
If i = 5 Then GoTo EndOfLoop
…
'ループ末尾にラベルを置いておく。'
EndOfLoop:
Next i
For i = 1 To 10
For j = 1 To 10
'ループの外へジャンプすることで、多重ループを抜ける。
If i = 5 And j = 5 Then GoTo EndOfLoop
Next j
Next i
'ループの外にラベルを置いておく。'
EndOfLoop:
GoToはその名の通りgoto文であるため、上記のような慣例以外ではむやみに使わない方がよい。
配列
配列は、型名と最大の要素番号または要素番号の範囲を指定して以下のように宣言する。使用可能な要素番号はLBoundとUBoundで取得できる。
'hoge(0)~hoge(3)の4要素を持つ配列を宣言 Dim hoge(3) As Integer hoge(0) = 100 MsgBox LBound(hoge) & "~" & UBound(hoge) ' 0~3 'fuga(1)~fuga(3)の3要素を持つ配列を宣言 Dim fuga(1 To 3) As Integer fuga(1) = 100 MsgBox LBound(fuga) & "~" & UBound(fuga) ' 1~3 'piyo(0, 0)~piyo(1, 2)の6要素を持つ2次元配列を宣言 Dim piyo(1, 2) As Integer piyo(1, 2) = 100 MsgBox LBound(piyo, 1) & "~" & UBound(piyo, 1) ' 0~1 MsgBox LBound(piyo, 2) & "~" & UBound(piyo, 2) ' 0~2
要素数を動的に決めるには、要素数未定で宣言した後、ReDimを使う。
Dim hoge() As Integer ReDim hoge(3) 'hoge(0)~hoge(3)が使用可能となる。 hoge(3) = 100
配列の各要素を任意の値で初期化するのにArrayが使用できる。
'要素数未定のVariant型で宣言する。 Dim hoge() As Variant 'hoge(0)~hoge(4)の5つの要素を任意の値で初期化する。 hoge = Array(1, 2, 3, 4, 5)
連想配列
連想配列を実現するにはDictionaryを使用する。
'連想配列の作成
Dim hash As Object
Set hash = CreateObject("Scripting.Dictionary")
'キー・値ペアの追加
hash.Add "key1", 100
hash.Add "key2", 200
'値の取得
MsgBox hash.Item("key1")
'値の削除
hash.Remove "key2"
'存在確認
If hash.Exists("key2") Then
…
End If
'値の列挙
Dim key As Variant
For Each key In hash
MsgBox hash.Item(key)
Next key
With文
同じオブジェクトに対して連続して処理を記述するような場合、With文が便利である。
'With文を使わない場合
Worksheets("Sheet1").Range("A1") = 100
Worksheets("Sheet1").Range("B2") = 200
Worksheets("Sheet1").Range("C3") = 300
'With文を使う場合
With Worksheets("Sheet1")
.Range("A1") = 100
.Range("B2") = 200
.Range("C3") = 300
End With
With文は入れ子にもできるが、可読性が落ちる場合もあるため注意が必要である。また、With~End Withの中で最初にWithで指定したオブジェクト自体に変化が及ぶような処理を行なってはいけない。
Excel上の操作
ワークブックの操作
現在開いているワークブックはWorkbooksから取得する。Excelでは、通常同じファイル名のワークブックは同時に開くことができないため、開いているワークブックは下記のようにファイル名のみによって一意に特定できる。
'Workbook型の変数を宣言
Dim myWorkbook As Workbook
'ワークブック「myWorkbook.xlsx」を取得
Set myWorkbook = Workbooks("myWorkbook.xlsx")
現在開いている全てのワークブックを列挙するには、以下のようにする。これで目的のワークブックが開かれているか事前に確認した方がよい(オブジェクトが存在しない状態はIs Nothingで判定できる)。
Dim wb As Workbook, wbName As String, wbTarget As Workbook
'開いている全てのワークブックについてくり返し
For Each wb In Workbooks
'Nameプロパティでワークブック名を取得
wbName = wb.Name
'目的のワークブックが見つかればくり返しを抜ける
If wbName = "Book1.xlsx" Then
Set wbTarget = wb
Exit For
End If
Next
'ワークブックが見つからなかったら終了する
If wbTarget Is Nothing Then
MsgBox "ワークブックが開かれていません。"
Exit Sub
End If
現在最前面で開いているワークブックはActiveWorkbook、実行しているマクロが保存されているワークブックはThisWorkbookで取得できる。マクロが保存されているワークブックとは異なるワークブックを処理対象とすることもあるため、ActiveWorkbookとThisWorkbookは常に同じとは限らない。
Dim name As String '自分自身のワークブック名 name = ThisWorkbook.Name
ワークシートの操作
ワークシートは、WorkbookオブジェクトのWorksheetsプロパティから取得する。Worksheetsには、1から始まるインデックス番号か、ワークシート名を与えることで、特定のワークシートを取得できる。
'Worksheet型の変数を宣言
Dim myWorksheet As Worksheet
'myWorkbookの「Sheet1」というワークシートを取得
Set myWorksheet = myWorkbook.Worksheets("Sheet1")
ワークブック内のワークシートを列挙するには、以下のようにする。他のワークブックのワークシートを参照する場合などは、ワークブックの場合と同様にこちらも事前に存在確認をした方がよい。
Dim ws As Worksheet, wsName As String
'全てのワークシートについてくり返し
For Each ws In myWorkbook.Worksheets
'Nameプロパティでワークシート名を取得
wsName = ws.Name
…
Next
なお、ワークブックの指定を省略してWorksheetsとだけ書いた場合は、ThisWorkbook.Worksheetsと同じ意味になるため、マクロを保存したワークブック内で処理が完結するのであれば、ワークブックの指定は省略できる。
現在最前面に表示しているシートはActiveSheetで取得できる。当然ながら、これはActiveWorkbook.ActiveSheetの略である。
セルの操作
ワークブック、ワークシートを特定して、初めてセルの操作が可能となる。単一セルまたはセル範囲を表すオブジェクトはRangeオブジェクトといい、WorksheetオブジェクトのRangeまたはCellsプロパティによって取得する。ワークシートの指定を省略した場合、ActiveSheetを指定したものとなる。
RangeとCellsの使い分け方としては、基本的に、変数を使って動的に位置を指定する場合にCellsを使う。"A2"などの文字列を生成してRangeに渡すことも不可能ではないが、コードの可読性に難が生じるため通常は行わない。
Cells(2, 1)は実際にはCells.Item(2, 1)の省略形である。後述の例で分かるように、引数の無いCellsは全てのセルを表し、Itemでその範囲内の相対位置を指定することで、単一のセルを取得している。
'A2セル
Range("A2")
Cells(2, 1)
Range(Cells(2, 1))
'A2セルとB4セル
Range("A2,B4")
'A2~B4の矩形範囲
Range("A2:B4")
Range("A2", "B4")
Range(Cells(2, 1), "B4")
Range(Cells(2, 1), Cells(4, 2))
行や列の全体を選択するには、Rangeでも可能なほか、RowsやColumnsも使用できる。また、前述の通り、引数無しのCellsは全セルを表す。
'2~4行の全て
Range("2:4")
Range(Rows(2), Rows(4))
Rows("2:4")
'A~B列の全て
Range("A:B")
Range(Columns(1), Columns(2))
Columns("A:B")
'全てのセル
Cells
あるセルからの相対的な位置を選択するには、Offsetを使用する。また、Ctrlキー+矢印キーを押したときのように連続したデータの端のセルを選択するのにEndが使用できる。さらに、範囲内の1つのセルを選択するには、Cellsが暗黙的に使用しているItemをRangeオブジェクトに対して使用すればよい。
'A2セルから2行下1列右のセル(=B4セル)
Range("A2").Offset(2, 1)
'A1セルから見て下方向または右方向に連続したデータの端のセル
Range("A1").End(xlDown)
Range("A1").End(xlToRight)
'選択したセル範囲内の各セルについてくり返し
Dim myCell As Range, i As Integer
Set myCell = Range("A3:A7")
For i = 1 To 5
'A3~A7が順に選択される
MsgBox myCell.Item(i, 1)
Next i
Rangeオブジェクトが取得できたら、そのセルに対して各種の操作が可能となる。値の代入や取得の際の.Vauleは省略できる。
'値の取得
Dim val As String
val = Range("A2").Value
val = Cells(2, 1).Value
'セルへ値の代入
Range("A2").Value = 1234
Cells(2, 1).Value = 1234
'セル位置の取得
Dim i As Integer, j As Integer
i = Range("A2").Row ' 2
j = Range("A2").Column ' 1
'範囲内に含まれるセルの数
MsgBox Range("A2:B4").Count ' 6
'範囲内で指定の文字列に一致するセル
MsgBox Range("1:1").Find("価格").Column
セルの書式や装飾
セルの文字色や背景色は以下のようにして設定できる。パレット番号は、「カラーパレットの位置」を表す番号で、全部で56種類あり、デフォルトでは以下のようになっている。ユーザーによってパレットが変更されている場合は異なる色になっていることもある。
'RGBで指定
Range("A1").Font.Color = RGB(0, 0, 0) '文字色
Range("A1").Interior.Color = RGB(255, 255, 255) '背景色
'パレット番号で指定
Range("A1").Font.ColorIndex = 1 '文字色
Range("A1").Interior.ColorIndex = 2 '背景色
パレット番号とデフォルトの色: 1 53 52 51 49 11 55 56 9 46 12 10 14 5 47 16 3 45 43 50 42 41 13 48 7 44 6 4 8 33 54 15 38 40 36 35 34 37 39 2 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
フォントと文字サイズは以下のようにして設定できる。
Range("A1").Font.Name = "MS Pゴシック"
Range("A1").Font.Size = 14
行の高さや列の幅は以下のようにして設定できる。
Rows(1).RowHeight = 20 '1行の高さ Columns(1).ColumnWidth = 20 'A列の幅
文字の位置は以下のようにして設定できる。
Range("A1").HorizontalAlignment = xlLeft '左揃え
Range("A1").HorizontalAlignment = xlCenter '中央揃え
Range("A1").HorizontalAlignment = xlRight '右揃え
Range("A1").VerticalAlignment = xlTop '上揃え'
Range("A1").VerticalAlignment = xlCenter '上下中央揃え'
Range("A1").VerticalAlignment = xlBottom '下揃え'
その他、様々な設定が自在にできる。
メッセージボックス
画面に確認ダイアログを出すにはMsgBoxを使う。
'「完了しました。」というメッセージと「OK」ボタンを備えたダイアログを表示。 MsgBox "完了しました。"
第2引数で、表示するボタンの種類やダイアログのアイコンを変更できる。
'警告アイコンのダイアログを表示。 MsgBox "この操作はできません。", vbCritical
()を記述して実行すると、ユーザーが押したボタンを戻り値として受け取ることができる。
'戻り値を受け取るための変数
Dim ans As Integer
'「OK」「キャンセル」の2つのボタンと問い合わせアイコンを表示する。
ans = MsgBox("実行してよろしいですか?", vbOKCancel + vbQuestion)
'押されたボタンによって条件分岐などができる。
If ans = vbCancel Then Exit Sub
表示するボタンの種類やダイアログのアイコンを指定する定数は以下のようなものがある。ボタンとアイコンを同時に指定するには、上記例のようにそれぞれを足し合わせて指定すればよい。
定数 | 意味 |
|---|---|
| 「OK」ボタンのみ表示 |
| 「OK」ボタンと「キャンセル」ボタンを表示 |
| 「中止」「再試行」「無視」の3ボタンを表示 |
| 「はい」「いいえ」「キャンセル」の3ボタンを表示 |
| 「はい」ボタンと「いいえ」ボタンを表示 |
| 「再試行」ボタンと「キャンセル」ボタンを表示 |
| 警告アイコンを表示 |
| 問い合わせアイコンを表示 |
| 注意アイコンを表示 |
| 情報アイコンを表示 |
また、戻り値として受け取る値は以下のようなものがある。
定数 | 意味 |
|---|---|
| 「OK」ボタンが押された。 |
| 「キャンセル」ボタンが押された。 |
| 「中止」ボタンが押された。 |
| 「再試行」ボタンが押された。 |
| 「無視」ボタンが押された。 |
| 「はい」ボタンが押された。 |
| 「いいえ」ボタンが押された。 |
MsgBoxの第3引数には、ダイアログウインドウのタイトルを指定できる。省略するとタイトル部分は「Microsoft Excel」と表示される。
その他
ボタンによるマクロ実行
ワンクリックでマクロ(Subプロシージャ)を実行できるボタンをワークシート上に作成するには、開発タブの「挿入」>「ボタン(フォームコントロール)」を選択する。ワークシート上でドラッグするとボタンが生成されて、クリック時に実行するマクロを選択できる。
作成後のボタンは、クリックするとマクロが即実行されてしまうため、リサイズや移動をしたりボタン名を変更したりするには、Ctrl+クリックをする。
省略記法
ここまで見てきた通り、VBAでは様々な省略記法があり、省略する方が一般的となっているものも多いが、省略しない記法を知ることで理解が深まる場合もある。
以下の外部リンクが詳しい。
ファイル出力
テキストファイルの出力は以下のようにする。出力先のファイル名はフルパスで指定する必要がある。ワークブックのあるディレクトリのパスを得るにはPathを参照すればよい。
Dim fileName As String fileName = ThisWorkbook.Path & "\output.txt" Open fileName For Output As #1 Print #1, "出力テスト" & vbLf; Close #1
Printは通常自動的に改行を出力するが、上記のように末尾に;を付けると改行を出力しない。改行文字を指定したい場合や、1行を複数に分けて出力するときなどは;をつける必要がある。
BOMなしUTF-8でのファイル出力
VBAにおいて、テキストファイルへの出力をすると、通常の方法では文字コードがShift_JISやBOMありのUTF-8となってしまう。BOMなしのUTF-8で出力するには、以下のような極めて回りくどい方法が必要となる。
Dim outputText As String, fileName As String
'出力するテキスト
oututText = "出力する文字列"
'出力するファイル名
fileName = "C:\~\output.txt"
Dim fout As Object, bytes() As Byte
Set fout = CreateObject("ADODB.Stream")
With fout
.Type = adTypeText
.Charset = "UTF-8"
.Open
.WriteText outputText
.Position = 0
.Type = adTypeBinary
.Position = 3
bytes = .Read
.Position = 0
.Write bytes
.SetEOS
.SaveToFile fileName, adSaveCreateOverWrite
.Close
End With
やっていることは、「UTF-8に出力後、バイナリに変換し、先頭3バイト(BOM)を削る」ということのようである。なお、途中で登場するad~から始まる定数を使用するには、VBEのメニューの「ツール」>「参照設定」から「Microsoft ActiveX Data Objects~」を選択しておく必要がある。