エクセルVBAで最終行取得ができない

このQ&Aのポイント
  • エクセルVBAでたとえばC列にどこまでデータが入力されているかを調べる方法について説明します。
  • 通常はCells(Rows.Count, "C").End(xlUp).Rowを使用しますが、入力フォームがある場合や途中に空白セルがある場合はうまく動作しません。
  • それらの問題を解決する方法について、詳しく説明します。
回答を見る
  • ベストアンサー

エクセルVBAで最終行取得ができない

エクセルVBAでたとえばC列にどこまでデータが入力されているかを調べるとき、通常は r = Cells(Rows.Count, "C").End(xlUp).Row などでできます。 しかし添付画像のような入力フォームが出来上がっており、かつC列に最初から何らか(画像では〒マーク)の入力がされているので、そこに回答者が途中まで入力された場合、これでは最終行は取得できません。 逆に上からEnd(xlDown)でやろうとしても、途中に空白セルがあってお手上げです。 余分な〒マークを元データから削除してしまいたいのですがそれでは入力されているところのマークまで消えてしまいます。 このようなファイルが何百もあり、それを1枚のシートにまとめようとしているのですが、この最終行取得でつまずいてしまいました。 どのような方法があるでしょうか? 画像はエクセル2013ですが、実際にマクロを動かすのは2010です。

質問者が選んだベストアンサー

  • ベストアンサー
回答No.6

#5です。お礼欄、補足欄、拝見しました。 > いいえ、名前がなく、他のデータがあるレコードは、不備データ「名前入力漏れ」として再回答を依頼することになります。 了解しました。 > 入力された最終行から有意なレコードの内の一番下にあるデータセットの最終データ[備考2]の行位置を求めるため、MOD関数を使い以下のようにしてみましたが、これ正しいでしょうか? 問題なく、ばっちり正しい結果が返せています。   If (nBtmRow - 1) Mod 6 > 0 Then     x = nBtmRow + 6 - (nBtmRow - 1) Mod 6   Else     x = nBtmRow   End If のように、括弧がひとつ減らして書くことも出来ます。  「Visual Basic における演算子の優先順位」  https://msdn.microsoft.com/ja-jp/library/fw84t893(v=vs.80).aspx  こちらで補足すると、  「Mod 演算子」は「加算と減算 (+、–) 演算子」より優先で計算され、  「乗算と除算 (*, /) 演算子」は「Mod 演算子」より優先で計算されます。 また私なら、   x = 6 * ((nBtmRow + 4) \ 6) + 1 のように条件分岐なしの1行で済ませる書き方をしたり、 またはレコード数を先に求めるようにして   cnt = (nBtmRow + 4) \ 6   x = 6 * (cnt) + 1 のように(必要なくてもレコード数を明示して)書いて残して、 特に説明を添えなくても、 他人にも(忘れた頃の自分にも)理解され易く メンテが楽になる方向で書き方を選ぶことが多いです。 ここら辺は人それぞれ好みの分かれる処かも知れませんね。 お礼欄、補足欄、への返信、以上です。

emaxemax
質問者

お礼

ありがとうございました! x = 6 * ((nBtmRow + 4) \ 6) + 1 なんでこれ1行で済ませられるのが悩みました。 レコード数とちゃんと念押しで書いていただいたのでやっと理解できたみたいです。 4は、項目数と各レコードの最初の行の行数の差なんですね? だから取得した最終行番号に4を加え、項目数の6で割った値の整数値が入力されたレコード数になる。 したがってレコード数と項目数の積に見出し行の+1で最終データ[備考2]の行位置が求められたということなんですね? すぐにこんなのがわかっちゃうrealbeatinさんはすごいです。 わたしは鈍いですねえ・・・。

その他の回答 (7)

回答No.8

