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

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

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

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

マクロ

定義された処理手続きに応じて、どのような一連の処理を行うのかを特定させるルールをマクロと呼びます。

Q&A

解決済

3回答

3597閲覧

For~next 内にある WorksheetFunction.MATCH のエラー回避手段について

mai._123

総合スコア15

VBA

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

マクロ

定義された処理手続きに応じて、どのような一連の処理を行うのかを特定させるルールをマクロと呼びます。

0グッド

0クリップ

投稿2019/09/17 15:12

前提・実現したいこと

EXCELシートの右側に、ランダムに並んだ文字列があります。
文字列の構造は[文字(値)]という構造です。
上記の文字列から、各行ごとに指定の文字列が含まれるセルを検索し、そのセルの値部分のみをどんどん書き出していきたいです。
指定の文字列が含まれるセルが存在しない場合は、エラー値または空白などが入る想定ですが「ない」ことさえわかれば何でもOkです…!

発生している問題・エラーメッセージ

WorksheetFunction.MATCHのエラーをうまく回避できず、進めません。。。
エラーではない値は、7周目(i=8)で初めて発生します。そこまで全てエラー値です。

該当のソースコード

VBA

1Dim 検索値 As String, i As Long, c As Long, MatchC As Variant, MatchR As Range 2 3c = ActiveCell.Column 4検索値 = Cells(1, c).Value 5For i = 2 To 200000 6 7 On Error Resume Next 8 MatchC = WorksheetFunction.MATCH(検索値, Range(Cells(i, c), Cells(i, "LHL")), 0) + c 9 If MatchC = 0 Then 10 Set Cells(i, MatchC) = "" 11 GoTo nexti 12 13 ElseIf Cells(i, 1).Value = "" Then Exit Sub 14 15 Else: 16 Set MatchR = Cells(i, MatchC) 17 Cells(i, c) = Mid(MatchR, InStr(MatchR, "(") + 1, InStr(MatchR, ")") - InStr(MatchR, "(") - 1) 18End If 19nexti: MatchC = "" 20Next i 21End Sub 22

試したこと

[On error Goto nexti]だと、2周めで「worksheetfunctionのmatchファンクションを取得できません」と出てしまいます。

[IsError(MatchC) then Goto nexti]だと、1周もしません。。
[Application.MATCH]も同上です。

[On Error Resume Next]で回避して、[If MatchC = 0 Then]で判定する手段もためしましたが、
[On Error Resume Next]の前に[MatchC=0]をいれるとずっと0のままで、
入れないと7周目以降も、7周目時点で代入された値を引き継いでしまいます。

補足情報(FW/ツールのバージョンなど)

EXCEL2016です。

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

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

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

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

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

guest

回答3

0

ベストアンサー

「たぶんこうしたかったんじゃないか」で書いてみました。

Dim 検索値 As String Dim i As Long Dim c As Long Dim MatchC As Long Dim MatchV As String c = ActiveCell.Column 検索値 = Cells(1, c).Value For i = 2 To 200000 If Cells(i, 1).Value = "" Then Exit Sub ' Exit判定は先頭 MatchC = 0 ' 初期化 On Error Resume Next MatchC = WorksheetFunction.Match(検索値, Range(Cells(i, c), Cells(i, "LHL")), 0) On Error GoTo 0 ' On Errorは必要最低限の範囲 If (MatchC = 0) Then ' Matchの成否判定はMatchの結果のみで行なう Cells(i, c) = "" ' 一致しなければクリア GoTo NEXTI ' 以降スキップ End If ' ' ELSE。 上記のif条件成立時はgotoでスキップするので「Else」ステートメントは不要 ' MatchC = MatchC + c - 1 ' オフセットを補正 MatchV = Cells(i, MatchC).Value ' 値を取り出す。Rangeである必要なし Dim sx As Long: sx = InStr(MatchV, "(") + 1 ' 同じ値を複数回使うなら変数に入れる(趣味の問題だが) Cells(i, c) = Mid(MatchV, sx, InStr(sx, MatchV, ")") - sx) ' 括弧内取り出し NEXTI: Next i

ちなみに、コード中のコメントにも書きましたがOn Errorを使うと
エラーメッセージが表示されませんのでエラーの箇所がわかりにくく
なります。On Errorの範囲は必要最小限にしたほうがよいです。
なお、エラーの出ていたのはMatchではなく以下の部分ではないかと。

If MatchC = 0 Then
Set Cells(i, MatchC) = ""

↑ (1)文字列の設定に"set"は不要
↑ (2)Match=0の処理なのでCells(i,0) を参照(indexエラー)。

投稿2019/09/18 06:00

編集2019/09/18 06:02
h.horikoshi

総合スコア505

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

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

mai._123

2019/09/24 09:44

ご回答いただきありがとうございます!「On Error Resume Next」と「On Error GoTo ~」をセットで使う知識がなかったことが今回ネックだったようです。式の説明を入れていただいたおかげで、細かい部分をじぶんのやりたい形に調整して書き直すこともでき、それにより理解も深まりました。ありがとうございました!
guest

