質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
VBA

VBAはオブジェクト指向プログラミング言語のひとつで、マクロを作成によりExcelなどのOffice業務を自動化することができます。

Q&A

5回答

6055閲覧

ExcelVBAで、空白がある表を範囲指定してコピーしたい

退会済みユーザー

退会済みユーザー

総合スコア0

VBA

VBAはオブジェクト指向プログラミング言語のひとつで、マクロを作成によりExcelなどのOffice業務を自動化することができます。

0グッド

1クリップ

投稿2019/07/30 02:10

VBAエキスパートの資格を取るため、勉強中です。
今回、ExcelVBAで範囲指定をしてコピーするためのコードを練習しているのですが、以下、1点分からないところがあります。

[やりたいこと]
画像1と2のように、空白がところどころに入っている表をコピーできるようにしたいです。
また、どこに空白があるかは分からないものとします。

イメージ説明
イメージ説明

この場合、タイトルは除きます。

[考えた方法]

空白がどこにあるか分からない表をコピーする方法としては、まず

  1. 表の最終行を取得する
  2. 表の最終列を取得する
  3. 最終行と最終列を使って範囲指定してコピー

という方法が考えられると思いました。
この場合、まず表の最終行を取得するコードを考えないといけないのですが、

VBA

1'表の最終行を取得(5列目) 2endcol = (Cells(Rows.Count, 5).End(xlUp).Row)

というコードを使って列を指定してその行の最終行を取得するコードがあることを知りました。

ですが、今回の場合はどこで空白があるか分からないため、行を指定してその行の最終行を取得するということは難しいと思います。

そこで、例えば最終行取得ならば1列ごとにその列の最終行を取得し、その中で一番大きな数字を表の最終行とする方法を考えました。

コードは以下の通りです。

VBA

1Option Explicit 2 3'名前の定数===================================================== 4Const stroutput_FName As String = "練習.xlsb" 5Const stroutput_SheetName As String = "テスト" 6Sub test() 7 8Dim wboutput As Workbook 'アウトプットファイル格納 9Dim wboutput_Sheet As Worksheet 'アウトプットファイルのシート 10 11Dim endcol As Integer 12Dim endcol_1 As Integer 13Dim endcol_2 As Integer 14Dim endcol_3 As Integer 15Dim endcol_4 As Integer 16 17Dim endrow As Integer 18Dim endrow_1 As Integer 19Dim endrow_2 As Integer 20Dim endrow_3 As Integer 21Dim endrow_4 As Integer 22 23 24 'アウトプットファイルのファイルパスを取得 25 Workbooks.Open ThisWorkbook.Path & "\" & stroutput_FName 26 Set wboutput = ActiveWorkbook 27 Set wboutput_Sheet = ActiveWorkbook.Worksheets(stroutput_SheetName) 28 29 30 '最終行をチェック 31 endcol_1 = (Cells(Rows.Count, 1).End(xlUp).Row) 32 endcol_2 = (Cells(Rows.Count, 2).End(xlUp).Row) 33 endcol_3 = (Cells(Rows.Count, 3).End(xlUp).Row) 34 endcol_4 = (Cells(Rows.Count, 4).End(xlUp).Row) 35 36 endcol = WorksheetFunction.Max(endcol_1, endcol_2, endcol_3, endcol_4) 37 38 39 '最終列をチェック 40 endrow_1 = Cells(1, Columns.Count).End(xlToLeft).Column 41 endrow_2 = Cells(2, Columns.Count).End(xlToLeft).Column 42 endrow_3 = Cells(3, Columns.Count).End(xlToLeft).Column 43 endrow_4 = Cells(4, Columns.Count).End(xlToLeft).Column 44 45 46 endrow = WorksheetFunction.Max(endrow_1, endrow_2, endrow_3, endrow_4) 47 48 Range(Cells(1, 1), Cells(endcol, endrow)).Copy 49 50 51End Sub 52 53コード

ですが、この方法だと、行や列がたくさんある時はその都度変数をたくさん作っていかねばならず効率がとても悪いです。

そこで他にももっと良いコードがないか色々と調べましたが分かりませんでした。

今回のように、空白がどこにあるか分からない表の範囲を取得してコピーする良いコードはありますでしょうか?

ご教示よろしくお願いいたします。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

torisan

2019/07/30 02:29

その表があるシートには 別のデータ、別の表は存在しますか?
退会済みユーザー

退会済みユーザー

2019/07/30 07:22

はじめまして。興味を持って下さりありがとうございます。 いいえ、ございません。
guest

回答5

0

手順としては
1行目は見出しで空白がないと前提するならば

手順1:1行目の一番右が何列目かを取得

vba

1max_column = Sheets("Sheet1").Range("A1").End(xlToRight).Column

手順2:手順1で取得した列数分forで最大行を取得

vba

1For roop = 1 To max_column 2 max_row = Sheets("Sheet1").Cells(Rows.Count, roop).End(xlUp).Row 3next roop

手順3:手順2のforの中で各列の最大行を比較して大きいものを変数に入れる。

vba

1max_num = 0 2If max_num < max_row Then 3 max_num = max_row 4 Else 5End If

