セル範囲を配列に一括代入する

配列にセルの入力内容を一括で代入させるコード

'セルデータの一括取得
Dim セルデータ As Variant
セルデータ = Range("A1:C3")

Excel VBAでセル範囲を配列に一括代入する方法を紹介します。
セルを1つずつループして読み込むよりも高速に処理できるため、大量データの集計や検索に役立ちます。
この記事では、Range.Valueを使った基本的な方法と注意点を解説します。

Variant型の変数にRangeオブジェクトを代入すると、そのセル範囲のデータが2次元配列として一括で格納されます

このとき「Rangeオブジェクトそのもの」が代入されるように感じるかもしれません。
しかし、実際にはLetステートメントが省略されており、省略しない場合は次のように記述します。

Let セルデータ = Range("A1:C3")

このLetステートメントは「値の代入」として認識されるため、セル範囲を指定してもRangeオブジェクトそのものが代入されることはありません。

Rangeオブジェクトを代入する場合には、Setステートメントを使用します。

話を戻すと、このように一括代入を行うことで、セル範囲のデータが2次元配列としてそのまま格納されます。

取得するセル範囲のデータ
取得をおこなうセル範囲

この表のセル範囲のデータを一括で配列に代入します。

取得した配列データ
代入された2次元配列のデータ

こちらは、実際に2次元配列として取得されたデータです。

この配列をそのまま他のセル範囲へ一括出力することも可能です。
ただし、その場合はCopyメソッドを使った方が分かりやすいため、あまり意味はありませんが実現できます。

ここで取得したデータを加工しながら別の2次元配列に格納し、最終的にその加工済み配列をセル範囲へ一括出力することもできます。

この方法を用いることで、セル上で直接加工を行わない分、処理の高速化が図れ、元のセルデータを改変してしまう心配もありません。

なお、この方法で配列へ代入すると、1列や1行のみの範囲指定であっても必ず2次元配列になります。
これはワークシートの仕様が2次元であるためです。もし1次元配列として扱いたい場合は、以下の記事をご参照ください。

VBA配列の注意点:添字と型の扱い

  • 配列の添字は必ず1から始まる
  • セル範囲を代入すると必ず2次元配列になる
  • Variant型でしか代入できない

通常の配列は0から始まりますが、セル範囲を配列に一括代入すると1から始まります。
例えば3行3列の範囲を代入すると「1 to 3, 1 to 3」と定義されます。

どうしても0から始めたい場合は、新たに要素数を定義した動的配列へ代入し直す必要がありますが、その処理は煩雑になるため、要素数を合わせて扱う方が実用的です。

また、セルには数値や文字列など様々な型のデータが格納されるため、Variant型で宣言する必要があります。
この配列代入はVariant型でしか実行できません。
型を限定したい場合は、配列を加工して特定の型を指定した配列へ代入することも可能ですが、実用上の利点はほとんどありません。

この記事のまとめ

  • RangeをVariant型に代入するとセル範囲を一括で配列に格納できる
  • 配列の添字は1から始まるため注意
  • セル範囲を代入すると必ず2次元配列になる
  • Variant型でしか実行できない

セル範囲を配列に一括代入することで、ループ処理よりも高速にデータを扱えます。
大量データの集計や検索に役立つため、VBAで効率的な処理を行いたい場合に覚えておくと便利です。

シートの最終セルを選択

SpecialCells(xlCellTypeLastCell)メソッドを使用すると、シートの最後のセルを取得できます。注意点があります

'シートの最終セルを選択する
Cells.SpecialCells(xlCellTypeLastCell).Select

シートの使用されている最終セルを取得するには「SpecialCells」メソッドに引数「xlCellTypeLastCell」を指定します

シートの最終セルとは、なんらかの設定の行われた最右下のセルです
入力値だけでなく書式設定などでも対象となります

ただ、使用時の注意点がありますので後述します

コード実行時の動き

シートの使用済み最終セルを選択状態にする
コード実行時の動き
Cells.SpecialCells(xlCellTypeLastCell).Select

コードを実行すると画像の様に書式設定されている最後のセルが選択状態になります

これを使用すれば、データベースの表に入力する際に最終行を調べるのが非常に簡単に行えます

Endメソッドによる移動は空白セルに影響をうける可能性がありますが、こちらは確実に最終行を取得することができますし、1行ステートメントなので処理も速いです

