VisualBasic入門  2005.5 修正

 これは,授業で説明する内容を簡潔に示したものです。エクセルで実際に実行してください。

 エクセルでの繰り返し作業をプログラムする方法をここでは示したい。MicrosoftのエクセルやワードにはVisual Basic for Application(VBAと略する)というマクロ言語macro assembly languageが組み込まれている。これをほんのちょっと知るだけで,あくびが出そうな単純作業を軽減することができる。VBAに関する書籍は多く出版されているので自ら学ぶことができる。私の使用OSはMacOS9なのだが,WindowsがXPになってからOffice for WinがMacのVBAプログラムを読み込むことができなくなった。そのため,エクセルファイルをダウンロードして頂いて使って頂くことができなくなった。このページでのプログラムの記述をコピーアンドペースト(テキストのみ)してもらうことで当然ながらXPでも使うことができる。
 VBAによるマクロの作成法には,エクセルでのマウスやキーによる入力を記憶させる方法(マクロ記録)と,言語で記述する方法がある。

1. マクロの記録

 マクロ記録の過程を次に示す。 メインメニュー/表示/ツールバー/Visual Basicを選ぶと,VBAのツールバーが表示される。左から2番目の●が記録ボタンで,これをクリックする。そうすると追加的にツールバーが表示される(表示されない場合はVBAツールバー右端の↓キーを押してユーザー設定/記録終了を選ぶ)。この左の■は記録終了ボタンである。必要な作業過程をマウスやキーによる入力で実施し終了したら■を押す。これで記録されている。新しいデータで,Visual Basicツールバーの左端の▼キーを押すと実行される。ただ,これだけだ。
 なお,マウスやキーによる入力はVBAではイベントという。そしてそれによる実際の処理をイベントプロシージャevent procedureという。マクロの名前はかならず文字から始める必要がある。マクロ名には日本語と_(アンダースコア)も使える。
 このマクロ作成過程では他のアプリケーションを使用したり,OSのファインダー操作をしても記録されない。エクセルで確定した作業だけが記録されるので,マクロ記録中の使用者の自由度は高い。
 なお,相対参照の場合は,記録開始とともに現れる記録終了ボタンの右横の相対参照ボタンをクリックすること。そして記録終了の前に再度相対参照ボタンをクリックして,記録終了ボタンを押すこと。相対参照にすると,実行する場所が選ばれているセルから始まる。言い換えるとどのスプレッドシートやセルでもいいことになる。

2. 言語で記述

 2.1. はじめに 直接,マクロを記録する方法には機能などの点で限界がある。そこでプログラム言語で記述することになる。 Visual Basic Editorを使うには,メインメニューのツール/マクロ/Visual Basic Editor(またはマクロのツールバーの右から二番目のノート様アイコン)を選べばよい。
 全く新しいメインメニューとツールバーが表示されて,三つのウィンドウが現れるのが通常。現れなくても問題はない。現れない場合は,このEditorのメニューの左端のエクセルアイコンの右隣のアイコン右の↓をクリックしてメニューを出し,新しい「標準モジュールCommonModuleコードウィンドウ(マクロのプロシージャをここで作成する)」のウィンドウを開く。
 左端上のウィンドウProjectが全体を管理する。このウィンドウの最上部に三つのボタン(コード表示,オブジェクト表示,フォルダーの切替)がついている。左端下のウィンドウはプロパティウィンドウPropertyでプログラムを記述する際に利用することができる。右側には三つのウィンドウが開いている。ユーザーフォームデザインDesignerウィンドウ,ユーザーフォームコードfrmResourceウィンドウ,標準モジュールCommonModuleコードウィンドウ(マクロのプロシージャをここで作成する)である。メインメニューで表示/オブジェクトブラウザを選ぶとオブジェクトブラウザが表示される。これにはマクロ記述の際に使えるメソッドやプロパティを調べることができる。
 メインメニューで表示/イミディエイトで,イミディエイトのウィンドウを開いてマクロを記述しても実行できる。ここではプログラムの一部を実行することができ,メインプログラムにコピーできる。


 