手順4:手順3までで選択する範囲がわかるのでselectで範囲を指定

vba

1Range(Cells(2, 1), Cells(max_num, max_column)).Select

ざっくりこのような手順でできます。
表の列が増えても行が増えても対応できます。

ただし、1行目にも空欄が入る可能性があるなら手順は変わります。
また、表の開始が1列目でない場合も手順が少し変わります。

全体

vba

1Sub select_cell() 2 3 4 Dim max_row As Long: max_row = 0 5 Dim roop As Long 6 Dim max_column As Long 7 Dim max_num As Long: max_num = 0 8 9 '表が1列目から始まっていて見出しに空白がないのが前提 10 max_column = Sheets("Sheet1").Range("A1").End(xlToRight).Column 11 12 13 For roop = 1 To max_column 14 max_row = Sheets("Sheet1").Cells(Rows.Count, roop).End(xlUp).Row 15 If max_num < max_row Then 16 max_num = max_row 17 Else 18 End If 19 Next roop 20 21 Range(Sheets("Sheet1").Cells(2, 1), Sheets("Sheet1").Cells(max_num, max_column)).Select 22 23 24End Sub 25

投稿2019/07/30 04:53

編集2019/07/30 05:21
beginner_t

総合スコア716

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

条件がわからないので
http://www.niji.or.jp/home/toru/notes/8.html
↑から適した物を探してみて下さい。

投稿2019/07/30 03:04

torisan

総合スコア678

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

ですが、この方法だと、行や列がたくさんある時はその都度変数をたくさん作っていかねばならず効率がとても悪いです。

こういう場合は、
For~Next文で繰り返し列を移動しながら最終行を探索し、
If~Then~文で比較して大きい方を変数に書き換えて記録していきます。

こんな説明でわかりますか?

その後、質問者さんの反応がないけど、、、、
ループして最大値を探すのは、If~Then~や変数の使い方も含めて、基本中の基本なので、
自分で思いついて、自分で書けるようになりましょう。
この辺は経験なので、試験用の勉強だけでは辛いかもです。

他の方も書いておられますが、僕が書いたらこんな感じ、、、、

ExcelVBA

1Sub test1() 2 Dim rngTable As Range '表のセル範囲(シート上の使用している範囲) 3 Dim rngTarget As Range 'コピーしたいセル範囲 4 Dim c As Range '各セル 5 Dim ix As Long '行番号 6 Dim ixMax As Long '最大データ行番号 7 8 '表のセル範囲を取得して変数に記録 9 Set rngTable = Sheets("Sheet1").UsedRange 10 11 '列毎に繰り返し見て行き最大データ行を調べる 12 For Each c In rngTable.Columns 13 ix = c.Cells(c.Cells.Count + 1, 1).End(xlUp).Row 14 If ixMax < x Then ixMax = ix 15 Next 16 17 '取得した最大行番号でコピー 18 With rngTable 19 .Range(.Cells(2, 1), .Cells(ixMax + 1, .Columns.Count)).Copy 20 End With 21 22 '貼付 23 With Sheets("Sheet2") 24 .Paste .Range("A2") 25 End With 26End Sub

自分でループ処理を書かなければ、こんな感じですかね。。。

ExcelVBA

1Sub test2() 2 Dim rngTable As Range: Set rngTable = ActiveSheet.UsedRange 3 Dim rngBottom As Range 4 5 With rngTable 6 With .SpecialCells(xlCellTypeConstants) 7 Set rngBottom = .Areas(.Areas.Count).EntireRow 8 End With 9 Set rngBottom = Intersect(.Cells, rngBottom) 10 End With 11 Application.Range(rngTable.Rows(2), rngBottom).Select 12End Sub

勘で書いたので、もしかしたら不具合があるかもです。
シート上の値の配置パターンにより、意図しない結果になる場合は、
その配置のパターンをお教え下さい。
暇があれば対応策を考えてみます。

投稿2019/07/30 02:41

編集2019/07/31 10:04
mattuwan

総合スコア2136

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

range("A1").select
Selection.CurrentRegion.Select

で行けます。

空白欄を選択しておくと意図しない動きをすることがあります。
(例:4列目までしか無い時に、5列目を選択していると1~5列目が選択される)

追記
すみません1行目いらないんですね。
でしたら
range("A2").select
Range(ActiveCell, ActiveCell.SpecialCells(xlLastCell)).Select
でA2からの範囲で選択されます。

投稿2019/07/30 02:39

編集2019/07/30 02:47
expy

総合スコア103

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

mattuwan

2019/07/30 03:08

>range("A1").select >Selection.CurrentRegion.Select >空白欄を選択しておくと意図しない動きをすることがあります。 今回の件の場合、セルを選択する必要がないので、 あえて選択しなくてもいいです。 「A1セルを含むアクティブな表をコピー」 と命令すればいいのです。 つまり、 range("A1").CurrentRegion.Copy と命令すれば、セルの選択状況に左右されません。 ただし、今回の件の場合は、表の中に空白行がある可能性があるので、 CurrentRegion案は使えません。 それから、 SpecialCells(xlLastCell) ↑これはネットで検索してみるとわかると思いますが、 意図したセルが取得できずにトラブルの元になりますので、 別のアプローチを模索した方が無難です。 ちょっと気になったのでコメントしました。
expy