メソッドの注意点(未保存)

しかし、このメソッドは注意点がいくつかあります

まず、大きな注意点としてこのメソッドは最後に保存された状態の最後のセルを取得します

つまり、未保存の状態のセル範囲には対応できません

保存をしていないとうまく取得できない動き
未保存だとうまく取得されない
Cells.SpecialCells(xlCellTypeLastCell).Select
Range("C:D").Delete
Range("A1").Select
Cells.SpecialCells(xlCellTypeLastCell).Select

画像とコードを確認してください

まず、最初にメソッドを実行します
この時点では黄色セルの最終セルが選択状態になっています

その後に、C・D列を削除してA1セルをアクティブ状態に戻します
そして再度実行すると、今度は黄色セルではないセルが選択状態になっています

このセルは削除前に最後のセルとして認識されていたセルになります
削除を実行した後に保存が実行されていないため、最後のセルのデータも更新されていないため同じセルが選択されてしまっています

この動きがあるため、削除を実行するような処理の場合にメソッドを使用するには必ず先に保存を実行しておく必要があります

メソッドの注意点(書式設定)

次に、書式設定を行っただけで最後のセルの判定に含まれると解説しました
実際に黄色セルが選択されていることを確認しましたので間違いありません

しかし、ここでも注意しなければいけない動作をします

例えば、よくデータベースの表で列に入力するデータが固定のため
列をまるごと決まった書式設定を行うことがあります

その際に、列をまるごと選択して実行するか
範囲指定をして、予備範囲までを設定するかで変わります

端を含めるかで対象が変化する動き
書式設定での動きの変化

コードが少し長いので、画像で確認してください

まず、最初にA列全体に対して「,」の表示形式を設定しています
その状態で最後のセルを取得するとB列の最後の行が選択されます

これが想定していた動きになります

次に、A列を削除してB列のデータをA列に移動します
そして、今度はA列全体ではなくA1からA10000までのセルに対して同じ表示形式の設定を行います
この状態で最後のセルを選択すると、B10000が選択されます

未保存であるためB列になる理由は解説しました
そして、A列の書式設定を行った範囲に対して最後のセルが認識されてしまう動きになっています

これは想定通りの動きではありません

つまり、行全体のように端を含むような書式設定はその行の標準設定となるため
使用済みセルと認識されることがなくなる
わけです
列方向に関しても同様です

要は、セル全体に背景色を設定したとしてもセル全体が使用済みセルとして認識されるわけでは無い、ということです

列や行の選択を知らず、とりあえず途方もないセル範囲に予備の書式設定を行ってしまっているようなワークシートにはほんまに要注意です

こういった注意点を踏まえて考えると、データの取得のみを行う表データで最終セルを取得するには適していますが、加工を行う表で使用するには適していないかもしれません

その場合は、UsedRangeプロパティの方がいいかもしれません
それを使用した最終セルの選択方法は以下の記事にあります

選択範囲の最終セルを選択する

選択範囲の中での最終セルだけを選択状態にするコード、シートの最終セルを選択状態にするコードの解説含む

'選択中の最終セルを選択する
Selection.Cells(Selection.Cells.Count).Select

セル選択をした状態で、そのセル範囲の最後のセルのみを選択状態にするコードです

主に表を作成したりして、そのセル範囲の最後のセルを単一で指定したい場合に使用します

使用例

使用済みセル範囲の最終セルを選択状態にする
使用済み範囲の最終セルを選択
'使用済みの最終セルを選択する
ActiveSheet.UsedRange.Select
Selection.Cells(Selection.Cells.Count).Select

実際のコード使用時の動きの確認を行います
今回の作成コードはこんな感じのコードです

目的としては、保存に時間がかかるブックのため頻繁に保存を実行できないブックで、表を整えるたびに最終セルを取得していきたい場合です

SpecialCells(xlCellTypeLastCell)メソッドでも最終セルは取得できますが、上記の条件のように表の改変を行いつつ、保存を実行しない場合はうまく取得できません

ActiveSheet.UsedRange.Select

そこで、UsedRangeプロパティを使用することで保存に影響を受けずに最終セルの取得を行うことが出来ます

しかし、このプロパティでは範囲選択されてしまうため
実際どこが最終セルなのかが分かりません