2.2. Visual Basic Editorの使い方  これを選ぶと,前述のように左上にプロジェクトウィンドウが表示される。ツリー構造の最下部にモジュールというフォルダーがあり,記録した順番でマクロがModule1,Module2というように表示されている。見たいマクロの対応モジュールを選んでダブルクリックすると,右のウィンドウに表示される。
 プログラミング(マクロまたはプロシージャの記述)は,エクセル上のオブジェクトを操作することだ。オブジェクトにはプロパティつまり特性がある。操作する方法はメソッドと呼ばれる。オブジェクトの例:ワークシートworksheet,セル範囲rangeなど。プロパティの例:セルの値Value,セルの列番号Column。メソッドの例:Copyメソッド(セルの値を指定した場所にコピー)。実は値を操作する命令文にはもう一つある。メソッドとは異なり,オブジェクトとは直接関係はないもので,関数と呼ぶ。その例の一つにMsgBox(後述)がある。
 わからない単語があればその単語を選んでHelpキー(無い場合はメインメニューのヘルプから検索)を押せばいい。
1. まずはエクセルのボタンの右隣のモジュールアイコンの右の下矢印をクリックし,標準モジュールを選ぶ。そうすると,新しいモジュール(コードウィンドウ)が作成される。
2. このウィンドウに,sub プロシージャ名,を入力して,リターンキーを押すと,次のようになる。
 sub プロシージャ名()
 |(カーソルがここでブリンク)
 End sub
 この間にプログラムを入力すればいい。
 具体例として,月名のオートフィルーー

 1 sub 月名入力()
 2 (Let) Range("A1")="1月"
 3 (Let) Range("A2")="2月"
 4 Range("A1:A2").Select
 5 Selection.AutoFill Destination:=Range("A1:A12")
 6 End sub
 (左端の数字は説明のための行番号)

そして,エクセルに戻って(左上のツールバー左端のエクセルアイコンをクリック),実行する。入力ミスでエラーが出たら,プロジェクトウィンドウでModule1を選び,ダブルクリックする。エラー部分が黄色く表示される。修正の後,保存して,エクセルに戻って再度,実行する。これを繰り返す。
 マニュアルで,オートフィルを実行するには,二つの連続する数値または文字を選んで,オートフィルコマンドを実行すればいい。その過程が上のプログラムで代用されるのである。上のプログラムで,(Letは通常省略される)Range("A1")="1月",では,右辺の値を左辺に入力するという意味である。文字列はすべて""で囲む。左辺のA1というセルがオブジェクトに対応する。第4行で,二つのセルを選ぶことになる。.Selectは,メソッドの一つである。第5行でオートフィルするのだが,SelectionはプロパティでメソッドはAutoFillになる。出力先の指定を,Destinationで実施するのだが,Destination:=,はRange以下の引数なのだが,:=,は名前付き引数,に使われる。

 さて,ここで問題。日,月を行方向に入力し,オートフィルで土曜日まで入力するマクロを作成しなさい。

 2.2' デバッグの方法(2006.11.22追加) マクロの実行時には,多くのエラーが発生する可能性がある。その対処法を以下に簡潔に示す。
 1. 「実行時エラー」というエラーメッセージが出たら,「デバッグ」のボタンをクリックする。 マクロのリストが現れて,問題の可能性があるところが,黄色く反転される。ツールバーのエディターアイコンの隣の■ボタンをクリックする。つまり,マクロの実行を中止する。その上で,エラーの原因となっているところを,修正する。その上で,再度マクロを呼び出して実行する。
  2. 「コンパイラーエラー」が表示されたら,エラーメッセージの内容を確認してOKボタンをクリック。
