プロシージャで処理を行うとき、変数を使うことは日常茶飯事です。
しかし、まとまりのあるデータを扱う場合は、配列変数を使うとマクロが組みやすくなります。
まず、下のプロシージャを比較してみましょう。
上のプロシージャでは、教科名を普通の変数に入れていますが、下のプロシージャでは、配列変数に入れています。
配列変数のいいところは、呼び出したり、書き込んだりするときに、数値で指定できることです。
このことによって、いろいろなデータを読み出しやすくなります。
Sub test01() Dim kokugo As String ←@ Dim syakai As String Dim sansuu As String Dim rika As String kokugo = "国語" ←A syakai = "社会" sansuu = "算数" rika = "理科" MsgBox kokugo ←B MsgBox syakai MsgBox sansuu MsgBox rika End Sub |
@ 必要な数だけ変数を宣言しています。(データ型は文字列型です) A 変数1つ1つに代入しています。 B 変数の内容を表示するのに、1つ1つ指定しています。 |
|
Sub test02() Dim 教科(3) As String ←@ Dim n As Integer 教科(0) = "国語" ←A 教科(1) = "社会" 教科(2) = "算数" 教科(3) = "理科" For n = 0 To 3 MsgBox 教科(n) ←B Next n End Sub |
@ 教科(3)と、配列変数で宣言しました。 配列のインデックス番号は、通常0から始まるので、この場合は 教科(0),教科(1),教科(2),教科(3)の4つの変数を宣言したことになります。 A 個別に代入していきます。 B 先ほどのマクロと違って、For〜Next文を使い、すっきりとまとまりました。 nの値が、0から3まで順に変わっていくので、4つの変数の内容を順番に表示します。 |
|
配列変数は、教科(2,3)というようにも宣言できます。この場合に使える変数は、 教科(0,0) 教科(0,1) 教科(0,2) 教科(0,3) 教科(1,0) 教科(1,1) 教科(1,2) 教科(1,3) 教科(2,0) 教科(2,1) 教科(2,2) 教科(2,3) の12個になります。これを2次元配列変数といいますが、同じように教科(2,3,4)というように3次元以上の配列を作ることも可能です。 しかし、現実的には、1次元配列か、2次元配列までをよく使います。 |
配列変数のインデックス番号は、任意の範囲で設定することができます。
Sub test03() Dim 教科(6 To 9) As String ←@ Dim n As Integer 教科(6) = "国語" ←A 教科(7) = "社会" 教科(8) = "算数" 教科(9) = "理科" For n = 6 To 9 ←B MsgBox 教科(n) Next n End Sub |
@ 今度は、教科()の中を、6 To 9 としました。 これで、作られた配列は、 教科(6)、教科(7)、教科(8)、教科(9) の 4つになります。 A もちろん、代入するときの範囲も6〜9です。 B 読み出すときも、6〜9になります。 |
|
左のようなワークシートから、 データを配列変数に読み込むときには、 左下のように行います。 左下のマクロでは、1列目の教科名を配列に読み込んでいます。 |
||
Sub test04() Dim 教科(1 To 8) As String ←@ Dim R As Integer For R = 1 To 8 教科(R) = Cells(R, 1) ←A Next R For R = 1 To 8 MsgBox 教科(R) Next R End Sub |
@ 1行目から8行目までのデータを読み込むので、 それに合わせて、1 To 8とします。 A セルの1列目のデータを1行目から順番に配列変数に格納します。 |
|
要素番号の変更は、2次元以上の配列でも、使えます。 例えば、教科(1 To 3 , 1 To 3 , 2 To 5)のようなこともできます。 |
1で扱った配列は,あらかじめ決まった大きさ(個数)の宣言するので,静的配列といいます。
しかし,場合によっては,データ数がわからず,データ数を調べてから宣言したいときもあります。
こういうときは,大きさは宣言せず,変数の型だけを宣言します。そして,あとから大きさを決めます。
この配列を,動的配列と言います。
Sub test05() Dim 教科() As String ←@ Dim n As Integer ReDim 教科(3) ←A 教科(0) = "国語" ←B 教科(1) = "社会" 教科(2) = "算数" 教科(3) = "理科" ReDim Preserve 教科(7) ←C 教科(4) = "音楽" ←D 教科(5) = "図工" 教科(6) = "家庭科" 教科(7) = "体育" For n = 0 To 7 MsgBox 教科(n) Next n ReDim Preserve 教科(4) ←E For n = 0 To 4 MsgBox 教科(n) Next n End Sub |
@ 動的配列変数を宣言するときは、インデックス番号を省略します。 A データの数が決まったら、ReDimステートメントで再宣言します。 データ型は変更できないので、文字列型のままです。 B データを代入します。 C データを保持したまま、配列を拡張したいときは、ReDimのあとに Preserve を付けて再宣言します。 D 4〜7の変数に、新たにデータを入れます。0〜3までのデータは保持されています。 E 同じように、ReDim Presarve で、先ほどの要素数より少ない数で宣言したときは、 はみ出した部分のデータは切り捨てられ、残りのデータは保持されます。 Preserveを使わないで、ReDimだけで宣言すると、配列の内容は初期化されてしまいます。 |
ここで、気をつけなければいけないのは、教科(2,3)のような2次元以上の配列を宣言したときです。 ReDimで変更できるのは、3のように一番末尾の部分だけです。それ以外の場所はできません。 |
|
※ 静的配列と動的配列では,「変数が使えるか?」でも違いが出てきます。 | |
Sub 静的配列() Dim n As Integer n = 3 Dim 配列(n) As Integer ←@ End Sub |
左の例は,@のところで変数を使って宣言をしようとしています。 実行すると,「定数式が必要です。」とエラーメッセージが出てしまいます。 定数式の意味ですが,nを2*3のようにすると正常に実行されることから, 完全に固定された数値ならいいようです。 また,下のようにユーザーが定義した定数も使えるようです。 Const n As Integer = 3 Dim 配列(n) As Integer |
Sub 動的配列() Dim n As Integer Dim 配列() As Integer n = 3 ReDim 配列(n) ←A End Sub |
Aで変数を使っていますが,動的配列なので正常に実行されます。 実際は,データの個数を調べて,その個数で宣言することが多いでしょうね。 |
配列変数にデータを格納するとき、今までは1つ1つ代入してきましたが、少ない個数であれば一気に格納する方法があります。
それが、Array関数を使う方法です。
Sub test06() Dim 教科 As Variant ←@ Dim n As Integer 教科 = Array("国語", "社会", "算数", "理科", "音楽", _ "図工", "家庭科", "体育") ←A MsgBox LBound(教科) & "〜" & UBound(教科) ←B For n = LBound(教科) To UBound(教科) ←C MsgBox 教科(n) Next n End Sub |
@ Array関数を使って配列に格納する時は、必ず、データ型をVariant型にします。 A 今回のデータは文字列なので、”(ダブルクォーテーション)で囲み、1つ1つのデータの区切りは,(カンマ)で区切ります。数値の場合は、”(ダブルクォーテーション)はいりません。 この1行で、変数「教科」に配列として、8つの教科名が入りました。 B 参考までに、インデックス番号がいくつから(下限)、いくつまで(上限)になっているか表示させています。今回の場合、0〜7になっています。 下限 LBound(教科) 上限 UBound(教科) C For〜Next文で、教科(0)〜教科(7)を表示させています。 |
Sub test07() Dim 教科 As Variant Dim tmp As Variant ←@ 教科 = Array("国語", "社会", "算数", "理科", "音楽", _ "図工", "家庭科", "体育") For Each tmp In 教科 ←A MsgBox tmp Next tmp End Sub |
@ For Eachを使いたいので、バリアント型で宣言します。 A ここから、For Each文を使っています。 変数の内容を取り出すだけなら、こういうやり方もできます。 |
同じように、ワークシートにあるデータも一気に配列に格納する方法があります。
やりかたは、Array関数とよく似ています。
この方法は、読み込みも書き込みも速いので、高速化には欠かせません。
Sub test08() Dim 文字 As Variant ←@ Dim R As Integer Dim C As Integer 文字 = Range("a1:d8") ←A For C = 1 To 4 For R = 1 To 8 文字(R, C) = "★" & 文字(R, C) ←B Next R Next C Range("a1:d8") = 文字 ←C End Sub |
@ 一気に変数に格納するので、Variant型で宣言します。 A ここで、範囲内のデータを、一気に2次元配列として格納します。 文字(R,C)のRは行、Cは列になります。 つまり、Cells(R,C)と同じように扱うことができます。 B 配列に入っているデータの前に、★を付けます。 C ここで、一気にセルに書き込みます。 |
ワークシートから配列変数に一気に格納する場合は、開始値は1からになります。 また、どんな場合も2次元配列になります。 例えば、 A1〜A10 までを格納した場合は、A( 1 to 10 , 1 )と同じですし、 A1〜G1 までを格納した場合は、 A( 1 , 1 to 7 )と同じです。 ここで,インデックス番号の上限と下限を求めるには,次のようにします。 下限 LBound( 文字 , 1 ) 上限 UBound( 文字 , 1 ) ・・・行方向の上限と下限が求まります。 下限 LBound( 文字 , 2 ) 上限 UBound( 文字 , 2 ) ・・・列方向の上限と下限が求まります。 |
配列変数は、通常、0から始まります。しかし、セルの行や列は1から始まるので、時にはこれと一致させておくと便利です。
そして、要素数とインデックス番号が一致するので、わかりやすいのも利点でしょう。
設定するのは、「Option Base」ステートメントを使います。
このステートメントは、モジュールの先頭で使います。
また、これを設定すると、このモジュール内の配列変数全てに適用されます。
(Option Explicit は、変数の宣言を強制するステートメント) こちらは、通常通りなので、下限が0になっています。 要素数は、 配列(0)、配列(1)、配列(2)、配列(3) の4つです。 |
(Option Explicit は、変数の宣言を強制するステートメント) こちらは、「Option Base」ステートメントを使っているので、 下限が1になっています。 要素数は、 配列(1)、配列(2)、配列(3) の3つです。 |
Option Base ステートメントでは、0か1を設定します。デフォルト状態が0なので、使うときは1でしょうね。 ちなみに、上限を調べるときは、UBound を使います。 |
Sub test09() Dim 教科(7) As String ←@ Dim 項目 As String 教科(0) = "国語" ←A 教科(1) = "社会" 教科(2) = "算数" 教科(3) = "理科" 教科(4) = "音楽" 教科(5) = "図工" 教科(6) = "家庭科" 教科(7) = "体育" 項目 = Join(教科, ",") ←B MsgBox 項目 End Sub |
@ 配列変数を文字列型で宣言します。 A 配列にデータを代入します。 B Jion関数で、配列内のデータをカンマでつないで、1つのデータにします。 |
Sub test10() Dim 項目 As String Dim 教科 As Variant ←@ Dim tmp As Variant 項目 = "国語,社会,算数,理科 ,音楽,図工,家庭科,体育" ←A 教科 = Split(項目, ",") ←B For Each tmp In 教科 ←C MsgBox tmp Next tmp End Sub |
@ 配列変数として使いたいので、バリアント型で宣言します。 A 項目という変数に、文字列を代入します。,(カンマ)が使われてはいますが、”(ダブルクォーテーション)で両端がくくられているので、このカンマもデータの一部です。 B Split関数で、,(カンマ)でデータを分割して、配列変数に代入します。 C ここで確認すると、ちゃんと配列になっていることがわかります。 |
Join関数とSplit関数は、CSVファイルの読み書きをするときに知っていると便利です。 書き込み・・・セルのデータを配列に読み込み、Join関数でつないで1行分のデータを作り、CSVファイルとして書き込みます。 読み込み・・・1行分のデータを読み込み、Split関数で配列に分割し、各セルに読み込みます。 2010では、CSVへの書き込みは正常にできますが、CSVを読み込むときにこちらの意図しない形に加工されてしまいます。 例) 01が1になったり、1E13が指数と認識されたり。 そこで、Line Input を使って読み込み、 Split関数を使って1行分のデータを分割し、セルに代入するようにすると、そのまま加工されずに値が入ります。 このとき、「セルの書式設定」は、表示形式を「文字列」にしておいてください。 そうしないと、数値と見なして、数値に変換してしまうので、元の木阿弥です。 |
|
Join関数を扱う時には,注意することがいくつかあります。 @1次元配列なので,Variant型の変数にセルの値を一気に格納した配列では,使えません。 AJoin関数が扱える配列変数は,文字列型かVariant型だけです。それ以外の変数では,エラーになります。 |
マクロを組んでいて処理が複雑になってくると、思ったような結果が得られないことが多々あります。その原因を探るために、変数の内容がわかると非常に便利です。MsgBoxを使って、処理ごとに表示させる方法もありますが、ローカルウィンドウを使うと全部の変数の内容を見ることができます。
デフォルトでは、マクロを書き込む画面の下に、イミディエイトウインドウとローカルウィンドウが表示されています。 ローカルウィンドウは、そのときの変数の内容を表示します。 このときに注意しなければいけないのは、処理が終わってしまうと変数も初期化されてしまうので、途中でプロシージャを止める必要があるということです。 左の例では、プロシージャの最後に、Stopメソッドを入れています。 これで、実行すると、Stopのところでプロシージャが一時的に停止します。 すると、全ての変数の内容を見ることができます。 処理の続きを行うときは、マクロの再生ボタン『Sub/ユーザーフォームの実行』を押します。そのまま終了するときは、停止ボタン『リセット』を押します。 |
|
プロシージャを止める方法として、ブレークポイントを設定する方法もあります。 左の図で●(茶色)と表示されている部分がありますが、これはマウスでこの場所をクリックするとなります。 つまり、この場所でプロシージャを止めることができるのです。 解除するときは、●をもう一度クリックすると、消えます。 ブレークポイントを設定して、プロシージャを動作させたときは、停止ボタンを押してからでないと、解除できませんので気をつけてください。 |
今まで配列変数に代入することばかり書いてきましたが,実際には,ループ処理の中で,「代入して処理して初期化して,代入して処理して初期化して,・・・」と,同じ変数を使うことも出てきます。
配列変数の初期化は,次のようにします。
Sub test11() Dim 数値(4) As Integer Dim n As Integer For n = 0 To 4 数値(n) = n ←@ Next n For n = 0 To 4 MsgBox 数値(n) ←A Next n Erase 数値 ←B For n = 0 To 4 MsgBox 数値(n) ←C Next n End Sub |
@ここで,配列変数「数値」に値を代入します。 以下のように,データが格納されました。 数値(0) = 0 数値(1) = 1 数値(2) = 2 数値(3) = 3 数値(4) = 4 Aここで,入っているデータを表示させて,確認してみます。 Bここで,配列変数の初期化をしています。 C内容を確認すると,全部0(初期の値)になっています。 |