Selection.Cells(Selection.Cells.Count).Select

そこで、記事コードを連続して使用することで最終セルのみを選択状態にできます

このコード自体は非常にシンプルな内容です

「Selection.Cells」という部分で、選択中のセル範囲のセル全体を表しています
そこに「()」でセルを相対的に指定することが出来ます

行列番号を指定する事以外に、セルのインデックス番号を使用することもできます
今回はこのインデックス番号で処理を行います

そして、インデックス番号は「Selection.Cells.Count」となっています
選択中のセル範囲のセル全体は上記のとおりです、そのセル全体の個数を取得しています
範囲選択のセル個数が100個あれば「100」が取得されます

このセルのインデックス番号は配列とは違い、1から始まるのでそのままの数値で最終セルを指定することが可能になります

Selection.Cells(1).Select

数値での指定を行えますので、このようにすれば選択範囲の1番目のセルを選択状態にできます

まあ、選択範囲が1つであればそこはActiveCellになるので、あえて指定することはあまりありませんが可能ではあります

シートの使用済みセルを範囲選択

UsedRangeプロパティは、シート内で使用済みのセル範囲を取得します

'使用済みのセルを範囲選択する
ActiveSheet.UsedRange.Select

シート内の使用済みセル範囲を選択するには「UsedRange」プロパティを使用します
これは、セル範囲のプロパティですがオブジェクトはシートになっていますので注意してください

コードの動きの解説

シート内の使用済みセル範囲を選択状態にする
使用済み範囲の選択
ActiveSheet.UsedRange.Select

例のコードを実行したときの動きです

コードを実行すると、B3からF8までが範囲選択されます

このプロパティは左上端と右下端の2点セルから範囲を取得します
その際、値が入力されているセルだけではなく、書式が設定されているだけでも対象となります

A列と1・2行目にデータは存在しないため、始点セルがB3になります
F8は黄色に背景色が設定されているだけですが、しっかり取得されています

イメージ的には、SpecialCells(xlCellTypeLastCell)メソッドに似ています
このメソッドはシート内の使用済みの最後のセル(右下端)を指定できるものです
このセルから始点は任意の箇所になりますが、同じように使用範囲を指定することができます

しかし、記事のプロパティでは保存の影響を受けません
保存を実行せずに最終セルを判定するには、こちらを使用することになります

ただ、範囲選択されてしまうので最終セルを保存しない状態で取得する場合は
この範囲の中から最後のセルを取得するような処理を作成する必要があります
その内容については以下の記事で解説を行っています

特定のセルを選択する

RowDifferences・ColumnDifferencesメソッドは、指定範囲の中で条件に指定したセル以外を選択するメソッドです

'アクティブセル以外のセルを選択状態にする(行方向)
Selection.RowDifferences(ActiveCell).Select
'アクティブセル以外のセルを選択状態にする(列方向)
Selection.ColumnDifferences(ActiveCell).Select

「RowDifferences」「ColumnDifferences」メソッドは、指定したセルと同一のセル以外を選択します

このメソッドは、指定範囲内に引数に指定するセルが存在していないとエラーになります

なので、基本的にはこのメソッドの引数にはActiveCellやCellsなどの相対的な指定方法で行う必要があります
また、引数に指定できるセルは単一のセルになるので、複数のセルを指定した場合もエラーになります

メソッドの動き

特定のセルを選択状態にする(列方向)
行方向への指定選択
Selection.RowDifferences(ActiveCell).Select

画像ではこのコードが実行されています
RowDifferencesメソッドは、行方向への操作時に使用します

画像の動きではActiveCellはA2になりますので、条件は「ステータスA」が指定される形になります

このメソッドは、指定したもの以外を選択状態にするので
元々の選択範囲から「ステータスA」を除いたセルが選択状態になります

空白も対象になるので、列全体を指定するワークシートの列全体を選択してしまいます

特定のセルを選択状態にする(行方向)
列方向への指定選択
Selection.ColumnDifferences(ActiveCell).Select

ColumnDifferencesメソッドは、列方向への操作時に使用します

画像の動きでは、ActiveCellはB2になっていますので、空白のセルが指定される形になります

なので、選択範囲の中で空白ではないセルが指定される形になっています