0

こういう、繰り返しの作業は、
変数を使いまわしますので、
変数の初期化というのを意識して書いて下さい。
(今回の場合は、検索する前に変数の値を0に戻しておく)

ExcelVBA

1Option Explicit 2 3Sub test() 4 Dim rngKey As Range 5 Dim rngFind As Range 6 Dim r As Range 7 Dim sKey As String 8 Dim ix As Long 9 10 Set rngKey = ActiveCell 11 sKey = rngKey.Value 12 With ActiveSheet 13 Set rngFind = .Range(rngKey, .Cells(.Rows.Count, "LHL").End(xlUp)) 14 End With 15 16 For Each r In rngFind.Rows 17 ix = 0 18 On Error Resume Next 19 ix = WorksheetFunction.Match(sKey, rngFind.Cells, 0) 20 On Error GoTo 0 21 22 With r.Cells(1) 23 If ix > 0 Then 24 .Value = Val(Split(r.Cells(ix).Value, "(")(1)) 25 Else 26 .ClearContents 27 End If 28 End With 29 Next 30End Sub 31

で見つかれば0より大きい答えが返ってきますし、
見つからなければ、エラーとなり、その行の実行は無視して、
次の行に処理が移りますので、
変数の中身は変わりません。
それによって条件分岐をしたらよいと思います。

あと、
Cells(i, 1).Value = ""
というのは、セルを空白にしようとしているのでしょうか?
「””」は長さ0の文字列ですので、
「””」をいれたら、空白には「見える」が、空白になっていません。
エクセル的には、セルを空白にするのは、クリアの処理なので、
「値のクリア」の命令を実行したらいいと思います。
vBA的には、空白 = Empty
ですので、ValueプロパティにEmptyをセットするのもありだとは思います。

それから、
今回の件の場合、
操作の対象はセル範囲なので、
セル範囲を変数に代入するようにした方が、
見た目がすっきりするかなと思います。
処理速度も上がるかも?
まぁ、この辺は個人の好みもありますので、
こういう意見もあるということを知っていただければいいかなと思います。

もう一つ、
何回くらいループすることになるかわかりませんが、
WorksheetFunctionで何回も繰り返すと、
処理がかなり重いかなと思います。
ワークシート上に数式を入れた方が処理は速くなると思います。
(それなりのテクニックは必要)
その辺は検討の余地があるかなと思いました。

あ、
「値」というのは、「数値」の意味ですよね?
文字列も値の一種なので、ちょっと分かり難い表現です。

投稿2019/09/18 00:26

編集2019/09/18 23:22
mattuwan

総合スコア2136

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

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

sazi

2019/09/18 00:48

With ActiveSheet.UsedRange は宣言だけになっていませんか?
tatsu99

2019/09/18 02:20

提示されたマクロは動作確認されていないように思われます。 Option Explicit を付加して、動作確認されたマクロを提示していただけるとありがたいのですが・・・
mattuwan

2019/09/18 23:28

ご指摘ありがとうございます。 一応、誤字・脱字を修正し、コンパイルで確認してました。 が、動作確認をする義務はないと考えます。 (テストデータのイメージが分からないので) 今回のテーマは、変数の初期化でしょうから、 その辺の処理の流れをイメージで掴んでいただければ幸いです。
mai._123

2019/09/24 09:59

ご回答いただきありがとうございます。変数の初期化や軽く動作させるためのテクニックについてご教授いただきとても勉強になりました!ありがとうございます。 今回は、EXCEL音痴のメンバーに向けて作成しておりましたため「ココ押せばおけ」状態にする必要があり、ワークシート上に数式や関数を使ってのシンプルな形式を摂ることができないといった事情がございました。説明が不足しており申し訳ございません。。。 このたびは、誠にありがとうございました!
guest

0

すみません。セルの配置がよくわからないのですが、添付図のようになっているということでしょうか。
検索文字列は、選択された列の1行目の文字列(添付図のC1のセル)
検索結果は、C2~C4へ設定する。(添付図の黄色のセル)
このように読み取れますが、あってますか。
私が提示したような内容で、あなたの環境のセルの配置を画像で提示していただけませんでしょうか。
添付図

投稿2019/09/18 03:27

tatsu99

総合スコア5438

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

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

mai._123

2019/09/24 09:52

ご回答いただきありがとうございます。画像が情報保護の観点からかなりの加工を加えなくてはならなかったため、開示することができませんでした、、、。 やりたかったこととしては、上記画像においてご説明させていただくと、D列以降のセルに左詰めで「東京(45)」「東京(3)」「大阪(57)」等の文字列が入力されており、各行内において文字列に挟まれる形の空白セルは発生しません。この中から、「東京」の後ろの数値(つまり 45 や 3 など)を、C列に書き出したかったといった意図でした。 丁寧にご返信いただきほんとうにありがとうございました!
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問