2019/07/30 06:21

ですと普通にループさせる方が良いですね。
meg_

2019/07/30 10:53

「SpecialCells(xlLastCell)」は正常に動作しないことがあるのでしょうか? ネット検索したところ、10年程前の古いExcelでは起こっていたのか、またはブックを保存前に使用していた結果のようです。(Q&Aサイトの情報のため詳細不明) 手元の環境「Excel2013」では正しく最終セルが取れています。 今後のために、詳細をご存知でしたら教えてください。
mattuwan

2019/07/30 12:17

そういえば、最近はその手の質問を見掛けませんね。 改善されたのかもしれませんが、他に確実な方法があるので、 SpecialCells(xlLastCell)をあえて使ってないので、分かりません。 ブックを保存する前に、シート上で編集行った後、 SpecialCells(xlLastCell)を使っても編集結果が反映されなかったように、 記憶してます。 回避するには、ブックを一旦保存するか、 UsedRangeプロパティを使用すると情報が更新されたように記憶してます。 気になるなら、個人で実験して探求してみてください。 個人的には、 with activesheet.usedrange .cells(.cells.count).select end with とした方が確実だと思ってます。
meg_

2019/07/30 12:43

現在のエクセルでは保存しなくても正しく最終セルを取れました。 但し「SpecialCells(xlLastCell)」にしても「Usedrange.cells(.cells.count)」にしても罫線が引かれたセルも対象になってしまいますね。 こちらの質問への対応は意外と大変ですね。 コメントありがとうございました。
mattuwan

2019/07/30 23:10

>罫線が引かれたセルも対象になってしまいますね。 >こちらの質問への対応は意外と大変ですね。 そうですか? 「使っている範囲の内、2行目から、データの入っている最後の行」が 操作(コピー)の対象なのですから、 それさえ言語化できれば、方法論はいくつかあると思いますよ^^ SpecialCellsを使えばループ処理を書かなくてもよいかと思います。
mattuwan

2019/07/31 10:17

>現在のエクセルでは保存しなくても正しく最終セルを取れました。 こちらは2010ですが、不具合がやはりありますね。 Sub test() Dim r As Range MsgBox Cells.SpecialCells(xlCellTypeLastCell).Address(0, 0) Range("B2:E5").Borders.LineStyle = True MsgBox Cells.SpecialCells(xlCellTypeLastCell).Address(0, 0) Range("A:B").Delete MsgBox Cells.SpecialCells(xlCellTypeLastCell).Address(0, 0) ActiveSheet.UsedRange MsgBox Cells.SpecialCells(xlCellTypeLastCell).Address(0, 0) End Sub 列を削除したあと、C5を取得して欲しいですが、 なにもないE5を返します。 UsedRangeと呪文を唱えると、 更新されてC5を返しますね。
guest

0

最終行、最終列だけではなく、最初の行と列も不明という条件のように思いますが、一番左上は固定で良いのでしょうか?
例えば、A列にデータがなく、B列から始まる場合、コピーもB列からであると思いますが、どうなのでしょう。
念のため、下記のコードでは一番左の列と一番上の行も取得するようにしています。

質問者さんの例の場合、Cells.Findでセルに何らかの文字または数式があれば、そのセルのアドレスを検索して見るという手法が使えます。変則的なやり方ですが、使い方次第では、セルの内容に応じて取得するかしないかを選択できたり、色々おまけ機能も加えられますので、対応の幅が広がるメリットがあります。
このコードで取得できる値を使ってコピーをすると予想通りの動きにならないでしょうか。

VBA

1Option Explicit 2 3Sub GetFirstAndLastAddressNumber() 4 5 Dim minRow As Long: minRow = Rows.Count '一番上の行番号 6 Dim maxRow As Long: maxRow = 2 '一番下の行番号 7 Dim minCol As Long: minCol = Columns.Count '一番左の列番号 8 Dim maxCol As Long: maxCol = 1 '一番右の列番号 9 10 Cells(1, Columns.Count).Select 11 12 Cells.Find(What:="*", After:=ActiveCell, LookIn:=xlValues, LookAt:=xlPart, _ 13 SearchOrder:=xlByRows, SearchFormat:=False).Activate 14 15 Do 16 If minRow > ActiveCell.Row Then minRow = ActiveCell.Row 17 If maxRow < ActiveCell.Row Then maxRow = ActiveCell.Row 18 If minCol > ActiveCell.Column Then minCol = ActiveCell.Column 19 If maxCol < ActiveCell.Column Then maxCol = ActiveCell.Column 20 Cells.FindNext(After:=ActiveCell).Activate 21 Loop While ActiveCell <> Range("A1") 22 23 MsgBox "最上行:" & minRow & " 最左列:" & minCol & "最下行:" & maxRow & " 最右列:" & maxCol 24 25End Sub

投稿2019/07/30 09:44

Secret

総合スコア220

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問