No.8 ワークシートの最終行、最終列を取得する | |||||
---|---|---|---|---|---|
|
|||||
ワークシートの最終セル(最終行、最終列)を取得する… VBAをはじめた人は誰しも最初に引っかかる問題ではありますが、幸いにもインターネットで検索するといくつもの解決法が見つかります。 ところが…動いたり動かなかったり、想定外の動作をすることがあります。そもそもいくつもの解決法があること自体おかしい、いったい本当の答えはどれ?この際、白黒はっきりさせようじゃないかというのがこのページの趣旨です。 ![]() <S1> ワークシートの最大行、最大列を取得するまず基本からですが、ワークシートの最大行、最大列を取得するコードです。もちろんこれ自体は最終行、最終列を取得するものではありません。MaxRow = Rows.Count MaxCol = Columns.Count Rowsはワークシートの行全体をあらわすオブジェクトで、Columnsは列全体をあらわすオブジェクトです。そのメソッドCountは数、すなわちそれぞれ行数、列数をあらわします。 返される値はEXCELのバージョンによって異なりますが、EXCEL97/2000/2003では65,536行、256列になります。先ほど発売されたEXCEL2007では1,048,576行、16,384列で、セル数にして17,179,869,184(171億)、従来の1024倍という膨大な量になります。65億全人類の名前が入ることになるわけですが…。 <S2> 下方向に最終行を検索する次はRangeオブジェクトのEndプロパティを使ってみましょう。End(xlDown)とは下方向に現在の領域の最下端を探すという意味です。マクロで記録するとわかりますが、このEnd(xlDown)はショートカットの[Ctrl]+↓にあたります。このコードは連続したセルである場合、正しく最終行を取得することができます。MaxRow = Range("A1").End(xlDown).Row MaxCol = Range("A1").End(xlToRight).Column 以下のようなシートを用意してみましょう。A1にカーソルを置き、[Ctrl]+↓を押してみます。期待通りその列の最終行であるA10にカーソルが移動することがわかると思います。 ところがシートの途中で抜けのセルがある場合、以下のような動作をします。値の存在する連続したセルを一つのブロックとして、その上端、下端を移動します。言い換えると空白の変わり目を探しているといってもいいかもしれません。 結論として、このコードはすべてのデータが連続したセルであることが保証されている場合には使用できますが、そうでない場合は使えないということです。 ![]() <S3> 上方向に最終行を検索するこれはもっとも知られている方法でしょうか。VBAの参考書などでも例としてでているようです。MaxRow = Range("A65536").End(xlUp).Row MaxCol = Range("IV1").End(xlToLeft).Column ' IV1=256列 <S2>ではセルが連続していない場合は×でしたが、こちらは連続していなくてもちゃんと動作します。仕組みとしてはワークシートの最下端(65536行)から上に向かって空白でないセルを検索するというものです。これだと途中で空白セルがあっても問題ないですよね。 ただし、このコードには問題が3つあります。 ![]() ![]() ![]() ![]() <S3-1> 上方向に最終行を検索する(改)これはMaxRow = Cells(Rows.Count, 1).End(xlUp).Row ![]() ![]() <S4> 使用済みの最終セルの選択 (SpecialCells)次にSpecialCellsメソッドを使用した例です。SpecialCellsの引数としてxlLastCellを指定すると"使用済みの最終セル"という意味になります。これはショートカットの[Ctrl]+[End](使用済みの最終セルを選択)や[Ctrl]+[Shift]+[End](使用済みの最終セルまで領域を拡張)に相当します。With Range("A1").SpecialCells(xlLastCell) MaxRow = .Row MaxCol = .Column End With やはりこの方式にも欠点があります。 ![]() 上のようなシートがあり、4〜6行を削除します。 A1にカーソルを置き、キーボードから[Ctrl]+[Shift]+[End]を押します。 すると、削除前の領域である"A1:E10"が選択されてしまいます。これはEXCELの仕様かバグなのかは不明ですが、結果として正しく判定できていないことは確かです。ちなみに一旦ワークブックを保存すると正しく判定できるようになります。 また行の挿入やセルのコピーをした場合は直後でも正しい結果が返るから不思議なものです。 ![]() <S5> 使用済みの最終セルの選択 (UsedRange)いよいよ本命の登場です。UsedRangeプロパティは指定されたワークシートで使われたセル範囲を返します。With ActiveSheet.UsedRange MaxRow = .Rows.Count MaxCol = .Columns.Count End With ですがこのコードには明らかな間違いがあるのです。 以下のようなワークシート(最初の行、列に値がない)の場合、UsedRangeは"B2:E10"(黄色の枠線で囲まれた領域)になります。Rows.Countはその領域の行数なので9、同じくColumns.Countも9となり、9列9行つまり"D9"を返してしまいます。 また、UsedRangeの特性として非表示の行・列やオートフィルタで非表示になっている領域も含みますので注意が必要です。 <S5-1> 使用済みの最終セルの選択(改) (UsedRange)<S5>の誤りを訂正したバージョンです。ややこしい表現ですが展開していくとわかりやすいです。With ActiveSheet.UsedRange MaxRow = .Rows(.Rows.Count).Row MaxCol = .Columns(.Columns.Count).Column End With .Rows(.Rows.Count).RowはUsedRange.Rows(9)つまり"B2:E10"の9行目を表し、さらにその.Rowということはワークシートの10行目ということになります。 ただし、このコードも<S4>同様書式付き空白セルまでカウントしてしまう問題は残っています。 <S6> 書式付きセルを除外する (UsedRange)書式付きセルを除外して値の入っている範囲内の最終行、最終列を求める方法です。UsedRangeの最下端、最右端からそれぞれ値の入っている最終行、最終列を検索するというもののです。With ActiveSheet.UsedRange MaxRow = .Find("*", , xlFormulas, , xlByRows, xlPrevious).Row MaxCol = .Find("*", , xlFormulas, , xlByColumns, xlPrevious).Column End With Rangeのメソッドであるfindは検索ダイアログ(メニューから[編集]-[検索])そのものです。引数もダイアログの各オプションとほぼ一致します。第1引数は検索文字列、ここでは"*"つまり空でないなんらかの値が入っていればマッチします。第3引数は検索対象でxlFormulasは数式、xlValuesで値を表します。第5引数は検索方向、xlByColumnsで列方向、xlByRowsで行方向に検索します。第6引数は唯一ダイアログにはなくxlNextで前方に検索、xlPreviousで後方に検索というわけです。 まとめ最後のコード<S6>はこれといった欠点もなく一応模範解答といえるものですが、正解かというとそうでもなく、VBAプログラムには相性というものがありそれぞれ答えが違うのではないでしょうか。たいていのプログラムは<S3>あるいは<S3-1>で問題なく動作するのです。また、行・列の非表示やオートフィルタの有無によってそれぞれ動作が異なります。それぞれの癖を熟知して使い分ければいいのではないかというのが結論とさせていただきます。 ![]() |
|||||
関連情報: | |||||