この画像の動きのように、行のなかで特定の列だけにデータが入力されている表でどの列にデータが入力されているかを調べるのに適しているメソッドです

ただ、指定範囲の中に条件のセルを含める必要があるのがすこし難点です
なので、この画像の例の表ではその為にあえて何も入力しない空白列(B列)が作成されています
こうすることで、常に空白を指定することが出来るようにはなります

しかし、この動きであればFindメソッドの方が柔軟に条件も指定出来て便利ではあります
ですが、Findメソッドは一度に条件にあてはまるセルを選択するような動きはできません
あくまでも1セルずつ処理を行っていきます

でもこのメソッドなら、条件さえ整えてあげれば一括で条件に当てはまるセルを一括選択できます
逆説なので、条件を整えるのがたいへんですが・・・

使いどころが難しいメソッドですが、機能としては便利な機能だと思います

フィルタ機能(全解除)

フィルタの条件を全解除するにはShowAllDataメソッドを使用します

'フィルタの条件設定をクリアする
ActiveSheet.ShowAllData
'フィルタの条件設定をクリアする
On Error Resume Next
ActiveSheet.ShowAllData
On Error GoTo 0

フィルタのどれかの列でソートがかかっている状態を一括で全解除するには「ShowAllData」メソッドを使用します

このメソッドは、フィルタがかかっていない状態で実行するとエラーが発生します
なので、処理の最初に初期化しておきたいような場合はエラーを無視して実行します

フィルターが設定されているかは調べればわかりますが、あまり意味は無いと思います

また、ここでいう解除とはフィルタ機能の解除ではありません
ソート条件の解除になりますので、注意してください

フィルタを処理中にかけなおす場合は、当然エラーにはなりませんので1つ目のエラー無視は入れずに実行するといいです

ユーザーがフィルタをかけて保存していると、当然処理がうまくいかない可能性がありますのでフィルタのあるシートを処理する場合は最初に入れておくと安心です

ちなみに、昇順降順の矢印の表示もクリアされますがその設定は解除されたりはしません

Worksheets(1).ShowAllData

またこのメソッドはシートに対して行いますので、このコードのようにワークシートを指定して、別のシートのフィルタ条件を解除することもできます

引数を渡してプロシージャを実行する

引数付きのプロシージャの作成方法と呼び出し方法について

'セルを引数に持つプロシージャ
Sub 呼出テスト(引数 As Range)

End Sub

プロシージャの呼び出しにはCallステートメントを使用します
この時に引数付きのプロシージャを実行する事ができます

その際には呼び出し先のプロシージャで引数の設定を行う必要があります

関数と同様にプロシージャ名の隣の「()」の中に指定を行います
通常引数の無い場合は、空白状態になっています

ここは引数を設定するための場所になります

引数は、変数と同じものを指定します
書き方も変数の宣言と同じものになります

実際の処理の動き

Sub 処理テスト()

 Call 呼出テスト(Range("A1"))

 Call 呼出テスト(Range("A1:A10"))

End Sub

Sub 呼出テスト(引数 As Range)

Debug.Print 引数.Address

End Sub

処理の動きの確認には以上のコードを使用します

引数付きの処理を呼び出したときの動き
引数付きの処理の呼び出し

画像の動きの確認をしてください

 Call 呼出テスト(Range("A1"))

 Call 呼出テスト(Range("A1:A10"))

Callステートメントで2回処理を呼び出しています
その際に、引数としてセルを指定しています

指定方法はプロシージャ名の後に「()」を指定して、中に引数に設定するものを記載します

Sub 呼出テスト(引数 As Range)

Debug.Print 引数.Address

End Sub

呼び出し先の処理では、引数は「(引数 As Range)」として指定されています

ここに呼び出し時に指定するセルを代入することができます
代入されたものは通常の変数と同じ使い方になるので、Addressプロパティでセルアドレスを取得することが出来ます

Callステートメントにより同様の処理をまとめることが出来ますが、こうして対象が違っていてまとめることが出来ない場合に使用できます

なお、この引数付きのプロシージャからデバッグモードに入ることはできません
なので動作確認を行いたい場合は、呼び出し元のプロシージャから実行することでデバッグモードを行うことが可能になります

また、ここではSubプロシージャで行っていますが、Functionプロシージャでも全く同様に使用できます