エディタの画面に戻ったら,ツールバーのリセットボタンを左クリックして修正する。
  3. デバッグを細かくしたい場合は, 表示/ツールバー/デバッグを選ぶと,ツールバーにツールが現れる。ステップイン,ステップオーバー,ステップアウト,ブレークポイントの設定や解除ができる。

 2.3. Visual Basicのデータ型Type 文字列型 (String,サイズ0〜65,535字),整数型(Integer,サイズ2 bytes,-32,768〜32,767),長整数型(Long,サイズ4 bytes,-2,147,483,648〜2,147,483,647),単精度浮動小数点型(Single,4 byte,負-3.402823E38〜-1.401298E-45,正1.401298E-453.402823E38),倍精度浮動小数点型(Double,サイズ8 bytes,負-1.79769312486232E308〜-4.9406564584124E-324,正4.9406564584124E-3241.79769312486232E308),日付型(Date,サイズ8 bytes,西暦100年1月1日〜9999年12月31日),通貨型(Currency),バイト型(Byte),バリアント型(Variant,サイズ可変),ブール型(Boolean,サイズ 2 bytes,True, False),オブジェクト型(Object,サイズ4 bytes),配列型(Array,サイズは設定による)。変数をプログラム内で使用するには,Dimステートメントを使用する。

 Dim 変数名 As 型
 例 Dim myAge As Integer
   Dim birthday As Date, age As Integer, tall As Single
など。
 String型は可変長である。文字の長さに応じて自動的に記憶領域を調節する。Asの無い定義文では,かならずバリアント型になる。これは数値データ(小数点,整数など)としても文字データとしても扱える。
 暗黙の宣言,といって,Dimで定義しなくても変数を使うことは可能だが,ミスを避けるためには暗黙の宣言を排除したほうがいい。プロシージャの定義が記述されているより前(宣言セクション)に,
 Option Explicit
という宣言をする。

 グローバル変数とローカル変数がある。変数の定義は基本的にはそのモジュール内のそのプロシージャだけで有効。これがローカル変数。プロシージャの外部で始めに変数宣言をすると,プロシージャを超えて使うことができる。これがグローバル変数である。下記のように。

 Dim gLength As Double
 
 Sub myProc()
 ---
 End Sub
 Function myFunc()
 ---
 End Function
など。

 2.4. 基礎的なプロシージャ例 
セル内の数値の読み込み例:  
2.4.1. セルB2, C2, D2内の数値を読み込み,合計をセルE2に入れるプログラムは次のようになる。

 1 Sub Gokei()
 2 Dim Data(3) As Integer
 3 Data (1)=Cells(2,2)
 4 Data (2)=Cells(2,3)
 5 Data (3)=Cells(2,4)
 6 Cells(2,5)=Data(1)+Data(2)+Data(3)
 7 End Sub

第3〜5行で,Cells(行番号,列番号)の値を,配列Data()に代入している。

2.4.2. データが多くなると,上のように,個々に入れるのは不可能になる。わかりやすくするために次のように4行3列マトリックスデータの読み込みで考える。

   B列   C列  D列  E列
2行  10    15   20  小計
3行  25    30   35  小計
4行  40    45   50  小計
5行  55    60   65  小計
6行             総和

 このような場合,For〜Next文を使う。第2行の小計を求めるのに,
For K=1 To 3
 Data (1, K) = Cells(2,K+1)
Next K
と書く。Cellsの()内の行番号は常に2だ。この部分をLとして,For L=1 To 4〜Next Lで上のFor K=1 To 3〜Next Kを入れ子にすれば,E列の小計4個をすべて求めることができる。ただし,Data(1,K)はデータ配列の第1行だけを示しているから,Data (L,K)に変更する必要がある。なお,エクセルの表に対応するのは,Cells(L,K)であるが,DATA(L,K)はエクセルの表とは関係なく,メモリー内の枠である。混同するので注意すること。

