[ 初めての方へ | 一覧(最新更新順) | 全文検索 | 過去ログ ]
『エラー処理について』(T1608)
下記コードを実行すると、「オーバーフロー」エラーになります。
おそらく「0除算」が原因かと考えているのですが、何故でしょうか?
A/BでA=10,B=0などの簡単なコードでは試しましたが問題ありません。
返信遅れがちになるかと思いますが、よろしくお願いします。
Set r3 = Range("AA2:AA" & maxR2) v3 = r3.Value i = 0 With WorksheetFunction For Each t In r3 i = i + 1 z = t.EntireRow.Range("B1") z2 = t.EntireRow.Range("A1") On Error GoTo Err 'エラー処理 v3(i, 1) = .CountIfs(Range("AP2:AP" & maxR), z, _ Range("AQ2:AQ" & maxR), z2, _ Range("AS2:AS" & maxR), "<=3") / _ .CountIfs(Range("AP2:AP" & maxR), z, _ Range("AQ2:AQ" & maxR), z2) GoTo MC 'Main Course Err: v3(i, 1) = 0 MC: Next End With
maxR,maxR2は入力データの最終行です。
< 使用 Excel:Excel2010、使用 OS:Windows7 >
まずは、この1行を消してから実行してみてください。そして、エラー停止した際に、各変数の中身がどうなっているか、全て調べてみてください。
怪しそうなのは、変数宣言部分です。そこを省略せずに、貼ってください。
(???) 2016/08/26(金) 11:44
分子も分母も共に0の場合、オーバーフローになるようです。
こんな風にして、0かどうか判断して進めれば、On Error 文に頼ることもないと思います。
With WorksheetFunction For Each t In r3 i = i + 1 Z = t.EntireRow.Range("B1") z2 = t.EntireRow.Range("A1")
N = .CountIfs(Range("AP2:AP" & maxR), Z, Range("AQ2:AQ" & maxR), z2, Range("AS2:AS" & maxR), "<=3") S = .CountIfs(Range("AP2:AP" & maxR), Z, Range("AQ2:AQ" & maxR), z2)
If N = 0 Or S = 0 Then v3(i, 1) = 0 Else v3(i, 1) = N / S End If Next End With
(半平太) 2016/08/26(金) 12:09
宣言部分を追記します。
Dim r3 As Range
Dim t As Range
Dim v3 As Variant
Dim i As Long
Dim z As Variant, z2 As Variant
Dim maxR As Long, maxR2 As Long
ご指摘の「On Error GoTo Err」を無効化した結果は「オーバーフロー」でした。
デバッグしたところ、v3(7,1)が「Empty値」となっています。
できれば、不測の事態(どのようなエラーでも)に備えつつできるかぎり簡単なコードを..と「On Error..」処理を使えないかと思ってますが、どうもお二人ともあまり推奨されていないように感じますが、何故でしょうか?
(邪推?であればご容赦願います..^^;)
関数式をマクロ化する作業を進めていますが、同様な課題がありそうな式が多くあります。
これを機会にしっかり学習いたしたく、ご教授願います。
(T1608) 2016/08/26(金) 14:20
>どうもお二人ともあまり推奨されていないように感じますが、何故でしょうか?
正しい使い方を理解していないように見えるから、じゃないですか。www
(とおりすがり) 2016/08/26(金) 14:35
で、何故 On Error Goto を使うな、と言うのか。 これは、Goto文と似た文字列ですが、Goto文のようなジャンプ命令ではないからです。ジャンプではなく、エラートラップ命令なのです。 つまり、割り込み分岐であり、どこからでもここに飛ぶ可能性のある、特殊処理です。 近年の高級言語では、Catch〜Try 等の命令に相当します。 C言語等の、Goto文は使うな、というのとは全く違います。
本当はどうやって使うのかというと、エラートラップにかかって飛び先に移った後、エラー時の処理を行い、終わったらResume文(ヘルプの例だとReturn文)で元のソースに戻します。これをせずに飛びっぱなしにすると、次々とエラー発生するとどんどん資源を使い続け、資源不足でシステム停止してしまう事があるのです。まぁ、そこまで行くことはまずないですが、1回エラー発生し飛んでも、2回目のエラーには無反応、とかもあります。
割り込み分岐の飛び先は、なるべく迅速に処理を終えて、元のコードに戻す必要があります。これは、分岐先でまたエラーになったらどうするの?、という事になるから。
Goto文と似ているので、多くの方が間違ったまま使い、思ってもいない時に問題発生する原因となっていたりします。 間違えているのに、当人には間違っているという意識がないので、原因不明で終わったり、ネットに質問を投げても迷宮入りしたりしがち。 だから、間違った使い方をした人は、二度とOn Error Goto文は使うな、というアドバイスをするのです。
(どうしてもOn Errorが必要な場合は、On Error Resume Nextを推奨します)
(???) 2016/08/26(金) 14:46
On Error は以降どこで発生するのか判らない割り込み処理なので、本来のロジック以外の遅さ、ロジック解析の判りにくさに繋がります。 今回は計算は1箇所ですが、実際にエラー発生した行はどこか?、も、判らなくなりますよね。 On Error Resume Nextも含め、必須な時以外は使うべきではないのですよ。
(???) 2016/08/26(金) 15:00
???さんから専門的なポイントでのお話がありましたので、 ちょっと別の観点で。
どんな時にエラーが発生するか?
1.間違えたプログラムを書いた場合 2.特定のデータを取得するためにVBAのメソッドを使ったとして、メソッドの中には、特定のデータがない場合にエラーになるものがある。 3.プログラムは正しいけれど、扱うデータに仕様外のものがある場合。(数値であるべきなのに英字になっているとか)
で、このエラーに対して、エラートラップで、エラーをバイパスするわけですね。
でも、1.は論外でしょう。 間違いは間違いで、バグですから、早く直さなきゃいけない。エラーバイパスすると、間違いに気が付くのが遅れる。 もしかしたら永久に間違ったまま。それでは具合悪いですね。こういった目的でエラーバイパスをやってはいけません。
2.については、事前に特定のデータの有無を調べるという手もありますけど、面倒なので、エラーバイパスが多いですね。 ただし、On Error Resume Next で処理して、当該メソッドの後に On Error Goto 0 でリセットしますね。
3.が悩ましいですね。相手にするデータが、他人(取引先であるとか、社内でも別部署であるとか)が作ったものであり その整合性や信憑性に、今いち、信頼がおけない といったケース、少なくないですね。 もちろん、それが少ない、あるいは、こことこと と限定できれば、処理前にチェックをかけて、エラーにならないような処理もできますけど あれもこれも・・・チェックのためのコードのほうが、正規処理のコードより多くなってしまう。 (実際の業務プログラムでは、例外チェックコードが正規処理コードより多いというのは、めずらしいことではないですが)
こういう場合に、エラートラップをかける。これはやむをえないかもしれません。 ただし、間違ったデータだったので、処理をしなかった、あるいは 値を 0 にした といったノーティスをあげることが必須。 メッセージも何もなしで、正常終了すると、
・本来は、仕様通りのデータを作ってもらうように依頼しなければいけないと思いますが、いつまでたっても、データが不備のまま。 ・処理結果が、たとえば 0 になっていて、これが本当の 0 なのか、本来 0 ではないのに、データエラーで 0 になったのかの 判断ができない。(そういったエラーが起こったということを誰も気づかない) その結果、その数値をもとに、販売活動等を行う場合、とんでもない事業損失が発生する可能性あり。
(β) 2016/08/26(金) 15:38
返信遅れてすみません、ありがとうございます。
原則的には半平太様ご提示案やIsEmpty関数、IsError関数で事前判定して
エラー回避をしたいと考えます。
その上で以下教えていただけないでしょうか?(今後の方針は上述なので、学習目的です)
>Variant型変数とした、とか、何でもアリの型にしたためかと思います >正しい使い方を理解していないように見える >間違えたプログラムを書いた場合 ↓ 今回うまくいかない具体的な原因は何でしょうか? 追記)データを提示していないので、可能性が高い原因という意味です。
>エラー発生するとどんどん資源を使い続け、資源不足でシステム停止してしまう事がある ↓ 「オーバーフロー」と表示さるのは、このことでしょうか?
>1回エラー発生し飛んでも、2回目のエラーには無反応 ↓ これはある意味怖いですが、エクセルの「バグ」とでもいうべきものでしょうか?
>On Error Resume Next で処理して、当該メソッドの後に On Error Goto 0 でリセット ↓ 当初はこれを使いましたが、これだとエラー発生時は「0」にならず「空白」となるため、 提示コードにしました。 「On Error Resume Next」を使用して「0」処理するコードを考えてたのですが、 うまくいきません..できるものなんでしょうか?
よろしくお願いします。
(T1608) 2016/08/27(土) 09:06
>>1回エラー発生し飛んでも、2回目のエラーには無反応 >↓ >これはある意味怖いですが、エクセルの「バグ」とでもいうべきものでしょうか?
いいえ、On Error ステートメントの使い方が間違っているからです。解説サイトを探して 勉強しましょう。www
(とおりすがり) 2016/08/27(土) 10:22
>>当初はこれを使いましたが、これだとエラー発生時は「0」にならず「空白」となるため、提示コードにしました。 >>「On Error Resume Next」を使用して「0」処理するコードを考えてたのですが、うまくいきません..できるものなんでしょうか?
今回は、半平太さんから提示された事前チェックで回避できますけど、一般論として。 エラーリセット前にエラー番号を保存しておいて、それを判定すれば、エラーが発生したのかどうかがわかります。
Sub Test() Dim w Dim er As Long
On Error Resume Next w = 100 / 0 '★エラーになる er = Err.Number On Error GoTo 0
MsgBox er
On Error Resume Next w = 100 / 10 '★エラ−にならない er = Err.Number On Error GoTo 0
MsgBox er
End Sub
(β) 2016/08/27(土) 17:33
>当初はこれを使いましたが、これだとエラー発生時は「0」にならず「空白」となるため、
>提示コードにしました。
>「On Error Resume Next」を使用して「0」処理するコードを考えてたのですが、
>うまくいきません..できるものなんでしょうか?
理解の単位がとても大粒です。
○○のときは××を使うというような、ユニット単位で考えているように見えます。
思い通りにコーディングするには、せめて行単位(できれば単語単位)の理解が必要です。
「On Error Resume Next」の意味ですが、
「エラーが出ても無視して次の行からコードの実行を続けろ」です。
「無視して」だから、「エラーが出ました」のダイアログボックスは出ません。
ただし、エラー自体は発生しています。無視されるだけです。消えてなくなるのではありません。
そして「次の行から実行」です。エラーが出た行は実行されません。
つまり v3(i, 1) = .CountIfs(Range("AP2:AP" & maxR), z, 〜 は、実行されません。
実行されなければとうぜん v3(i, 1)になにも代入されませんので、v3(i, 1)の値は代入前のままです。
代入前の値が「空白」なのか「0」なのかは、「On Error Resume Next」と関係ありません。
関係があるのは、代入前にv3(i, 1)に何が格納されていたかです。
特に何も代入していないなら、その変数型の初期値はなにか、です。
( 佳 ) 2016/08/28(日) 08:26
みなさん、ご丁寧な説明ありがとうございます。
ご指摘のとおり、中途半端な理解で安易に使えないなぁ〜と
つくづく思いました...
十分に咀嚼できているかはなはだ疑問ありですが、
作業を進めていきたいと思います。
これからも、よろしくお願いいたします。
(T1608) 2016/08/28(日) 12:58
'分母を事前判定する例 Sub test1() Dim w As Long Dim x As Long
x = 0 If x = 0 Then w = 0 MsgBox "分母0は0割エラーになります。", vbCritical, "エラー" Else w = 100 / x MsgBox w End If End Sub
'On Error Resume Nextで処理する例 Sub test2() Dim w As Long Dim x As Long Dim iEr As Long
x = 0 Err.Clear On Error Resume Next w = 100 / x iEr = Err.Number On Error GoTo 0 If iEr <> 0 Then w = 0 MsgBox "分母0は0割エラーになります。", vbCritical, "エラー" Else MsgBox w End If End Sub
'On Error Gotoで処理する例 Sub test3() Dim w As Long Dim x As Long Dim iEr As Long
x = 0 On Error GoTo sEr w = 100 / x MsgBox w
L1: On Error GoTo 0 Exit Sub
sEr: w = 0 MsgBox "分母0は0割エラーになります。", vbCritical, "エラー" Resume L1 End Sub (???) 2016/08/29(月) 10:40
再々ありがとうございます。(返信遅れましてすみません)
「On Error...」は、よほど注意深く使用しないと“ケガをする”ことが
よくわかりました。
また、コード自体も(使わない場合より)簡単とはいえないですねぇ..
“錯覚”しておりました。
ご教授陣レベルの足元に近づいたかな..と思えるまでは、「使用厳禁」と
します(苦笑)。 これからもよろしくお願いいたします。
(T1608) 2016/08/30(火) 09:37
[ 一覧(最新更新順) ]
YukiWiki 1.6.7 Copyright (C) 2000,2001 by Hiroshi Yuki.
Modified by kazu.