では、一応、Evaluate版も上げときますね。 でも、これ、誰がメンテするのか、ってことがネックになると思います。 実際ここでの質問でもEvaluateネタにはなかなか回答付かないですし、、、。 (本当はEvaluateの戻り値エラーに対する処理も必要ですが省略) Sub Re8939737ev() Const S_FML = "max(row(C1:C#)*(C1:C#<>"""")*(C1:C#<>""〒""))" Dim nBtmRow As Long, cnt As Long, vRtn   With ActiveSheet     nBtmRow = .Cells(Rows.Count, "C").End(xlUp).Row     vRtn = .Evaluate(Replace(S_FML, "#", nBtmRow))   End With   cnt = (vRtn + 4) \ 6   nBtmRow = 6 * cnt + 1   MsgBox nBtmRow End Sub

emaxemax
質問者

お礼

realbeatin さん、今回はいろいろありがとうございました。 Evaluateって聞いたことがなく、何か難しいような気がします。 いまは、明日までにこの質問に書いた何百ファイルの統合に取り組んでますので、後日勉強させていただきたいと思います。 実はその前に、また問題が起きてしまったのです。 ひとつは、シート保護されているファイルがいくつかあり、オートフィルタが使えない! もうひとつはセル内の文字数が多すぎるのか、取得したデータを Application.Transposeで、別BOOKに横向きに転記しようとすると、エラーになる! 解決できない場合、再度新たな質問をたてるかもしれません。 今回はありがとうございました。

  • Nouble
  • ベストアンサー率18% (330/1783)
回答No.7

VBA用関数しか使えない VBA用関数しか使ってはいけない では、無いですよね? Evaluateでシート関数を使えば 意図も簡単にできます ただ、 Evaluateは扱いが難しいので 其の点が、難… ですが MIN(INDEX((Left(A:A,2)="No")*ROW(A:A)+(Left(A:A,2)<>"No")*999999999999999,,)) 此を、 Evaluateに改造すれば 行ける… で、しょう(?) コツは、 定数文字列は 変数に入れてしまえ さらば楽 ですね 手元にエクセルが無いので 何ともいい難い のですが… Option Explicit ' Function 最終項探査(探査キー Az String) Az Long '   最終項探査 = Evaluate("MIN(INDEX((Left("A:A",2)="探査キー")*ROW("A:A")+(Left("A:A",2)<>"探査キー")*999999999999999,,))") Sub 呼び出しはこんな雰囲気()  Range("C"&最終項探査("No")).Select End sub バグっていたらご免なさい Evaluate解説ページ www.ne.jp/asahi/hishidama/home/tech/excel/workfunc.html

emaxemax
質問者

お礼

早朝の回答、ありがとうございます。 > Evaluateでシート関数を使えば 初めて聞く言葉で、面食らってます。 いまは、明日までの課題(この質問に書いた何百ファイルの統合)に取り組んでおりますので、後日勉強させていただきます。 ありがとうございました。

回答No.5

こんにちは。お邪魔します。 専ら実務ベースの応用的な回答になります。 「〒マーク」のことは一旦忘れてください。  [名前]  [住所]  [電話]  [メアド]  [備考1]  [備考2] という6項目単位のデータが一括りになっていて、 有意なデータであれば、6項目の内のひとつでも空欄があったとしても、 「空欄であるという情報」までは、消さない意図なのだと思います。 (それを消すぐらいならrange.SpecialCells (xlCellTypeBlanks)や  転記方法についての言及がある筈ですから、、、) 今回の課題には書かれていなくても、 その後の処理の過程では、6項目単位で処理されるでしょうから、 有意なレコードの内の一番下にあるデータセットの 最終データ[備考2]の行位置を求めることになるのでしょう。 (∵> このようなファイルが何百もあり、それを1枚のシートにまとめようとしている...) > そこに回答者が途中まで入力された場合 どこであれ「途中まで入力された場合」を想定するということになりますから、  6項目の内、[名前]が入力されていれば、有意なデータ。  6項目の内、[名前]以外の項目だけ入力されているのは無効なデータ。 のように切り捨てていくのが、現実的な対応と思います。 名前(ID)の無いレコードは扱いようがない訳ですし、 逆に、名前(ID)が入力済であればこそ、「途中まで入力された」 という判断が付くのではないでしょうか。 これは、プログラムの中身としてのロジックのことではなくて、 実務レベルでの設計基準という視点になりますから、 他の考え方があったとしても、それは職務に副った要求、 ということになるのだと思いますから、 もし違っているなら、別の方法を考えないとなりませんが、 以上の説明は、ひとつの解釈としては成立していると考えますので、 その方法を示します。  B列に対して、オートフィルターで[名前]項目だけを抽出します。  C列に対して、.End(xlUp).Rowを実行し、[名前]が入力されている最下行を取得。  最下の[名前]行の5行下の[備考2]行を取得する。 ' ' /// Dim nBtmRow As Long   With ActiveSheet     .AutoFilterMode = False     .Columns("B").AutoFilter Field:=1, Criteria1:="=名前"     nBtmRow = .Cells(Rows.Count, "C").End(xlUp).Row + 5     .AutoFilterMode = False   End With ' ' /// > このようなファイルが何百もあり ... というお話ですから、 きっとループの中で、Workbook、Worksheet、をオブジェクトとして 順次捉えながら処理することになるのかと思いますが、 例示では仮にActiveSheetを対象としていますので、適宜書換えてください。 (このやり方では、常に掛けっ放しのオートフィルターがあっても  キャンセルされます。必要なら手当ては可能です。) 何か不足があれば、補足をお願いします。

emaxemax
質問者

お礼

> その後の処理の過程では、6項目単位で処理されるでしょうから、 > 有意なレコードの内の一番下にあるデータセットの > 最終データ[備考2]の行位置を求めることになるのでしょう。 まさにその通りです。 > 名前(ID)の無いレコードは扱いようがない訳ですし、 いいえ、名前がなく、他のデータがあるレコードは、不備データ「名前入力漏れ」として再回答を依頼することになります。 ありがとうございました。

emaxemax
質問者

補足

入力された最終行から有意なレコードの内の一番下にあるデータセットの最終データ[備考2]の行位置を求めるため、MOD関数を使い以下のようにしてみましたが、これ正しいでしょうか? Sub test02() Dim nBtmRow As Long With ActiveSheet .AutoFilterMode = False .Columns("C").AutoFilter Field:=1, Criteria1:="<>〒" nBtmRow = .Cells(Rows.Count, "C").End(xlUp).Row .AutoFilterMode = False End With If (nBtmRow - 1) Mod 6 > 0 Then x = nBtmRow + 6 - ((nBtmRow - 1) Mod 6) Else x = nBtmRow End If MsgBox x End Sub

  • kagakusuki
  • ベストアンサー率51% (2610/5101)
回答No.4

 その様な場合はオートフィルターを使用して「『"〒"とだけ入力されている行』以外の行」のみを表示させてから r = Cells(Rows.Count, "C").End(xlUp).Row という処理を行う様なVBAにすると良いと思います。 Sub () Dim r As Long '変数rを定義 Columns("$C").AutoFilter Field:=1, Criteria1:="<>〒" '「〒」と入力されていない行のみを表示 r = Cells(Rows.Count, "C").End(xlUp).Row '最終行の行番号取得 Columns("$C").AutoFilter 'オートフィルターモードの解除 End Sub

emaxemax
質問者

お礼

なんと、オートフィルタで非表示になった行はEnd(xlUp)でひっかからないのですね! これは知りませんでした。 とても役に立つご教示をありがとうございました。

  • matsu_jun
  • ベストアンサー率55% (146/265)
回答No.3

スマホからの回答ですので、細かなルーチンまでの記載はできないことはご勘弁を。 大きく分けると三通りのやり方があると思います。 1) 下からxlupで検索し、結果行のセルの値が「〒」だった場合は、Rows.Countの代わりに、(結果行-1)を入れて検索し直してやればokです。Do While構文で回しましょう。 2) 表を見たところ、二行目から六行おきにデータが入っています。名前の記載が必須だとすると、まずは二行目のセルが空欄かを確認、空白でなければ次は8行目、つど行数に6を足しながら、空欄を確認するまでDo While文で回しましょう。 3) 一時的に作業列を作ります。例えばZ列に、〒を消す数式(replaceかrightで一文字だけ消すか)をしてその列に対してEndプロパティをかけてやれば良いかと。 特に上の二つは、データ量によってはかなり時間がかかるかもしれません。

emaxemax
質問者

お礼

ありがとうございました。

  • FEX2053
  • ベストアンサー率37% (7987/21355)
回答No.2

もっとプリミティブに。 Range("B65535").end(xlUp).Row これで最終行番号が得られるはずです。 すなわち、「最終行までセルにデータが埋まってる列を使って、 最終行より明らかに先のセルから、[ctrl]+[↑]を操作する」 と言う操作のマクロ記録で行ける、って話です。 セル選択は何もCellsにこだわる必要なんてないですから。 あと、どうしてもC列でチェックしなくちゃいけない場合は、 「その行は必ずデータが入っているはず」の行を順番に For I=1 to 65536 Step 10 if Cells(I,3)="" then Exit For Next みたいな形で、Iをチェックする方法もあります。

emaxemax
質問者

お礼

ありがとうございあす。 Range("B65535").end(xlUp).Rowでは、あらかじめ作成された入力用フォームの最終行になり、実際に入力してもらうC劣の最終行ではありません。 >「その行は必ずデータが入っているはず」の行を順番 これはいいですね、ありがとうございました。

  • kuma56
  • ベストアンサー率31% (1423/4528)
回答No.1

>かつC列に最初から何らか(画像では〒マーク)の入力がされているので・・・・ そのなんらかがさいしょからわかっているなら、 r = Cells(Rows.Count, "C").End(xlUp).Row のあとに、IF関数でセルの内容を判断して、該当する物なら消去して、もう一度最終行は取得しにいけば??

emaxemax
質問者

お礼

ありがとうございます。 たしかにその通りなのですが、あまりに手間取りすぎ、何百ファイルをそれでまわすと時間がかかりそうです。

関連するQ&A

  • エクセルVBAでデータ最終行取得方法

    エクセルVBAでデータ最終行取得方法で良い方法を教えてください。 データの行数、列数は不定。 最多のデータ行の列も不定。 この条件で、データ最終行を取得するにはどうすればよいでしょうか? lastrow = ActiveSheet.Cells(ActiveSheet.Rows.Count, "A").End(xlUp).Row では、A列の最終行に限定されます。 lastrow = ActiveSheet.Cells(1, "A").SpecialCells(xlLastCell).Row では、列の限定はありませんが、一旦データ入力後、削除した部分まで入ってしまいます。 lastrow = ActiveSheet.UsedRange.Rows.Count では、データ入力後、削除した部分まで入ってしまい、かつ、1行目など上部が空白の場合、不正確になります。

  • エクセルVBAにて最終行の取得とその活用

    エクセルVBA初心者です。 VBAで入力行最終行を取得するのは、色々なところに載っているですが、 その行から一個下の行を全て消すのはどうしたらよいのですか? Sub 最終行取得() Dim r r = Range("B4").End(xlDown) Rows(r + 2).ClearContents  ←ここは適当です。 ちなみに Range("a"&r+1).ClearContents で一つしたのセルを消す事は出来ました。

  • エクセルVBAで最終行取得方法

    エクセルVBAで最終行を取得する良い方法を教えて下さい。 VBA初心者でいきづまっていまして、、、 内容はボタンを押した時にF列に対して最終行を取得して、 その最終行の下のセルにボタンの内容が繁栄させるようにしたくて Private Sub CommandButton202_Click() Dim lastRow As Long lastRow = Cells(Rows.Count, 6).End(xlUp).Row + 1 Cells(lastRow, 6).Value = CommandButton202.Caption End Sub と作ってみたのですが、F17より下に文字がはいっているため、 検索範囲を「F列」ではなく、「F2:F16」のように範囲を指定したいのですがどうしたらよいでしょうか、、、。 結構色々調べてはみたのですができなくて、、、

  • Excelでフィルターのかかっているときの最終行の取り方。

    Dim mySh As Worksheet Set mySh = Sheet("Sheet1") Debug.Print mySh.Cells(mySh.Rows.Count,1).End(xlup).Row でデータのあるA列の最終行を取得していますが、この場合、フィルターがかかっていると実際のデータのある最終行でなくて見えている部分の最終行が返ってきます。 Excel2003 SP3 WindowsXP SP3 フィルターがかかっていても実際のデータのある最終行を取得する方法あるのでしょうか?

  • エクセルで結合セルがあるため最終行が解りません。

    エクセルで結合セルがあるため最終行が解りません。 A列2行目と3行目が結合セル(見出し)のためA列の最終行(罫線を除きデータが入ってる行)を求めれなくて困ってます。データは4行目以降から入力していく予定ですが Range("A" & Rows.Count).End(xlUp).Row は2となるため+1で入力行を求めてると3となってしまいます。A4からAの最後の行の範囲で罫線を除く最終行を出せればよいのですが  最初は4 以下順次5 6 7 ‥ どなたかご教示頂けないでしょうか?

  • VBA データのある最終行の取得

    エクセルVBAで最終行を取得する良い方法を教えて下さい。 VBA初心者でいきづまっていまして、、、 内容はボタンを押した時にF列に対して最終行を取得して、 その最終行の下のセルにボタンの内容が繁栄させるようにしたくて Private Sub CommandButton202_Click() Dim lastRow As Long lastRow = Cells(Rows.Count, 6).End(xlUp).Row + 1 Cells(lastRow, 6).Value = CommandButton202.Caption End Sub と作ってみたのですが、F17より下に文字がはいっているため、 検索範囲を「F列」ではなく、「F2:F16」のように範囲を指定したいのですがどうしたらよいでしょうか、、、。 結構色々調べてはみたのですができなくて、、、

  • VBAで最終行を取得する方法

    最終行を取得する方法について教えていただきたいです。 例えば以下のようなデータがあった場合 (A列~D列、3行までデータがある状態) -------------------------------- 田中 24歳 生命保険 営業 山口 47歳 銀行   営業 大野 36歳 IT    SE -------------------------------- ここで最終行を取得する際に、 Range("A1").End(xlDown).Rowをすれば取得できました。 しかしデータが以下のような場合 (A列が空白) -------------------------------- 田中 24歳 生命保険 営業    47歳 銀行   営業    36歳 IT    SE -------------------------------- この場合に上記と同じ取得方法をすると、オーバーフローになります。 そこで他の取得方法を調べたのですが、 ActiveSheet.UsedRange.Rowを試したところ、最終行として取得できたのは「1」でした。 A列が空白の状態でも、今回の場合だと「3」という最終行を取得するにはどうすれば良いでしょうか。

  • 何列の何行目が最終行なのか取得するコードはありますでしょうか?

      A列  B列  C列 1  ○   ○ 2        ○ 3               ○ これは最終行が3行目と言うのがぱっと見でわかりますが もっと列も行も多い場合、 シート内の何列の何行目が最終行なのか取得するコードはありますでしょうか? Cells(65536, ?).End(xlUp).Row を行ないたいのですが 毎回何列の行が一番下かを取得する方法があれば教えてください。 よろしくお願い致します。

  • VBA最終行取得

    Sub 最終セル取得() Dim gyou As Integer gyou = 1 Do Until Cells(gyou, 1).Value = "" gyou = gyou + 1 Loop Range("C1").Value = gyou End Sub 上記のプログラムのどこを変えれば 文字が入力されている最終行の取得ができますでしょうか。 A列に入っている文字の最終行(セル)がC1に表示される というようにしたいのですが…; このまま実行すると1つ多いセル行が表示されてしまいます 同じような質問をしてしまって申し訳ありません; どなたかわかる方回答お願い致します Excelは2000です;

  • VBScript エクセル 最終行と最終列取得

    今、たくさんのシートがある、エクセルのファイルがあり、「C6」以降が縦の項目行、「D5」以降が横の項目列です。 項目行と項目列には、間に空白のセルは存在しません。 この状態で、項目行、項目列の最終の場所を取得しなければなりません。 そこで、「r = Range("C6").End(-4121).Row」(「-4121」は「xlDown」)、「c = Range("D5").End(-4161).Column」(「-4161」は「xlToRight」)で次々に求めていったのですが、「C6」に縦の項目が1つだけ、「D5」に横の項目が1つだけ、というシートがありました。 すると、そのシートでは、「r」と「c」の値は、エクセルそのものの最大の行数と最大の列数が返ってきてしまいました。 そこで、「D6」にのみ、文字があるサンプルファイルを作り、そのサンプルファイルに対して、「r = Range("D5").End(-4121).Row」と「c = Range("C6").End(-4161).Column)」で求めると、ちゃんと、「r = 6」、「c = 4」という値が返ってきたので、「r」の場合は、1つ上から、「c」については、1つ左から「Range」を設定すればいい、と思って、実際のデータで試すと、他のシートでは、全然うまくゆきません。 結果的に、問題は、「If」文で、ムリヤリ解決させたのですが、「If」文の場合ですと、前もって全シートを確認して、調べておかなければなりません。 何とか、汎用的に項目が1つしかない場合も、その項目のある「行」と「列」が最終行、最終列として取得できる方法はないでしょうか? ただし、項目が「0個」ということはありませんので、そこまで考えて頂く必要はありません。 「VBA」でも結構ですので、よろしくお願いいたします。

専門家に質問してみよう