2.4.3. 総和を求めるプログラムを考える。
 全体の構造を次に示す。一部を隠す。

 Sub 総和()
 Dim ステートメント
 'データの読み込み
 For〜Next文入れ子
 '各行の総和を求める。
  For l = 1 To 4
    sum(l) = data(l, 1) + data(l, 2) + data(l, 3)
  Next l
 '計算結果の表示
 For l = 1 To 4
    Cells(l + 1, 5) = sum(l)
 Next l
 '各行の小計を合わせて全体の合計を求める
 For〜Next文
 Cells(6, 4) = "総計:"
 End Sub

 それでは,
 隠された部分を完成させなさい,そして
 上の式で得られた総和から平均値を求め,表に出力する文を追加しなさい。

2.4.4. 文字検索例 好きなものがあるかどうかを調べるプロシージャを作成する。

   A列    B列   
1行 入力位置
2行       ミカン  
3行       リンゴ  
4行       ナシ    
5行       ブドウ  
6行       レモン  
7行
8行      出力位置

 データはB2:B6にあり,欲しい果物をA1に入力し,あれば,B8に入力されたものが出力される,というプロシージャを作成する。

配列宣言とデータの読み込み:
Dim Moji(5) As String
For K=1 To 5
 Moji(K)=Cells(K+1,2)
Next K

上のデータ表のセルA1に欲しい果物をカタカナで入力して,あれば,セルB8にその名前が出るというようにするには,
IF〜Then...条件文を使う。セルA1に「リンゴ」が入力されたらセルB8に「リンゴ」を出力するための,まずは条件文は次のようである。

For K=1 To 5
 If Moji(K)="リンゴ" Then SUKI="リンゴ"
Next K

これではリンゴだけしか当てはまらないから,5個の果物に拡大する。

Sub 文字検索()
Dim Moji(5), SUKI, HOSHII As String
Dim K as Integer
'セルA1での既手入力値を読み込む
HOSHII=Cells(1,1)
'B2:B6のデータ系列を読み込む
For K=1 To 5
 Moji(K)=Cells(K+1,2)
Next K
' セルA1での既手入力値がB2:B6のデータ系列にあればSUKIという変数にその値を入力
For K=1 To 5
 If Moji(K)=HOSHII Then SUKI=HOSHII
Next K
'SUKIという変数値をB8に出力
Cells(8,2)=SUKI
End Sub

 これではあまりに不便なので,少なくとも「探して欲しい果物は?」というInput文,あれば「あります」というメッセージ文が欲しい。
 すぐ上のプログラムを参考に下の隠れた部分を補いなさい。
Sub 文字検索()
'
Dim Moji(5), SUKI, HOSHII As String
Dim K As Integer
'データの読み込み
HOSHII=InputBox("探して欲しい果物は?")
For〜Next文
'検索
For〜Next文
If Suki=HOSHII Then
MsgBox(HOSHII & "はあります")
Else
MsgBox(HOSHII & "はありません")
End if
'
End sub

2.4.5. 選択したデータ範囲を処理対象とするためには
 後に記述します。

なお,あるブックで作成したマクロを別のブックにコピーする方法と次にしめす:
 極めて簡単。コピーしたいモジュールがあるブックで,VisualBasicEditorを開いて,Moduleの中味をコピーして,ブックを閉じる。そして,コピーしたいブックを開いて,VisualBasicEditorを開いて,挿入/標準モジュール,この白紙のモジュールに,さきほどコピーしたものをペーストするだけ。

参考文献
 VBテックラボ&瀬戸遥著, 1998. 10日でおぼえるExcel98 VBA入門教室 Macintosh版. Shoeisha.
 新居雅行, 1994. Visual Basic for Excel 5.0. 日経BP出版センター.
 杉山和雄・井上勝雄編,1997. Excelによる調査分析入門 企画デザインのためのツール集. 海文堂.
参考 KyozaiContents/63.htm