エラー処理(エラー内容をクリアする)

On Error GoTo -1ステートメントは、エラー情報をクリアすることが出来ます。再度エラー処理を適応することが可能になります

'エラー情報をクリアする
On Error GoTo -1

処理を移動させずに、エラー状態の解除のみを実行するには「On Error GoTo -1」ステートメントを使用します

このステートメントを使用する場面は、エラーが発生している状態で、さらにエラーが発生した場合にどうしたいかによります
つまりエラー処理中のエラーはどうするか、という点です

エラー処理中のエラー発生について

エラールーチン中にエラーの再発生したときの動き
エラー処理中のエラー発生時の動き

画像の動きを確認してください
エラーが発生した際、On Error GoToステートメントにより「エラー発生」のラベル行に処理が移動します

このとき、エラー情報は発生したものが保持された状態です
この状態をエラールーチンが行われている状態といいます

このエラールーチン中に、再度エラーが発生した場合はステートメントの影響に関係なく実行時エラーが発生して処理が停止します

エラー処理とはいえ、処理である以上その処理にエラーが発生する可能性があります
その際にユーザーに処理停止を対処させるのは難しい話である場合が多いと思います

そこで、エラー処理中にエラーが発生したら、エラー処理を行いたい
こういった状況が出てくることがあります
そんなときに使用するのが、このステートメントになります

ステートメント使用時のエラー処理

エラールーチン中にエラーを解除する動き
エラー情報をクリアした時の動き

On Error GoTo -1ステートメントを使用している場合の動きになります

エラー発生:
On Error GoTo 0
On Error Resume Next
'Err.Raise (1)
On Error GoTo -1
Err.Raise (1)
Debug.Print "エラー終了"

この処理が画像のエラー処理部分になります

On Error GoTo 0

まず、最初にOn Error GoTo エラー発生の設定を解除するためにこのコードを実行します
これをしていないと、後で発生するエラーでまた「エラー発生:」に戻ってきてしまい無限ループに入ってしまいます

これで、エラーが発生したらメッセージが出て処理が停止する通常の状態になっています

ただ、このステートメントはエラー発生時の制御を解除しただけであり
エラー情報をクリアしたわけではないので、エラールーチンは継続中です

On Error Resume Next
'Err.Raise (1)

次に、エラー処理中にエラーが発生した場合の対応を作成します

今回はエラー処理中に発生したエラーは無視するように設定しますので、On Error Resume Nextステートメントを使用します

2行目はエラー発生させるコードをコメントアウトしています

実際、ここでもまだエラールーチン中なのでエラーが発生した場合は実行時エラーが発生し処理が停止します

On Error GoTo -1

ここでようやく記事のステートメントを使用しています

この時点で、エラー情報がクリアされます
そして、その時点でエラールーチンも終了しています

これにより、上記で設定したエラー発生時の制御が有効な状態になります

Err.Raise (1)
Debug.Print "エラー終了"

画像の動きと合わせて確認してください

ここでエラーを発生させていますが、処理は停止せずに最後のエラー終了の文字列出力まで実行されています

エラー処理中のエラー対応について

通常エラーの解除にはResumeかResume Nextのどちらかのステートメントを使用しますが、このステートメントでは処理が戻ってしまいます

通常、処理の組み方としてはそれが正統な作り方だと思います
でも前述したように、エラー処理を作成するということはそこでもエラーが発生する可能性は十分にあり得ます

例えば、以下の記事の動きを確認してもらいたいのですが
エラー処理でLong型変数に加算していき使用できる整数値で処理が正常に戻ります

ただ、このエラー処理でも使用できる数値がLong型の範囲内になかった場合
加算され続けて、いずれオーバーフローして実行時エラーが発生します

処理上、ほぼあり得ませんが
エラーが出ない処理は無い、ということは事実として存在します

なので、このコードを実行することによってエラーの発生がクリアされるので再度エラーが発生した場合は改めてエラー処理を適応することが出来ます

ただ、エラーの情報がクリアされるので当然エラーの発生した行もどこにあるかが分からなくなります
なので、Resumeステートメントなどでエラー発生した行に戻ることはできなくなるので注意が必要です

エラー処理(エラー処理後に戻す)

Resume Next・Resumeステートメントを使用すると、エラー処理後に処理を戻すことが出来ます

