エクセルの マクロ言語(VBA)に 慣れるためのメモ

エクセルの
マクロ言語(VBA)に
慣れるためのメモ
2014/1/31~
~2015/12/31
作成:縁木精史
●エクセル・マクロ言語とは
エクセルのマクロとは、手番をあらかじめ記述しておくことにより、通常手操作している処理
を自動化すること・自動化したものです。
マクロで手番を記録すれば、繰り返し作業を自動化できます。また、連続的な集計作業の
フローを明示化でき、作業条件が変わらなければ、マクロはそのまま継続使用できます。
マクロは、コードを直接入力する方法と操作が自動的にコード変換されたキー記録の2つの
方法があります。簡単なコードは覚えましょう。複雑なコードになるばあいはキー記録で作成
されるコードを利用しましょう。ただ、キー記録で作成されるコードの意味がほぼ分かる程度
までは、コード・言語レベルの知識を持つことが大切かと思います。
マクロ言語とは?
http://excelvba.pc-users.net/index.html
http://www.eurus.dti.ne.jp/~yoneyama/Excel/Exl-_zen.htm#vba_jitu
http://excelvba.pc-users.net/
VBA画面の説明
http://www.eurus.dti.ne.jp/~yoneyama/Excel/vba/vba_vbe.html
ExcelVBAの使い方
http://homepage2.nifty.com/nandemoarchive/cyou_nyumon/excel_01.htm
●VBA for Excelの特徴
(他のスクリプト・言語に比べ)
・コード(構文)の次群が自動表示され、また実行エラー箇所を示してくれます。
・ネット上に、山と言うほどの説明があるし、回答者が多くいます。
注1)英語語句の読み方は、 http://vbae.odyssey-com.co.jp/study/glossary.html を参照
注2)マクロとは、プログラミング言語。
その一つに、「Visual Basic for Applications」(略してVBA)があり、EXCEL用のVBA、WORD
用のVBA、powerpoint用のVBAがあります。
注3)まずは、VBAが何であるかを理解する前に、実際に作成し実行し、その結果を見て経験する、
「馴染む」「慣れる」ことが理解への近道。
注4)「変数」とは?
マクロ内で使用する”セル”みたいなものです。ただ、セルのように何でも入るわけではありません。入れるものが
数値、文字、範囲、シート名などであれば、それぞれに変数の定義をする必要があります。
変数は、値の入れ物ですが、その処理やブックを終了すると、消えます。
定義 dim i as long ⇒ iを数値型(long)として定義します。
文字を入れるなら、dim i as string とします。 ただ、慣習があり、I,j,kなどは数値を入れる変数とされています。
ちょっと不便なセルが変数ということでしょうか?
成り立ちとしては、変数から、セルが考えられたのでしょうけど。
[参考] 「わずかな知識で
わずかな知識で VBA!
!」 http://koikide.net/hensuu.html
●マクロ言語の基本文
A1と言っても、同じ画面を見ている人同士は
①セル・シート・ブックの表現
それで通じるが、コードを記述する場合は、そ
セルA1
⇒ range(“a1”)
range:読み方:レンジ
のような前提は通じないので、どのブックの、
どのシートの、A1かを指定しないといけない。
シート1
⇒ sheets("sheet1")
abcブック ⇒ workbooks("abc.xlsx")
マクロが保存されているブック ⇒ Thisworkbook
アクティブになっているブック ⇒ Activeworkbook : キーボード・マウスが対象とするブック
②セルに値を代入する。(右から左に代入)
セルA1に10を代入する
range("a1")=10
sheet2のシートのB1に、「住所」を代入する。
‘ マクロでは、シートを切り替える必要はない。
sheets(“sheet2”).range(“b1”)=“住所”
セルA1の値をセルA10に代入する。
range(“a10”)=range(“a1”) 又は range("a10")=range("a1").value
セルA1にSUM関数を代入する
range("a1")="=sum(b1:b10)"
セルA1からセルA100を選択する
range(“a1:a100”).select
セルA1からA100までのすべてのセルに、5を入力する
Range("a1:a100") = 5
シートやブックを明示的に
もし、abc.xlsxのシート1がアクティブで、セルA1に100を代入するなら、
指定しない場合、予期せ
range("a1")=100 で良いが、
ぬ処理結果がシートに出
sheet2がアクティブである場合に、sheet1のA1に100を入れるなら、
力される可能性があるの
で、注意すること!
sheets(“sheet1”).range(“a1”)=100
とします。
2つのブックをオープンしている場合なら、
workbooks("abc.xlsx").sheets("sheet1").range("a1")=100
としないと、どのブックに代入するのか不明確となり、意に反する動きとなります。
●マクロ言語の基本文
③変数を使う(処理上仮に値を入れるもの)
まず、使うだろう変数を定義します。
dim i as long ‘ 変数iは慣習、longは数値型。文字を入れる変数なら、stringとします。
i=range(“a1”) ‘ ← A1の値は数値であること。でないと、エラーとなります。
range("b1")=i
range("b2")=I
④コピー&ペースト
range(“a1:a100”).copy range(“h2”) ‘ a1からa100のセル範囲をコピーし、h2に張り付けます。
dim 範囲 as range
‘セル範囲として 範囲と言う語を変数定義。
set 範囲=range(“a1:a1000”)
‘セル範囲を“範囲”に代入する。(range型のみsetを使う。))
範囲.copy range(“h1”)
’範囲“をコピーして、H1セルに貼り付けます。
●基本処理
(1)繰り返し処理
①for~
~next
セルA1からA100までに、1,2,3と連番を入力する場合。
dim i as long
for i=1 to 100
range("A" & i)=i
next
‘ nextは、next i と変数を付けてもOKです。
‘ for~nextのなかに、for~nextがあるような入れ子構造をネストすると言うそうですが、
‘ そのような場合、nextがどの変数のものかなどを明示するために、nextの後ろに 変数を
‘ 付けたほうが良いと思われますが、そもそも的にはネストした構文は、使わないことを
‘ お勧めします。
セルA10から上方向に、10をカウントダウンした数値を代入します。
dim i as long
for i=10 to 1 step -1
range("a" & i)=10-i
next
②do While ~ loop
while:読み方:ホワイル
:読み方:ホワイル
セルのA1~A10に1~10を入力する場合。
i=1
Do While i < 11
Worksheets("Sheet1").range("a" & i).Value = i
i = i + 1 ‘ for~nextの場合は自動で変数iはカウントアップしますが、do~loopの場合はしません。
Loop
●基本処理
(2)条件文
if~
~end if 条件文
①A1セルが10だったら、A1セル値を2倍にした数値をB1セルに代入する場合。
if range("a1")=10 then range("b1")=range("a1")*2
②A1セルが5だったら、B1セルにA1セル値を代入する場合。
if range("a1")=5 then
range("b1")=range("a1").value
end if
③A1セルが5以上だったら、A1セルに10を加算した値をB1セルに代入し、
そうでなかったら、A1セルに10をマイナスした値をB1セルに代入する場合。
if range("a1")>=5 then
range("b1")=range("a1")+10
Else
range("b1")=range("a1")-10
end if
●基本処理
(3)for~
の繰り返し文とif文を組み合わせる
(3) ~nextの繰り返し文と
の繰り返し文と 文を組み合わせる
①A1から
からA100で、値が○だったら、
で、値が○だったら、B列に●を代入する。
から
で、値が○だったら、 列に●を代入する。
dim i as long
for i=1 to 100
if range("a" & i)="○" then range("B" & i)="●"
next
②A1から
からA100で、
で、A列が○で、かつ
から
で、 列が○で、かつB列が●だったら、
列が○で、かつ 列が●だったら、C列に
列が●だったら、 列に×
列に×を代入する。
dim i as long
for i=1 to 100
if range("a" & i)="○" and range("B" & i)="●" then range("C" & i)="×"
next
(4)マクロ言語の中でワークシート関数を使うには
セルA6からa4400,000までの可視セル(フィルター実行した結果)の数をカウントし、A1セルに代入する。
range("a1") = Application.WorksheetFunction.Subtotal(3, Range("a6:a400000"))
もし、A1セルに関数式を代入するなら、 Range("a1") = "=Subtotal(3, a6:a400000)" とする。
たとえば、マクロからシートに関数式を代入し、その結果をまたマクロで利用することも可能。
B1セルに、A1セル値を参照するvlookup関数があったとして、
range("a1")=10
msgbox range("b1")
この場合のiには、vlookup関数の結果の値が入る。
計算が複雑であれば、結果が出る時間とマクロが動作する時間差が生まれて、
変数iに何も入らないかもしれないが、30万件のvlookupでも一瞬に計算される。
おそらく、通常のオフィスでの計算では、時間が掛かり過ぎるという計算はないと
思われる。あるとすれば、計算ではなく、シート選択やシート上の値の分類、フィルター、ピボットや
ブック選択などの処理だと思われます。
●基本処理
(5)キー記録を参考にしてコードを作る。(キー記録のアレンジ)
例:
L6~L13を選択し、コピーし、N6をクリックし、行列を入れ替えて貼り付ける処理を
キー記録したら、
Range("L6:L13").Select
Selection.Copy
Range("N6").Select
Selection.PasteSpecial Paste:=xlPasteAll, Operation:=xlNone, SkipBlanks:= _
False, Transpose:=True
Range("L6").Select
Application.CutCopyMode = False
操作を忠実に記録しているが、無駄も多い。
マクロ言語では、いちいちセルをクリックする必要がないので、
Range("L6:L13").copy でコピーが完了する。
copyの次にセルを指定すると、貼付となるので、
Range("L6:L13").copy
Range("N6").PasteSpecial Paste:=xlPasteAll, Transpose:=True
Range("l6").Select
Application.CutCopyMode = False
とすることができる。
●基本処理
(6)データの最終行を知る
マクロでデータを集計する場合に、どの行に対して行うのかを知る必要が出てきます。
お約束のコードは、
[参考] http://officetanaka.net/excel/vba/tips/tips130.htm
Cells(Rows.Count, 1).End(xlUp).Select cells:読み方:セルズ
意味:A列のシート最終行からCTRL+↑キーで最初に値が見つかったセルを選択する。
A列の最終行番号なら、
Cells(Rows.Count, 1).End(xlUp).row
cells(行、列)の表記となり、range(列行)とは反対となる。
range(列行)は、A1,B1と分かり易いが、cells(行、列)となっているが、列についても変数が使える利点があります。
cells(1,1)は、range("a1")と同じ。
Cells(Rows.Count, 1).End(xlUp).row +1 とすれば、A列の最終行の次の行となり、追加すべき行となる。
●基本処理
(7)ファイル選択画面を表示し、選択したエクセルブックを開く。①
[参考]http://officetanaka.net/other/extra/tips15.htm
Sub Book_open()
Dim Atai As Variant, aFile As Variant, ws As Variant, wb As Variant, flag As Variant
Dim F_name As String
Dim i As Long, j As Long
Atai = MsgBox("ブックを開く" & vbCrLf & _
"オープンするブックを選択して下さい。", vbOKCancel, "ブックの選択")
If Atai = vbCancel Then Exit Sub
'※ ファイルを取得する為に、指定した拡張子のみ表示したエクスプローラを起動する。
aFile = Application.GetOpenFilename("xlsファイル (*.xls?),*.xls?", , "ブックを選択して下さい", "OK", False)
If aFile <> False Then
j = Len(aFile)
For i = j - 1 To 1 Step -1
'左から最初に見つかる¥で、文字列を区切る。
If Mid(aFile, i, 1) = "¥" Then
F_name = Mid(aFile, i + 1, j) 'ブック
'選択したxlsのパスとファイル名を変数に代入。
Exit For
End If
'
MsgBox aFile
' パス付ファイル名
Next
'
MsgBox Mid(aFile, 1, i) 'パス名
Else
'
MsgBox F_name
'ファイル名
Exit Sub
End If
'同名のブックがあれば、flag=trueとする。既に、開いているなら、処理中止する。
For Each wb In Workbooks
If wb.Name = F_name Then
flag = True
Exit For
End If
Next wb
If flag = True Then
Atai = MsgBox(F_name & "は、開いています" & vbCrLf & "処理を開始する前に、閉じてください。" & vbCrLf & _
"オープンしているブックをアクティブにするなら、OKをクリックしてください。", vbOKCancel)
If Atai = vbCancel Then Exit Sub
Workbooks(F_name).Activate
Exit Sub
End If
Workbooks.Open Filename:=aFile
End Sub
●基本処理
(7)ファイル選択画面を表示し、選択したエクセルブックを開く。②
[参考]http://officetanaka.net/other/extra/tips15.htm
Sub ブック選択open()
'ブックを複数選択して、オープンする。
'既にopenしている場合は、openしない。
Dim OpenFileName As Variant, Target, Wb As Variant
Dim I As Long, j As Long
Dim Filename
Application.ScreenUpdating = False
OpenFileName = Application.GetOpenFilename(FileFilter:="Microsoft Excelブック,*.xls?", MultiSelect:=True)
i=5
If IsArray(OpenFileName) Then
For Each Target In OpenFileName
j=0
For Each Wb In Workbooks
If Wb.Name = Dir(Target) Then
j=1
MsgBox "既に" & Wb.Name & " はopenしています。"
End If
Next
If j = 0 Then Workbooks.Open Target
i=i+1
Next Target
Else
' MsgBox "ブック選択は、キャンセルされました"
End If
ThisWorkbook.Activate: Sheets("ブック一覧").Activate
End Sub
●基本処理
(8)図形(ボタン)とマクロ(モジュール)との関連付け
ボタンは図形から選んで、シート上に作成します。
ボタンを右クリックして、マクロの登録を選択。
既に、モジュールを作成していれば、モジュール名=マクロ名が表示されているので、
それを選択して、OKボタンクリックで、関連付け完了となります。
これで、ボタンをクリックすると、マクロが実行されます。
この関連付けは、オブジェクトであればできるので、テキストボックスにも、
グラフにもマクロとの関連付けを行えます。
○コントロールのコマンドボタンを使っても、当然に関連付けることが
できます。フォームのコマンドボタンだと、サイズを確定した後に、
マクロの登録フォームが開きます。
(9)処理途中にメッセージを表示させる
マクロが勝手に処理を行い、いつ終了したのかわからないので、
メッセージを表示させることができます。
・・・
msgbox "処理が終了しました。"
Atai=msgbox("次の操作に移りますか?",vbokcancel,"title")
if atai=vbok then msgbox “yes selected ”
・・・
●業務作業に適用する
(1)業務フローを想像してみる。
例:各部署に指定のエクセルブック(書式統一)を配付・回収し、その個々のブックの集計を半自動化する。
①前提条件
配付のエクセルブックは、以下のとおりとする。
C11~c15までを回答欄とする。
シートは1枚のみのブックとする。
シート名はsheet1とする。
C10は氏名とし、氏名がない場合は確認後氏名をハンド入力する。
②処理イメージ
自動で行う事は ファイルを手作業で指定し、開いたら自動で集計ブックに転記し、
そのブックを閉じる。
③作っていく
新規ブックを"集計"の名前で保管。
集計ブックのVBEを開いて、挿入~標準モジュール
モジュール画面にコードを入力していく。
sub マクロ名(自由の名前を)()
' ()はお約束。
'処理を書いていく。
「‘」 が行の先頭にある行は、注釈となり、処理では無視される。
end sub
今回は、集計というマクロ名にする。
まず、行いたいことを注釈で書く。
その注釈をコードに直していく。
●業務作業に適用する
(2)業務に応用してみる。~注釈書き、その後コード化する。
④処理を注釈(言葉)で書いてみる。
ファイル選択画面を表示する。
(ファイル選択画面は、特定のフォルダーを指定したい。)
ファイル選択画面で、対象ファイルを見つけ、選択する。
何も選択しない場合もある。
選択したファイルを開く。
サーバーからのファイルを開くのに時間が掛かるかもしれない。
貼付け先の最終行の次行を知る。
開いたブック・シートの対象セル範囲をコピーする。
このブック・シートの最終行の次行に、貼り付ける。
クリップボードをクリアする。
開いたブックを閉じる。
●業務作業に適用する
⑤実際のコード
Sub 集計() '回答表を選択して開く
Dim Openfilename as string
With CreateObject("WScript.Shell")
.CurrentDirectory = “¥¥○○○¥総務部¥01_作業¥”
End With
''貼付け先の行を調べる
Dim i As Long
i = ThisWorkbook.Sheets(“sheet1”).Cells(Rows.Count, 1).End(xlUp).Row + 1 ‘ 貼り付け先はこのマクロがあるブックのシート1のA列
Openfilename = Application.GetOpenFilename(“Microsoft Excelブック,*.xls?”) ‘ 開きたいファイル名をフルパスで取得。
‘ フルパス付ファイル名= Openfilename
If Openfilename <> "False" Then ' 選択内容が空でなかったら、
Workbooks.Open Openfilename ' 選択したファイルを開く
Else
exit sub
End If
MsgBox dir(openfilename) & “を転記します。“
'回答表のC11~C15までをコピーし、集計ブック(このブック)の5行目から貼り付けていく。
'' オープンしたら、そのブックがアクティブになっている!
ActiveWorkbook.Sheets("sheet1").Range("c11:c15").Copy
‘ コピーしたセル範囲を行列入れ替えて、このシートの(最終行+1)行に貼り付ける。
ThisWorkbook.Sheets("sheet1").Range("a" & i).PasteSpecial Paste:=xlPasteAll, Transpose:=True
‘ コピーモードを解除する。
Application.CutCopyMode = False
'貼付けが完了したら、回答表は閉じる。
ActiveWorkbook.Close
'適時、処理が終わる都度、メッセージを表示する。
MsgBox "処理が完了しました。“
End sub
●機能を追加する
「集計」というマクロ(プロシージャ)が完成したら、更に使い易くするために機能追加を考えてみます。
だた、この段階では、機能の為の機能を考えてしまうことがよくあります。
ハンドで簡単にできるのであれば、マクロを作成する必要はありません。
マクロを本当に作成するべきかどうか、自問することが大切かと思います。
[Q.]
①自らの作業効率と間違い防止の為なのか、
②他の人に使ってもらう為に作成するのか、
③マクロの勉強の為に作成するのか、充分に検討・考えるべき。
[A.]
①作業効率よりも間違い・誤操作の防止に重点をおいて、考え・作成するべきでしょう。
②他の人の力量レベルはそのままか、力量レベルを上げるのか、2つの方法がある。
③あまり好ましいとは言えない。
更に、欲しい機能は?
転記したファイルは、転記済みとして記録・チェックし、2重の転記を防ぐ。
●注釈の重要性
コードを書いて、正常に実行すれば、終わりではありません。
その処理が何をしているのか、同時に決めた変数が何を表すのか、条件分岐はなぜしないといけないのか、
などなど、出来るだけ詳細に注釈を入れてください。
作成中や作成直後は、その処理がイメージされているが、時間と共にその記憶は薄れます。
1年後に、不具合や機能追加・機能修正が発生した場合に、自ら作成したとはいえ、マクロの処理イメージを
忘れていることが多々あります。 一からコードを読んで、理解する手間を省くためにも、自分のために、注釈
を書いておきましょう。
[例]
Sub tesuto1()
‘テストのマクロです。 セルに連続したデータを入力します。
'ただし、5の倍数の場合は、値を10倍します。
'セルの値を入れる変数を定義します。
Dim i As Long
'セル1行から100行まで、セル毎に判定し、結果を代入します。
For i = 1 To 100
'まずiには、1が代入されます。
'nextまで行ったら、iに1が加算されます。
Range("a" & i) = i
' a mod 5とは、aを5で割った時の余りを求めます。
' 5の倍数とは、5で割ると余りがないので、余り=0となります。
If (i Mod 5) = 0 And i > 1 Then
Range("a" & i) = (i) * 10
Else
'5の倍数以外の時は何も行いません。
End If
Next
End Sub
注釈は、コードの実行とは関係なく、コードの
意味、役割など、コード編集時に役立てます。
注釈は、行の先頭に、「’」を入力します。この
「’」以降の行の語句は、注釈と解釈されて実行
時は無視されます。1行ごとにつける必要があり
ます。
注釈の始まり、注釈の終わりという記号は、あ
りません。
●簡単な練習(マクロを作ってみましょう。)
(1)A1からA20までに、連番を入力してみる。
開発タブ~VisualBasicを選択し、挿入~標準モジュールを選択。
白い入力スペースが表示されるので、下記のように入力します。
シートとVisualBasicの編集画面(VBE)を併置すると分かり易い。
sub tesuto1()
Dim i as long
for i=1 to 20
range(“a” & i)=i
next
end sub
エクセルのシートをアクティブにして、VBE画面も見えるようにウインドウ調整して、
ツールバーにある ▶ ボタン(実行ボタン)をクリックし、シート上のA1からA20セルに、1からの連番が
入力されたことを確認します。
このコードをシートを切り替えて実行してみましょう。やはり、同じ結果になります。
つまり、このコードはアクティブシートに対して行われるコードです。アクティブシートが予期していな
いシートであった場合、既に入力されている値を書き換えます。
マクロでの変更は、ctrl+zの元に戻すが効きません。
●簡単な練習(マクロを作ってみましょう。)
(2) 先ほどのコードで、対象シートをsheet1に限定してみます。
sub tesuto1()
Dim i as long
sheets(“sheet1”).activate ‘<----- sheet1シートをアクティブにします。
for i=1 to 20
range(“a” & i)=i
next
end sub
(3)アクティブシートに関係なく、sheet1シートにマクロが実行するようにします。
sub tesuto2()
Dim i as long
sheets(“sheet2”).activate ‘ sheet2シートがアクティブになります。
with sheets(“sheet1”)
‘
ですが、連番は、sheet1に入力されます。
for i=1 to 20
.range(“a” & i)=I
‘
「.」は、 sheets(“sheet1”) .range(“a” & i)の省略
next
end with
‘
with~end Withはworkbooks、worksheets、range
‘
などの省略する場合に使用すると便利です。
end sub
●簡単な練習(マクロを作ってみましょう。)
(4) (3)のコードでは、処理が終了しても
分かりません。処理が終了したら、メッセー
ジを表示するように、変更してみます。
sub tesuto2()
Dim i as long
sheets(“sheet2”).activate
with sheets(“sheet1”)
for i=1 to 20
.range(“a” & i)=I
next
end with
msgbox “処理が終了しました。” , , "end"
end sub
(5)処理前に、メッセージを表示し、処理確
認を行うようにしてみます。
sub tesuto2()
Dim i as long
Dim atai as variant
atai=msgbox(“開始します
か”,vbokcancel,”開始”)
if atai=vbcancel then exit sub
sheets(“sheet2”).activate
with sheets(“sheet1”)
for i=1 to 20
.range(“a” & i)=I
next
end with
msgbox “処理が終了しました。” , , "end"
end sub