'エラー発生の次行に戻す
Resume Next
'エラー発生行に戻す
Resume

エラーが発生して、On Error GoToステートメントで処理を移動させていた状態から元のエラー発生した場所に戻すには「Resume Next」「Resume」ステートメントを使用します

戻す場所によって、コードを使い分けます

エラーが発生した行の次行に戻すにはResume Nextステートメントを使用します
エラーが発生した行に戻すにはResumeステートメントを使用します

動きの確認

エラー処理後に次の行から再実行する動き
Resume Next

エラーが発生した次の行に処理を戻すコードです

次の行に処理が戻るので、もともとエラーが発生した処理は実行されません
つまり、そのエラーが発生した処理が以降の処理に影響を与えない場合にのみ使用できます

当然、以降の処理にこのエラーが発生した処理を前提としていた場合バグが発生します

例えば、フォルダの新規作成や、存在しなかったファイルの削除など
エラーが発生すること自体は処理に影響を与えない場合に利用できます

On Error Resume Nextステートメントでもエラーが無視できますが、エラー処理が行えません
ユーザーにエラーが発生していることはメッセージで伝える必要がある場合はこちらのコードでエラー処理を作成してコードは次の行から再開させます

エラー処理後エラー発生行から実行する動き
Resume

このコードはエラーの発生した行に戻します

エラーが発生した行に処理を戻すことになるので、このエラー原因を解消するようにエラー処理を組まないと、無限ループに入ってしまいます

画像の動きを確認してください
A1セルからマイナス方向への移動ができないため、エラーが発生します

エラー処理では、移動値に数値を加算してから処理を発生行に戻しています

しかし加算値が「1」なので「-2」からは2回実行する必要があるので、2回エラーが発生して、最後にまた「0」になりA1セルが指定されて処理が正常動作に戻ります

このように、エラー処理で複数回繰り返してでもエラー原因を解消するような組み方が重要になります

ファイルの名前を付ける際に、重複しない連番をここで更新していくことで取得することが出来ます

なお、どちらのステートメントもエラー処理に使用するコードとなります
エラーが発生していない状態で実行されるとエラーが発生します

エラー発生後のエラー処理の最後に入れるようにします

エラー処理(発生時に移動させる)

エラーの発生時に任意の箇所に処理を移動させるコード。On Error GoToステートメントを使用します

'エラー発生したときは移動させる
On Error GoTo エラー発生

エラー発生:

エラー処理として、エラーが発生した際に特定の処理を実行する場面があります

その場合、エラーが発生した時点で実行するステートメント行を移動させることになります
その際に使用するのが「On Error GoTo」ステートメントです

On Error GoTo エラー発生

このステートメントに指定した文字列がラベル文字列となります
行指定も出来ますが、通常は例のように文字列で指定します
ここでは「エラー発生」の部分が当てはまります

このコード以降にエラーが発生した時点で、処理は停止せず指定した行に処理が移動されます

なお、ラベル文字列の設定はGotoステートメントと同じ設定の仕方になるので混同しないようにわかりやすい名前を付けましょう
Gotoメソッドと同じようにラベル文字列行の前には処理を終了させる「Exit Sub」を配置するようにしましょう

この設定を解除するには「On Error GoTo 0」ステートメントを使用します
解除しないと、以降の全ての処理でエラー処理が実行されますので、エラーの予期している範囲で使用するようにします

コードの動き

エラー発生時に処理を移動させる動き
エラー発生時の動き
Err.Raise (1)

このコードはエラーを意図的に発生させるコードです

このコードが実行された時点で、エラーが発生しOn Error GoToステートメントにより処理が移動しています

通常、エラー処理は正常処理の後に作成します
そのままEnd Subまで走らせる形で処理を継続させます

エラーが発生せずに処理が通常終了する動き
エラーが発生しない場合の動き

エラーが発生せずに正常に処理が実行された場合の動きです
終了コードにより、End Subまで行かずに処理を終了させています

これが通常のエラー処理の組み込み方です

処理終了コードを入れていない場合の間違った動き
終了コードを入れてない場合の動き

この画像のように、終了コードを入れ忘れていた場合、正常処理後にエラー処理が実行されてしまいます

Exit Sub
エラー発生:

なので基本的には、以上のような形でコードを作成するようにしましょう