[ 初めての方へ | 一覧(最新更新順) | 全文検索 | 過去ログ ]
『On Error 〜ステートメントについて』( ichinose)
↑このスレッドで On Error 〜ステートメントの是非について、反論を受けました。 本投稿は、File I/Oのエラーチェックという上記スレッドのこっくさん(さん)の投稿内容に限ったことでないということ、上記スレッドでは、その辺りが直接の原因ではないらしいということで 別スレッドにて考えたいということで投稿いたしました。
実は、私、FileI/Oに関して、On Error 〜ステートメントを使わない対処をとる投稿には、 今までも On Error 〜ステートメントを使う方が良いと何度も投稿してきました。 それは、FileI/Oのエラーチェックとして、これより良い方法を知らないからです。
このOn Error 〜ステートメントについて、このサイトだけでなく他のサイトでもなるべく使わない方がよい という意見を投稿された方がおられました。
かなりの熟練者のかたの発言だったので影響力があったのでしょう。
その内に
コレクションから指定キーを検索するのに
For each でぐるぐる回して検索するコードまで出てきました。
On Error Resume Next
Set Obj=Col(Key)
If err.number<>0 then ‘見つからない処理 Else ‘見つかった処理 End if On Error Goto 0
とした方が速いのに・・・。
On Error 〜ステートメントは、使い方が難しいステートメントですが、使わなくてもプログラムが作成できるステートメントでは ありません。 よって、毛嫌いするのではなく、良い使い方をさぐるべきステートメントだと思います。 これを研究すれば、随所で役に立つステートメントに成り得ると信じています。
前述のスレッドにて、βさんが >基本的には、むやみやたらの エラートラップ(バイパス)は使うべきではないと考えています。
と記述されています。この記述だけ見れば、私も頷けます。 なのにOn Error 〜ステートメントについての考え方に大きな隔たりがあります。 ファイル I/OのエラーチェックにOn Error 〜ステートメントを使うのは、むやみやたらのそれなんでしょうか?
むやみやたらとは、どんな使い方を指しているのでしょうか? 逆に言えば、むやみやたらの範疇の外であれば、On Error 〜ステートメントを使ってもよいことになります。
この「むやみやたら」という言葉を定義することが鍵のように見えてきますが、いかがですか? 多くの方の御意見を伺いたく思います。
手始めに 先のスレッドにおいて、私に対してのβさんの反論にたいする意見を次投稿にて・・・。
< 使用 Excel:Excel2010、使用 OS:Windows7 >
ファイル I/Oに対してのOn Error 〜ステートメント使用の否定的な意見があるとは 思っていなかったので、正直驚いたのですが、βさんの記述を伺った上での私の意見です。
ファイル I/Oとは、Openを含めたファイル(フォルダも特別なファイルと解釈)に対する処理 全般(読込 書込み 削除 コピー 移動 その他)を指します。 これらのエラーチェックに対し、On Error 〜ステートメント以外に簡単で便利な方法はないと思います(知りません)。 ファイルI/Oの一例でオープン処理(Open Workbooks.Open Workbooks.Opentext etc)では、必ず ファイルの有無のチェックを中で行っています。 加えて、その他のエラーも厳正にチェックされた後にオープン処理が行われます。チェックに引っかかればプログラムは、エラーとなり、そこで止まります。 本来ならば、エラーになってプログラムが止まる処理をOn Error 〜ステートメントを使うことにより プログラムを中断することなく、エラーの有無という結果を受けとり、その後の処理をプログラムに委ねることが出来ます。 Dir関数等を使わなくてもファイル存在チェックを行い且つ、その他のエラーもチェックしてくれるのです。 仮にDir関数でのエラーチェックを行うとすれば、ファイルの存在チェックを Dir関数とオープン処理の2回行うことになります。 処理自体は大したことはないですが、無駄と言えば無駄です。 オープン処理に対する例ですが、このような理由から、ファイル I/Oに対してのOn Error 〜ステートメントは非常に効果的だと思っています。
>確かに、ファイルそのものは存在するけど、様々な原因で読めない場合もあります。 >だけど、それを言い出せば、エクセルの世界です。たとえばセルにエラー値が(計算式の結果だけでは ・ ・ >もう面倒だからと言って、(β) 2015/09/03(木) 19:17 で、ちょっとふれましたが、先頭に1行、 >On Error Resume Next と書くという >本末転倒の発想を持つ人もでてくるわけです。
これは、Excelなんてエラーだらけなんだから、何もファイルI/Oだけきちんとしてもしかたない という話でしょうか?
私は、ファイル I/Oのエラーチェックの手法について話をしています。 他のエラーの問題は ファイル I/OのOn Error 〜ステートメントを使う使わないとは関係ありません。 VBAプログラマは、粛々とより良い手法を考えることが肝要だと思います。 先頭に1行うんぬんは、正に使い方の問題です。βさんのいう「むやみやたら」の範疇なんでしょうか?
>ましてや、今回のテーマは、(こっくさん) ご自身が、自分で作業するためのコードです。
今は、個人使用でも作られたものでもその完成度によっては、将来、研究室内でのツールになる可能性もあるかもしれませんよ。 その修正コードが大変な労力を有するならまだしも手法自体は 労力を有するものではありません。
又、「勉強を始めた」と仰っています。先のスレッドの投稿を見る限り、 真面目な方なのだろう と感じました。そんな方には、ファイルオープンでは、ファイルが存在しても ファイルが使用中やファイルの使用権限の設定により、エラーになることがあるということも知って頂くことは、今後にもつながることだと思います。 そして、そういうことも考慮して、On Error 〜ステートメントを使うということもです。
エラー発生時にプログラムを止めいない理由は、そのときその時で違うので一概には言えません。 例えば、先のコレクションの件、SpecialCellsメソッドの件等。 ファイルI/Oでは、例えば あるフォルダ下の多数のファイルを処理する仕様の場合など、 特定のファイルに致命的なエラーがあっても そこでプログラムを止めずに次のファイルを処理し、 エラーファイル情報はエラーログとして残す なんて仕様も考えられます。
私のファイル I/Oに対するエラー処理手順は、30年前のオフコン時代と大きく代わっていません。
当時のファイルのオープンって、今のOpenステートメントみたいに簡単ではなく、かなりの手順が必要だったのですが(なんやかんやで50ステップぐらい)、 当時On Error 〜ステートメントはありませんが、Open処理でリターンコードは返ってきました。 このコードから、オープンが正常終了か、 ファイルが存在しないか使用中かその他のエラーかという手順で処理をしてきました。 今と変わりません。パソコンがスタンドアロンの時代は、使用中かと特別にチェックするのは 止めていましたが、それでも その他のエラーかというチェックは行っていました。
今、複数のパソコンが繋がっていることを考えると、やはり 使用中(シェアエラー)までは常に頭に入れておくことが必要だと思います。
On Error 〜ステートメントは、必要です。 On Error 〜ステートメントは、うまく使うことです。 むやみやたら を定義するヒントになれば 投稿した意味があります。
追伸
Dir関数だって 条件によってはエラーになりますよ!! もっともこれは、代用品がありますけど・・・・。
( ichinose) 2015/09/06(日) 19:19
私の発言がきっかけのトピですので、ROMってわけにはいきませんね。 ただ、今なお現役でご活躍のichinose師匠のご意見に対して、私のようなポンコツが、さっと、もっともだなぁという コメントをすぐにアップするのは難しいので少し自分でも整理してみます。
いずれにしても、コードをどう書くべきかということは、大きな構えとしての開発方法論の、一部というか 一部にしか過ぎないということと、開発方法論は、「こうだ、これしかない、こうじゃなきゃいけない」といったものではなく 「商品」として提供する側、「商品」を武器として使う側、自分で自分の業務の補助のために使う人 それぞれの立場というか、 もっとストレートにいうと「利害」を基準にして定められるというのがβのスタンスです。
ソフトウェアという「商品」は、それ自体には、そんなに価値はなく、それを、経営や事業推進で、どのように 活用できるか、これもストレートにいうと、事業での収益が確保できるか、そこがポイントですから、 コード理論だけではなく、その保守体制が、「継続性」をもって維持できるかということが重要で その観点で見ますと、「少々、効率の悪いコードでも、それが一握りの人だけが対応できるのではなく、それに携わるメンバだれもが対応できる」 というレベルの維持ができなければ、「開発方法論」としては、「こんな難しいコードをかいちゃだめ」というのも 当然ありなんです。
ちょっと横道にずれましたかね。
エクセルVBAですから、ファイルIO以外にも、エラーになる可能性は、もう、そこらじゅうにあります。 これはβがかって、統括していたメンバに言い渡していたことですけど、 たとえばあるプログラマがアプリプログラムを書く。そのコードが10あったとして、その中の2ぐらいを 中途半端に、たとえば、今回のきっかけのファイルIOのエラーチェックにつかっている。 こんなばかなことはやめろ! と指導していました。10の余力があるなら10をすべて、アプリロジックにつぎこめと。
確かにミッションクリティカルな処理で、特に、対象が外部から送られてきたファイルだった場合、心配だから というポイントもありますが、それだけ重要な処理であれば、アプリロジックの中に、中途半端なンチェックを混在させて わかりにくくするより、ファイルは正常だという前提で、すべての力をアプリとしてのビジネスロジック実装につぎ込むべき。 それほど、重要な処理であれば、アプリとは別に、事前にファイルが適正かどうかのチェックステップを実行させる。
これがβのスタンスです。 したがって (少なくともエクセルVBAの世界では) 【障害ではないけどエラーになりうる、ないしはそれを回避するためのロジックをいれると煩雑になり本筋のコードの流れが見づらくなる】 こういう場合に限って(βは)エラートラップをかけます。
いずれにしても、時間をいただいて、少し整理してみます。
(β) 2015/09/06(日) 19:48
きちんと整理して ichinoseさんのコメントに見解をアップするのは、まだ時間がかかりそうです。
ちょっとポイントがずれてしまうかもしれませんが、プログラムというものに関するβの基本スタンスについて 別の板ですけど、
http://excelfactory.net/excelboard/excelvba/excel.cgi?mode=all&namber=171603&rev=0
この 投稿者/ β -(2015/09/06(10:24)) でコメントしてみました。
まぁ、だからなんだというものかもしれませんが。
いずれにしても ファイルIOのチェックをするなら On Error 〜 が一番簡単でわかりやすく かつ、コードの有効性も高いということには異論はありません。
違いは
ichinoseさんは、「だから」それを使う。 βは ファイルが読めないことは想定しない。だから【使わない】
それだけだと思ってますが?
(β) 2015/09/06(日) 20:08
実際に業務で使用するには、エラー処理が重要になり、それに注意を払うことはプログラマーとして当然のことと思います。 プロシージャの1行目にOn Error Resume Next を記述してその後のすべてのエラーを無視するような手法は 論外ですが、エラーが容易に予測できる行を、On Error Statementでエラーを判定することは効率的な手法でしょうし、 この件については、その目的はハッキリしているし、簡潔に処理されていて二重丸でしょう。
(seiya) 2015/09/06(日) 21:11
よ〜く考えてみると、本件(逃げるわけじゃないですけど)私がコメントした開発方法論云々といったものではなく
1つには、ファイルIOのチェックは On Error 〜 が一番効率的 かどうか。 2つめには、そのチェックをすべきかどうか。
こういったことなんですよね。
で、1.に関してはすでにコメントしていますが、チェック【するなら】一番効率的だろうと、そう思いますので すくなくともichinseさんとβ間で議論するテーマではないですね。 (他の皆さんから、いやいや、こっちのほうが効率的だというものが紹介されるかもしれませんけど)
なので、議論を続けるとすれば2.、別の観点でいいますと、エクセルVBAで書き上げるコードの中で どんなものを エラートラップするかということなのかなと。
で、もう、これは、「こうであるべき」ということではなく、それぞれのスタンスで、それぞれ基準をもてばいいということですから これも、意見交換としては有意義ですけど、べき論ですすめると不毛のトピになりそうで、もったいないですね。
少なくとも エラートラップをかける場合に、コードはわかりやすく、間違いのないように書きましょうということは 議論の余地がない部分ですね。
ちなみに、「意見交換」としては、βがかけるエラートラップは、SpecialCellsでの取得の他には、あまり思い浮かびませんね。 これも、FindメソッドのようにNothing を返してくれれば不要なんですが、MSが意地悪をするのでしょうがなく。
特殊なケースとしては・・・そうですね、たとえば、操作者がセルに文字列をいれる、この文字列を新規シートの名前にする。 シート名として使っていい文字や、その長さというものは、ちゃんと仕様としてあるわけで、それをチェックしてもいいのですが この仕様自体がバージョンによって(桁数含め)かわってきていますので、もう、これはエクセルにまかせようということで エラートラップ。
この文字列が読みこむべきファイル名だった場合は、基本、エラートラップはかけませんね。DIR関数です。 見つかった時に2回アクセスすることになる といったことは、まったく気にしませんね。(βの場合は)
また、コードの中で特定シート(たとえば "基本データ") を取得する場合は、 単純に Set 変数 = Sheets("基本データ") ですね。エラートラップはかけません。 エクセルの世界ですから、操作者がシートを削除している可能性もありますが、そういったことにロジックを割くことはありません。
たとえば、複数のシートを1つのシートに統合する、この場合、どんどん転記していくと どこかで行数や列数オーバになりエラーになるわけですが、ここにエラートラップもかけませんね。 なったらなったということで。(転記前のチェックも基本手を抜いています)
そういうことが、どれだけの頻度、確率で発生するか、まぁ、これは、個別要件ごとに異なるわけですが しょせん(?)エクセルの世界で、こんなことはないんだろうなと。少なくとも我々がふつうにエクセルを使うケースでは。
もちろん、【商品】として客先に納品するようなものは、このあたりにも手をいれるべきなんでしょうけど それなら、エクセルVBAじゃないでしょ という気もします。
これはβの個人的な「感覚」なんですが、エラーを発生させると、なんだか、エクセル君に、石を投げつけて 痛めているような、そんな罪悪感(?)
なので、できるだけ、エラーを発生させずにチェックできる方法を模索してコード化しますね。(βの場合は)
といいながら、だんだん面倒になって、あれやこれやに エラートラップをかけるようになるかもしれませんが。 老人になると、根気がなくなるので。
(β) 2015/09/06(日) 21:20
(1)時間をかけない
Excel VBAで作成するプログラムは、多くの場合、ツールの位置づけだと思っています。
特に、ここで質問される方の問題解決には、短いソースコードで済む場合が多いです。
(中には、シート不要なのに、それExcelでやるの?、というような処理要求もありますが…)
なので、システムを構成するアプリ開発とは違い、きっちりしたエラー処理に時間をかけるよりも、
エラーならそこで止まってくれる方が、開発時間はかからないし、問題解決もしやすいと思います。
(2)少しでも短いコード
初心者さんに提示するサンプルでは、なるべく実現したいロジック以外を排除し、
コードの理解を容易にしてあげたいので、On Errorは必須な場合以外、書きません。
止まったら、デバッグするのも、初心者さんの勉強になりますし。
Dir関数のところでよくエラー停止するのですが…、とか追加質問が出た場合には、On Errorを教える時期到来です。
(ファイル名を間違えていただけ、という場合がほとんどだったりしますけどね)
(3)使い方を間違えやすい
On Errorを教えない理由はもうひとつ。エラートラップとか割り込み処理とかの考えを理解していないと、
Gotoと等価に考えて、みつけにくいメモリリークに繋がる場合がある点。Resumeせず、Gotoで飛ばす人が多いのですよ。
割り込み処理内で、本筋のロジックが続いていくという事に気持ち悪さを感じます。
まぁ、On Error Goto と書くから、Gotoの仲間に見える訳ですが。
必要があって教えるときは、On Error Resume Nextにしています。そして、目的の箇所の後にはすぐOn Error Goto 0。
(エラートラップ解除がGoto 0なんて、ものすごい古風な命名だと思います。昭和だなぁ〜)
次のステートメント実行で勝手に内容が消えてしまう、Errオブジェクトにも注意です。
(4)コレクションのループは?
Sheets("シート名")は、エラー処理無しで多用してます。止まったら、文字列を見直せば、大抵解決です。
On Error してからの Set Obj=Col(Key) は、私は反対ですね。エラー原因が名前指定ミスだけなら良いですが、
他の原因でも If err.number<>0 だと反応してしまうから。
エラーコードを指定すれば良いだけですが、そうなると今度は他の原因ならどうする?、という事になり、
例えば MsgBox表示して止まる、とかを追記して、コーディングが長くなってしまいます。
しっかり対応すると、時間をかけずに短いコード、という方針に反してくるという事になります。
(???) 2015/09/07(月) 10:24
βさんと私は、先に示したスレッドにてのこの議題の言わば当事者です。 質問者さんの目的と離れると申し訳ないので 単に場所を移したスレッドです。 このスレッド、もうちょっと開けておかせてください。
seiyaさんと???さん、ご意見ありがとうございます。 seiyaさんとは、このOn Error ステートメントに関する考え方は、以前にもお聞きしたことがありました。 大筋、私と同じような考え方を持っておられますよね!!
???さん、投稿ありがとうございます。 取りあえず、この一点だけ。
>On Error してからの Set Obj=Col(Key) は、私は反対ですね。エラー原因が名前指定ミスだけなら良いですが、 >他の原因でも If err.number<>0 だと反応してしまうから。
これの弊害を教えてください。
私が申し上げたのは、コレクションを使った検索をイメージしています。
コレクション内にあるかないかわからないキーを検索するときの手法として >On Error してからの Set Obj=Col(Key)
という意味で申し上げています。Workbooks Worksheets Shapes あるいは、Namesなども 同じです。
>他の原因でも If err.number<>0 だと反応してしまうから。 は、キー指定とインデックス指定でのエラー番号の違いがあることは承知していますが、 If err.number<>0では、問題になる理由、事例等がありましたら、教えてください。
( ichinose) 2015/09/08(火) 06:17
Sub test() Dim iw As Long Dim cw As String
On Error Resume Next cw = Sheets("Sheet1").Names '★ If Err.Number <> 0 Then cw = "NG" Else iw = "OK" '★ End If On Error GoTo 0
MsgBox cw End Sub
シートがあればOK、無ければNGを表示するコーディング例になります。
これだと短いので一目瞭然ですが、変数名を間違えた、というバグのつもりです。
On Error が効いているために、問題箇所でエラー停止せず、原因探しが手間取る…、と。
だからといって、On Errorを使うのは駄目、というわけではなく(むしろ、速度面ではループより良い)、
他のエラー全てを隠してしまう可能性があるので、初心者には必要がなければ教えない、という考えです。
(???) 2015/09/08(火) 10:55
(11:35 コーディング例に、プロパティの使い方ミスも追加)
>他のエラー全てを隠してしまう可能性 なるほど、でもこれは、コレクション使用時に限ったことではありませんよね? これは、On Errorステートメントを使う際に気をつけなければならない箇所ではあります。
キー設定のコレクションで Col(Key) という使い方で検索できなければ 何のためのコレクション構造か と疑問に思ってしまいます。
On Error ステートメントは使い方が難しいステートメントです。 使い方の要点を押さえることは重要だと思います。 >これだと短いので一目瞭然です
ご自身が仰られた↑にもそのヒントがありそうに思えます。
On Errorステートメントを使う範囲が短ければ、想定外のエラーも見つけやすい On Errorステートメントを使う範囲を単体テストが簡単に実行できる構造にしておく
なんてことが???さんの既述から考えられます。
単体テストができれば、これは 一例ですが、VBEの「ツール」---「オプション」---「全般」タブ ここのエラートラップという項目で 「エラー発生時に中断」で On Error ステーメントを無視する 設定なども可能です。
後は、VBAに対するスタンスの問題でしょうか?
( ichinose) 2015/09/09(水) 09:13
>>後は、VBAに対するスタンスの問題でしょうか?
つきつめると、そういうことだと思います。 エクセルVBAの(通常の)守備範囲は、エンタープライズなシステムを構築するというより 自身が参加している職場での、ちょっとした処理の自動化という面が、おそらく大部分。
もちろん、seiyaさんが指摘されるように、
>>実際に業務で使用するには、エラー処理が重要になり、それに注意を払うことはプログラマーとして当然のことと思います。
この通りだと思います。
その注意の範囲でしょうね。これは処理がどんなものなのか、運用はどのようにされるのかによっても様々でしょうし プログラマあるいはプログラマが属する組織全体の、それぞれの考え方によるんだと思います。
繰り返しになりますが、ファイルIOに関して、エラートラップをかけるということは ・ファイルの存在チェックができる。 ・そのほかの予期しない様々なエラーも防ぐことができる。 いわゆる【1粒で2度おいしい】方法だということには異論はありません。
違うのは、【2度おいしい、その2度目の要件もチェックが必要だからエラートラップを掛けるべき】かどうか ということでしょう。 で、これは人それぞれでいいと思います。
βのスタンスは、『通常起こりうる例外』については、1粒で1度おいしければ、それでいい というものです。
まったく別のポイントで、これは、あまりこのテーマには関係がないかもしれませんけど、 エラートラップで様々な障害の発生を回避でき、安定稼働が得られたとします。 業務処理としては、素晴らしいことです。 ただ、コードを書く側からいえば、めったに発生しない障害が発生して実行時エラーになる。 で、そこで、あれこれデバッグし、苦労はしますが、あぁ、こういったことがありうるのかと。 このようにして、アプリコードでは見えない裏側の仕組みに触れる機会がもてる。 で、これがテスト段階ですべてでれば身につきますが、実際にはそうならず、本番運用で発生。 ここにエラートラップがかかっていれば、プログラマが駆け付けたときには、エラー時の状況とは違っていますので エラートラップをはずして再現しようとしても、なかなか再現しない。 実行時エラーのままであれば、そのときの状況がそのまま残っているのでデバッグも少しは楽。
本番でエラーになるということは、通常のエンタープライズなシステムではあってはいけないことですが しょせん(しつこいですね)インターラクティブに操作者とエクセルがやりとりしながら処理をすすめていく世界。 それが、【ふつうは発生しない障害】であれば、本番エラーで、あぁ、ごめんごめん でもすむのではと横着に考えています。
もちろん、エンタープライスシステムとして24時間自動稼働とか、そういったものであれば別です。
(β) 2015/09/09(水) 09:57
ちょっと長話が出来ないので書き逃げになるかも&スタンスの話でしかないけどちょろりと。
on error resume nextをうまく使えば便利、そこに異論は無い ただ掲示板などにアップしたコードはそれを見た誰かがその意図に気付かず流用や改造した結果 エラーが発生してるのに気付かない、なんてコードが生まれそうだから極力使いたくない意識があったりする
コードの途中でいきなりon error resume nextが出てくると個人的には混乱する エラーの検知が前提であるならそれを別の関数に閉じ込めてその中で使う けど元コードが提示されてる中でその途中に組み込む方がシンプルになるならそれでも良いか その場合注釈コメントをガンガン書くと思う
VBA言語としてTry〜Catch〜Finalyに相当するものが提供されてればいいのにと思うことがある 無いものねだり 自力で似た構造で処理したいならon error〜を使うことになる
Collectionにキーがあるかを見る場合、ExistがあるDictionaryを使いたくなる 順次処理するだけならCollection、一部を探して取り出してならDictionary
自分がon error resume nextを使った例 他PGがファイル書き出しが終わったかを見るために そのファイルをmove可能か試みてエラーが出るなら待ち、みたいな場面で使用したことがある 記述に際しては先に示したように関数化してその中だけで使うようにした (ご近所PG) 2015/09/09(水) 10:50
エラー処理ねぇ。 なんか素人レベルと製品レベルで違うんじゃないかと。 想定されたエラートラップに使うのは大ありだと思うし。
製品版だと予期せぬエラーってのがありますからね。 ユーザー側の使い方の問題、データファイルは、完璧に作られているのか、 その他もろもろ。 で、「デバック!」なんて出たら・・・。 製品版だと、マクロ保護して見えなくしている場合ばっかりだし。 製品版を作ったことがないけど。
最終的にはエラー処理は必要なのだけれど、素人に教えると、 原因を調べずに何でもかんでも on error で済まそうとする傾向がかなり高いって事なんだろうと思います。 しょっぱなから、on error resume next だと動いているのに結果が出ない。ってなことに。
これって、on error を教えるか教えないかのレベルなんだろうか? 汚い言葉で言うと、素人の知ったかぶりの結末って事じゃ? () 2015/09/09(水) 13:49
ROMってましたが、臆せず参加です。
FileI/Oに関しては知見を持っていないのでなんとも言えないのですが 「むやみやたら」に対してご近所PGさんがおっしゃった >エラーの検知が前提であるならそれを別の関数に閉じ込めてその中で使う
という使い方が一番わかりやすいです。(業務でしか使わないズブのど素人の意見ですが)
何に使うか、ではなく、どのように使うか、という視点でも「むやみやたら」を定義できそうな気がしました。
それに関数で分ける手法を選ぶなら、真似されてもそのまま使われる可能性が高いし、 そこそこ経験を積んだ方しか手を出さないだろうし・・・。
関数化すれば、連結文字列を引数にしたデバッグも容易に行えますから。
みなさんはFileI/Oを関数にするなら(引数をパスの文字列、戻り値をワークブックオブジェクト) どういう作りにするのですかね? (稲葉) 2015/09/09(水) 15:25
ご近所PGさん 投稿ありがとうございます。
大筋は、私も同じです。 >極力使いたくない意識があったりする ここも本来は、同じだと思います。が、 私は、On Errorステートメントに対して、使用することに もう少し積極的な発言を掲示板ではしてきたと思います。
今回と同様に使わない方がよい 発言が多くなってくると、使い方をもっと学ばなくてはならない ステートメントなのに話題に上がらなくなってしまう というのが主な理由かなあ・・・。 今回のFileI/Oのように On Errorステートメントを使う方がより確実なのに そのことを知らないから 使わなくなってしまう エラー処理が増えそうだと思ったことが大きいかなあ。
>ExistがあるDictionaryを使いたくなる これも同じですが、私が言っているコレクションはExcelが構成しているコレクションや Ado(Adox)等外部コンポーネントから取得するコレクションを指しています。
>関数に閉じ込めてその中で使う こういうOn Errorステートメントの使い方のヒントになるような話を掲示板上でしたかった ということです。 私もそうのような方法で使っています。 [[20140903104551]]
これは、ADOやADOXの例ですけど・・・。
>名無しさん 投稿ありがとうございます。
>素人に教えると、 >原因を調べずに何でもかんでも on error で済まそうとする傾向がかなり高い
On Errorステートメントは、このように使うとよい 等という既述や難しいポイントなどのコメントが 殆どないからでしょうね!! よって、きちんとやるべきだと思うのです。難しいAPIを使いこなすよりは、簡単だと思いますよ!!
>稲葉さんへ 投稿ありがとうございます。
>という使い方が一番わかりやすいです。
そうですね!!
File I/Oと言ってもインターフェースは 種類によって違ってきますし、結構悩むことがあります。 ご近所PGさんに対する返信で ADOのI/Oですが、私がこのとき 作成したモジュールパックです。
このようにしておくと 単体テストも比較的簡単にできます。
もう時間、仕事なので 後は、次の投稿にて。
( ichinose) 2015/09/10(木) 13:01
Excelワークブックを開くは、難しいですね エラーコードが単純なのでメーセージごと返さないとわかりづらいのです。 Openステートメントや ADO等は、細かくエラーコードを返してくれますけどね!!
これは 一例ですが、
Function Mybkopen(ByVal flpath As String, Optional bk As Object, Optional emes As Variant = "", Optional ByVal disp As Boolean = False) As Long '機能 指定されたファイルパスで示されたExcelブックを開く ' input flpath -----開くブックのパス ' disp -----エラー時に メッセージ表示の有無を指定 True---関数内でエラーメッセージを表示 False--エラーメッセージは表示しない(既定値) ' output Mybkopen----エラーコード 0 正常に開いた その他 ---エラー ' bk ----正常に開いたワークブックを取得 ' emes ----エラー時のエラーメッセージを取得する Application.DisplayAlerts = False 'これ付けないとファイルが存在しないときだけエラーがメッセージが表示される On Error Resume Next Set bk = Workbooks.Open(flpath) If Err.Number <> 0 And disp Then MsgBox Err.Number & " : " & Err.Description End If Mybkopen = Err.Number emes = Err.Description On Error GoTo 0 Application.DisplayAlerts = True End Function
使用例
Sub testfunc1() '正常ブックを指定する Dim wbk As Workbook Call Mybkopen(ThisWorkbook.Path & "\サンプル2.xlsx") 'ただ単にブックを開けば良い場合) MsgBox "ただ単にブックを開けば良い場合" ActiveWorkbook.Close False Call Mybkopen(ThisWorkbook.Path & "\サンプル2.xlsx", wbk) MsgBox "開いたブックを変数WBKに格納する" & " : " & wbk.Name wbk.Close False End Sub Sub testfunc2() '自分でエラー処理 Dim emes As String If Mybkopen(ThisWorkbook.Path & "\ないブック.xls", , emes) <> 0 Then MsgBox emes Else '正常時の処理 End If End Sub Sub testfunc3() 'エラー処理(メッセージ表示)を関数に任す Dim emes As String If Mybkopen(ThisWorkbook.Path & "\ないブック.xls", , , True) = 0 Then '正常時の処理 End If
End Sub
基本的に 設計をトップダウンとボトムアップを併用するような手法をとれば、自然にこのような構造のプログラムを作るように なるはずです。 その際のインターフェースは非常に悩みますよ!!
今抱えている仕様だけを視野に入れるのではなく、他の場合(仕様)でも簡単に利用できるようにするには??
なんてことを考えながら決めようと思うので本当に悩みます。
( ichinose) 2015/09/11(金) 03:18
そろそろ βさんに対してのコメントです。
何故 エラートラップをかけるのか?
SpecialCellsメソッドだって エラーになるはずはない この仕様では必ず、一つは検索値がある このような環境下なら On Eerorステートメントは要らないかもしれません。 βさんが例にあげられた >コードの中で特定シート(たとえば "基本データ") を取得する場合は、 >単純に Set 変数 = Sheets("基本データ") ですね。エラートラップはかけません
これだって 基本データというシートがないなどという事は仕様上あってはならない あるということは プログラム上に問題があるという判断になるならば、トラップはかけません。
トラップかけるのは、仕様上 ここでかけないとエラーになる可能性があるからかけるのです。
SpecialCellsメソッドで仕様上 検索値がない可能性があるから エラートラップをかけるのです。
Set Obj=Col(Key)
このコードもコレクションにないキーが引数になる可能性があるからエラートラップをかけるのです。
仕様上 存在しないキーが引数になることはありえないならば、トラップはかけません。
ファイル I/Oは、ハードに対するアクセスですから、どんな仕様でも何が起こるかわからない
だから、エラートラップをかけるのです。
トラップをかけた後の処理は、プログラマに任せればよいのです。 仕様によって様々な対応があると思います。
seiyaさんのコメントに対し、
>>実際に業務で使用するには、エラー処理が重要になり、それに注意を払うことはプログラマーとして当然のことと思います。
>この通りだと思います。
と仰っていますよね?
私もβさんなら、こんなこと言わずもがなの話だよなあ と思っています。
問題になったこっくさんのスレッドでは、投稿されたコードには 既に On Errorステートメントは使われていました。
使い方がよくないのでそれを修正するのではなく、エラー処理としては、より不完全なDir関数を推奨されましたよね!! 既に On Errorを知っている方なので 使い方の問題だ と 私は 指摘しました。
これに対し VBAレベルでは、しかも 個人的レベルに使うものに そんなエラーチェックは要らないという趣旨の投稿ですよね!!
こっくさんは、既にエラートラップの方法を知っています。
FileI/Oにおいて On Errorステートメントを使う効果や使い方を記述するべきだと思いましたから、 既述のような結果になりました。
FileI/O に限って言えば
個人的なレベルのコード、私なら、それでも ある目的で作ったVBAコードなら必要箇所では On Errorステートメントを使います。いずれ 誰かが使うかもしれないということを考えての事です。
あるオブジェクトの勉強や研究のために その使い方や動作のゆくえを知るためだけのコードなら、On Errorステートメントはつかわないこともありますけど・・。
今は個人使用でも 将来的に 他人が使う可能性があるならば(希望でもいいです)、On Errorステートメントを使ったエラー処理を行うのがよいと思いますよ!!
これね、使い方は難しいですが、分かってしまえば、このFileI/Oに施すOn Errorステートメントは、そんなに時間が掛かるものではありません。
分かってしまば Dir関数もOn Errorステートメントも対した時間差ではありません。
ご自身が仰れているように
>SpecialCellsでの取得の他には、あまり思い浮かびませんね。
これには 使うのですよね?
他にも 実行した結果がエラーになることで判断するような事例があれば使うという事になります。
こんな事例は FileI/Oを含めて 他にもあると思いますよ!!
それともSpecialCellsは使っているから許すけど、それ以外は VBAでは個人レベルだからエラーになってもよいと言い続けますか?
On Errorステートメントを使う可能性があるなら、その使い方を学ぶべきです、研究すべきです。
使いたくないから使わない 使わないから 手法が開発されない でもどうしても必要なら使う
もう一度、
On Error 〜ステートメントは、必要です。 On Error 〜ステートメントは、うまく使うことです。
(ichinose ) 2015/09/11(金) 05:18
この掲示板でもFileI/O関して エラー処理をしていないコードもよく見かけますし、 それに対し、エラー処理すべき という投稿はしませんし、私も コードを提示する場合、 エラー処理のないコードを提示します。
それは、テーマが違うからです。
でも、Dir関数などでファイルの存在チェックはしているが On Errorステートメントのないコードには その点を指摘する投稿をするかもしれません。
>>実際に業務で使用するには、エラー処理が重要になり、それに注意を払うことはプログラマーとして当然のことと思います。
私もこの意見に賛成ですし、業務のコードを作成する ものが殆どだと思います。 業務なら、今は、個人使用でも 出来が良ければ業務を引き継ぐ別の方も使う可能性があります。
だったら、極力止まらないコードを書くことに努力するべきです。
閉めたわけではありませんが、私は、時間を隔てて投稿頂いた内容を気が付いた時に見返してみたいと思います。
今は、納得できませんが もしかしたら 気持ちぐらいわかるかもしれませんから。
中締めという事で 投稿いただいた方に感謝いたします。
βさん、不毛になるのは、議題でなく、議論する当事者の問題だと思います。
私は、 べき論でもよいと思っています。
ひょんのことから このスレッドを残すことができたのですから、これは、βさんのおかげです。 感謝します。
(ichinose ) 2015/09/11(金) 07:05
まず、言葉尻というかささいなポイントで恐縮ですが
>>問題になったこっくさんのスレッドでは、投稿されたコードには >>既に On Errorステートメントは使われていました。 >>使い方がよくないのでそれを修正するのではなく、エラー処理としては、より不完全なDir関数を推奨されましたよね!! >>既に On Errorを知っている方なので 使い方の問題だ と 私は 指摘しました。
こっくさんもコメントしておられますが、
>>エラートラップについて、 おっしゃる通り、データファイルがなかった場合の対応策としてOn Error >>Resume Next を使っていました。
つまり、ファイルの存在チェック以外も心配だからということではないですよね。 (逆にいえば、こっくさんの環境でも、考えうるリスクとしてはファイルがないということだけと、そう判断しておられたわけです)
何度も申し上げます。 どうすべきか、これは、それぞれです。(人・・・であったり、要件・・・であったり、立ち位置(立場)・・であったり) 私は、本件(のみならず)で 【・・べき】ということは、それこそ【いうべきではない】と思っています。
また、本件、おそらく、統一見解として合意に至ることは(いくら、ここで議論を続けても)ないだろうと思いますし 何度も申し上げていますが、至らなくてもいいと思っています。意見交換でよろしいと思います。
自分はこう思う、あるいは自分はこうする でしょうね。 当然、ichinoseさんのように、実務経験の中で豊富かつ貴重ななノウハウを得ておられる人は、【啓蒙】も含めて、様々な面で われわれにアドバイスをされる。大変にありがたいことだと思います。
そういう中で、おぉ、これは、そのとおりだと(けっして、おっしゃっているコードレベルの深い話ではなく 自分が書いたコードが使われる環境での運用実態から)思えば、参考にさせていただき、コードに盛り込む。
幸か不幸か、私は、エクセルVBAとしては、万年初級の素人プログラマーで、商品として、提供する立場ではなく 職場(せいぜいある事業組織内であったり、範囲も、それぐらい)で、ちょこっと日常業務のための コードを書いてきただけです。
ただ、おそらく、ここ学校をはじめ、Q/Aサイトに質問をあげる人の立ち位置、そのコードが使われる範囲も それぐらいなんだろうと勝手に推測しています。で、それが、【普通の使い方】(言葉に反応されては困りますが)なんだろうと。
で、これも幸か不幸か、【その環境では】20年以上、ファイルが読めない という局面に遭遇したことはありません。 一方、参照するシートがない ということは、すくなからず発生しました。 操作者が、それを削除、あるいはシート名をかえる。簡単にできますので。
つまり、エクセルの世界の【普通の使い方】環境では、発生の可能性・頻度・リスクは、圧倒的に、シートがない という状況のほうが多い。
これも言葉尻をとらえるようなことにもなりますが
>>これだって 基本データというシートがないなどという事は仕様上あってはならない >>あるということは プログラム上に問題があるという判断になるならば、トラップはかけません。
ichinoseさんとβの発想の最も大きな相違はここかもしれません。 シートがないということは、プログラムのしようがどうこうといったことに全く関係なく、日常的におこりうる話で βの頭の中では、ファイルが(存在さえすれば)、読めないかもしれないという思いは皆無に近い、 一方、シートがないかもという思いは いつも、頭の中にあります。 仕様だからいいだという発想は持ちません。 もちろん、仕様は仕様ですから、それを守れ!というのは当然ですが、だから、そんな心配はしないんだという発想にはなりません。
申し上げたように、βは、基本、どちらにもエラートラップはかけません。 ただ、かけない(βとしての)理由は、この2つ(ファイルIOとシートの有無)では全く異なります。
前者は、【少なくとも自分のコードを必要としていた、あるいは、している】環境では【発生しない】。 (しかも、Q/Aサイトに質問があがるケースの環境は、それに近いと勝手に思ってますが) 後者は、【ばかもの!そんなことしちゃいけないだろ!】で済むからです(βのケースでは)
様々な先達のご意見を読ませていただき、自分のコードが働く環境で、そういったことが頻繁に発生すれば あぁ、なるほど と 取り入れればよろしいですし、βのケースのように20年以上発生しなければ、それはそれで 見守るだけでよろしいのではと思っています。
ITという領域でのβの立ち位置は2つ。
1つは、プログラム大好き人間、だけど、アセンブラー以外の言語の経験も知識もない、VBAだけはかろうじて マネグラミング で、おぉ!動いた。楽しいな と感動する立ち位置。
この立ち位置のβとしては、ichinoseさんが、おりにつけコメントされる、コードレベルの深い話に感激し 自分のコードでも、それを実装してみようと、ワクワクします。(おりにつけ、ichinoseさんが発信されるオブジェクトの抹消の件も含めて)
2つめは、半世紀近く、最初はプログラマ、ロートルになってからはITという業務というか事業をマネジメントしてきた立ち位置。 (つくる側、戦略として利用する側、あるいは、監査する側 期間が長いだけに、一貫はしていません)
この立ち位置のβとしては、たとえばプログラムをつくるといったケースで、ここのコードテクニックより先に心配すべきことは やまほどあるだろうという気持ちが強いです。やまほどあるなかの優先度がどうかという、これも絶対的なものではなく βとしての物差しですが、まず、その優先度の高い部分に、自分自身の100%の力を(あるいは配慮を)そそぎこんでもらいたい。 それができ、余力があれば、他のこともチャレンジしてほしい。(ここも、おりにつけ、ichinoseさんが発信されるオブジェクトの抹消の件も含めて)
ここを詳しく述べだすと、もうきりがないのですが、βの立ち位置からくるβの感覚としては たとえば、
https://www.excel.studio-kazu.jp/kw/20150910004904.html
この質問に対して、もちろん、1.の立ち位置としてのβは、なんとか、このパズルのような案件を処理するコードを書いてみたいと そう、ふっと思ったりもするんですが、2.の立ち位置のβは、いやいや、違う。コードの問題ではないだろ。問題はそこではないだろ。 こういったことが、もう気になって気になってしょうがありません。このスペックで業務を続けていくとすれば、プログラムは、それそのものが 目的ではなく、それを使って、最終的には事業が成功するということが目的ですから、いつか、とんでもない意思決定の誤りで、 甚大な損害がでる、それが気になって気になってしょうがありません。
もう1つ、本トピのきっかけになったトピ内でも記載しましたが別板の http://excelfactory.net/excelboard/excelvba/excel.cgi?mode=all&namber=171603&rev=0 投稿者/ β -(2015/09/06(10:24))
このあたりの価値観です。
本トピの元になったトピでいいますと、ファイルIOに対して、On Error をどうすべきかということ以前に、まず、質問者さんの現状のレベルとパワーのなかで それを100%注ぎ込んでほしい部分は、きちんとした、みやすいコードの流れの記述だと、そう思っています。 まず、そこに対してアドバイスを行い、質問者さんが、【普通の組み立て】ができるようにお手伝いをする。
ファイルIOの様々なリスクをどうするかは、その後の後の後ぐらいの優先度なんだろうとβは思っています。
(β) 2015/09/11(金) 07:20
βの価値観を1つ補足します。
Worksheet_Change で、Target.Value を平気で参照される人がいます。 一方、これでは具合が悪いということで Target(1).Value としてコードを書く人がいます。
前者は、もちろん、通常のエクセル操作の実態の元で実行時エラーになる確率はきわめて高い。 後者は、エラーの発生をおさえるとこができる。
この2つだけを比べた場合、βは、
前者に軍配をあげます。
後者はとんでもないことです。絶対に書いてほしくないコードです。
ichinoseさんがファイルIOのリスクが気になって気になってしょうがないというのと同じく βは、ここが気になって気になってしょうがありません。
もちろん、βは、いずれの書き方もしません。Intersectをとってえられた領域から For Each でセルを抜きだして処理しますが 2つだけをを比べた場合ということです。
コードを書く場合、その結果が、最終的な利用者(会社であったり)の意思決定に使われた結果、とんでもないことになるのを 絶対に防ぎたい、まず、そういう視点で書きたいと思っています。
そういう面に、自分自身が持っているパワーの100%をつぎ込んでいきたいと、そう思っています。
(β) 2015/09/11(金) 07:53
こう、βさんの投稿には色々勉強させていただき、尊敬してもおるのですが時々おしつけが過ぎるというか 1案件に対して、これもあると、あれもあるよ、これでもできるよとたくさん投稿されますよね?
これも言葉尻かな・・・ >本トピの元になったトピでいいますと、ファイルIOに対して、On Error をどうすべきかということ以前に、まず、質問者さんの現状のレベルとパワーのなかで >それを100%注ぎ込んでほしい部分は、きちんとした、みやすいコードの流れの記述だと、そう思っています。 >まず、そこに対してアドバイスを行い、質問者さんが、【普通の組み立て】ができるようにお手伝いをする。 というのであれば、On Error Resume Nextを使った、正しいOn Error ステートメントの使い方を お手伝いされたほうがいいのかなと・・・ それからコードにもコメントが少ないですよね。
見ている私からすれば勉強することはたくさんあって、とてもありがたいのですが!! 例えば先ほど上がった Worksheet_ChangeのIntersectの件も、なるほどなと思った次第です。
主題から離れてしまいましたが、おそらく同じ立場を歩んでいる私から見ていて思ったことです。 若輩者が失礼しました。 (稲葉) 2015/09/11(金) 09:45
下手な結論(言葉が悪くて済みません)を出されると困るので・・
On Error〜に限らず、どんなステートメントでも不適切に使えば問題が生じる、と私は思うだけです。
逆に、トラブルが生じなかったり、 生じたとしても想定の範囲内でよし(目的に叶っている)と判断できるなら 使ってトラぶろうが、使わないでトラぶろうが、何も問題はないと思います。
目的を達成できるなら、より簡潔なコードが良いコードになると思います。
実のところ、私もこの感覚を持っているんですが、 ↓ > エラーを発生させると、なんだか、エクセル君に、石を投げつけて痛めているような、そんな罪悪感(?)
他の回答者に On Errorで簡単に済ませられる案を見せられると、 「それで良かったのかぁ」「まてよ、何か問題がありはしないか?」とか考えます。
それで、問題の所在を指摘できなければ「そこで、On Errorを使うべきではない」とは言えないです。 ※本当は問題があるのに指摘できないなら、自分の力量不足を嘆くしかないです。 (実際は、力量不足自体にも気が付けないので、嘆くことも起きないですけど・・)
いずれにしても、プログラムの目的について、共通の認識がないと話が噛みあってこないと思います。
掲示板の回答用(それも実用なのか、教育用なのか)であったり、社内用であったり、社外用であったりとか。
議論の土俵が違ったら、各自の一人相撲でしかないです。
(半平太) 2015/09/11(金) 11:33
>>時々おしつけが過ぎるというか・・・・とたくさん投稿されますよね
御意!反省です。
>>というのであれば、On Error Resume Nextを使った、正しいOn Error ステートメントの使い方を お手伝いされたほうがいいのかなと・・・
そうですね。返す言葉もありません。反省。
>>それからコードにもコメントが少ないですよね。
ですね。横着ものなので。これも反省。 (質問者さんから要求があれば、追加説明はしているつもりですが)
それはそうと、Changeイベントの件は Intersect 手当てより、Target.Value での不具合発生と Target(1).Value での不具合回避を比べれば、不具合発生する前者が優れていますよといういうことを申し上げたつもりでした。
ところで、ファイル有無をチェックするためのコードとして、1粒で2度おいしい エラートラップ、 あぁ、これはいいなぁと、そう思ってはいるんですよ。次回からのβの回答コード案には、ずうずうしく エラートラップバージョンでコードを書いているかもしれません。 (しつこいようですが、2度目のおいしさを追及してではなく、コードとして簡単そうだから)
半平太さんがいわれるように、何をターゲットとして検討しているのかがずれてしまえば βも含めた一人相撲ですね。
(β) 2015/09/11(金) 12:00
> (質問者さんから要求があれば、追加説明はしているつもりですが) これはしっかり見ております! こちらをしっかり書かなかったこちらの落ち度です。 申し訳ない。 追加説明含め、今後も勉強させてください。 (稲葉) 2015/09/11(金) 12:27
サロンで見つけた on error の使い方 http://excelfactory.net/excelboard/excelvba/excel.cgi?mode=all&namber=171765&rev=0 ( ) 2015/09/11(金) 15:37
ここでの話題は、初心者コードが対象になってるわけではありませんよね? VBAにある程度精通している人たちが On Error statementについて夫々考え方を述べているのでしょう?
過去にこのようなスレがあり、その中で少々触れています。 [[20111215140611]]
On Error statementは、むやみに使用するものではないことは私も同感です。
が、他者のコードの On Error statement に異議を唱えるなら、突っ込まれるのを覚悟して 自身の回避策を提示してからにするべきだと思います。
(seiya) 2015/09/11(金) 16:40
>稲葉さん >FileI/Oを関数にするなら(引数をパスの文字列、戻り値をワークブックオブジェクト) 私は、提示したように エラーコードを戻り値 パラメータの返り値として ワークブックオブジェクト とエラーメッセージ文字列を付けました。On Errorステートメントの使い方は 大体こんなものですが、 インターフェースは、いろいろ思い描くものに違いがあると思います。
半平太さん、投稿ありがとうございます。
>下手な結論(言葉が悪くて済みません)を出されると困る どんな結論だと困ると 思っているのでしょうねえ??
>On Error〜に限らず、どんなステートメントでも不適切に使えば問題が生じる 確かにそうですね。その問題が生じても気が付きにくいことが 問題なのでしょう
>目的を達成できるなら On Errorは、File I/Oの方法として、機能的には良い方法(これ以上に良い方法をしらない)だと思うのですが・・・。
>> エラーを発生させると、なんだか、エクセル君に、石を投げつけて痛めているような、そんな罪悪感(?)
FileI/Oでは、実際に処理を行うのは、OSです。VBAは、OSに対して命令を出します。
FileI/O処理で処理が失敗しても OSがエラーになることは原則ありません、なったら、大変です。 VBAは、FileI/Oの失敗を受けたとき エラーコード等の設定をし、自らアボートします。
私は、使ったことがありませんが、ErrオブジェクトにはRaiseメソッドというのがありますよね!!
'Osに対しての処理 apiを使えば、同様の事が出来るのでしょうね if FileI/Oの処理を失敗 then Err.Raise 1004, , "これは駄目" end if
イメージとしてこのようにOSの処理失敗を受けて 自ら仕様(意思で)としてエラーを発生させている と解釈していますから、 >石を投げつけて痛めている という感覚には 私はなりません。
ただ、かつて、VBA質問箱でよく教えていただいた方の中にも 同じような事を言っていた方がいました。
私は 自分の作ったプログラムがエラーになってしまう方が私の心が痛みます。
>議論の土俵が違ったら、各自の一人相撲でしかないです。 この溝を埋める努力はしているつもりですが、これは 簡単ではないのですねえ。
私は、最後は ドキュメントとしてここに残れば 後で又 考えられるので これはこれで良いと思っています。
(ichinose ) 2015/09/12(土) 04:33
on error に限りませんが、理解して書いているならOK、理解していないならNGと
考えています。「あ痛!」となったときに修正が効きますから。成長ができるともいえる。
だから、理解したつもりだったが後になって本当には理解できていなかったと分かるのもOK。
本職のプログラマーならこんな悠長なことを言ってはいられないでしょうけれど。
FileI/Oでの on error〜 は普通だと思います。
エクセルから外に出たときと、ひとが関与する部分はどれほど用心してもしすぎということはありません。
> エラーを発生させると、なんだか、エクセル君に、石を投げつけて
> 痛めているような、そんな罪悪感(?)
わたしにはこの感覚はないです。
たとえば要素数10の配列があるとして、20番目の要素に値を格納しようとすると、
エクセル君は10と20を比較して20が大きいので「そりゃ無理ですって」と言ってきます。
ところが世の中には、この比較をせずに平気で20番目の要素に格納してしまう言語もあるのです。
厳密には、20番目の要素がもしあればそこに格納されるであろう計算上の位置、あ、メモリ上の
位置ですが。たまたまそこがあいていればたいした害はありませんが、すでになにか格納されていた
場合、既存の値を破壊してしまいます。
書き込んだ時点ではなにも起きませんが、あとで別の処理でそこの値を使おうとしたときに、
運が悪ければ、エラーも出ずにいきなりプログラムごと落ちてしまうこともあります。
不都合があるときはエラーを出して教えてくれる。高レベルの言語ってありがたいなぁとしみじみ思います。
戻りますが、数値Aと数値Bを比較して数値Bが大きければしかるべくエラー出す、そうでなければ
しかるべく格納する、エクセル君にとってはどちらもおなじ通常動作です。
( 佳 ) 2015/09/12(土) 07:33
>>下手な結論(言葉が悪くて済みません)を出されると困る >どんな結論だと困ると 思っているのでしょうねえ??
言葉が足らなくて済みません。
当初、ichinoseさんは「File I/Oの方法として」でテーマを限定されていた様に思うのですけど、 その時点では、私にはよく分からない内容なので、 「On Errorを使うと良いケースなんだな」位に読んでいたのですが、 話が、On Error 使用の是非全般に広がってきて、 下手すると、一般論的に「手段が尽きた時以外使うべきではない」とか「カジュアルにどんどん使うべし」とか 極端な結論になったら嫌だなと思った、と云う意味です。
多分、それ以外でも、嫌だなと思う結論も出るかも知れませんが、 そこまで先回りしてどんな結論なのか、考え出す想像力はありません。
> >On Error〜に限らず、どんなステートメントでも不適切に使えば問題が生じる > 確かにそうですね。その問題が生じても気が付きにくいことが 問題なのでしょう
その意味では、使用時には、より注意深さが求められるかも知れません。
> >エラーを発生させると、なんだか、エクセル君に、石を投げつけて痛めているような、そんな罪悪感(?) > FileI/Oでは、実際に処理を行うのは、OSです。VBAは、OSに対して命令を出します。 > FileI/O処理で処理が失敗しても OSがエラーになることは原則ありません、なったら、大変です。 > VBAは、FileI/Oの失敗を受けたとき エラーコード等の設定をし、自らアボートします。
この辺りの見極めが出来ないので、石なんか投げでご免なさいと云う気持ちが出るんでしょうね。
一般生活では、ドラ息子が、金持ちのオヤジに金の無心したとき、 「お前はバイト位はやったのか」「ハローワークに相談に行ったか」とか聞いて 借りようとするもの(ユーザー)が人の道としてやるべきことをやらずに来たとすると 金なんか出さない、なんて展開が考えられますが、 コンピュータの世界では、兎に角、金を借りに来たんなら、 いつでも気持ちよく出すぜって云うオヤジなんですね。
にしても、最後の護りにすぐ頼るのは、やっぱり抵抗感が残ります…不思議。
一方、先のレスでも書きましたが、「あれ、On Error噛ませれば、厄介な下準備は要らなかったのかぁ」と 思うことも(思わされたことも?)あるので、その辺りは自分の判断でどうするか決めたい・・・下手な結論を バイブル的(教条的)に押し付けられる様なことになったら困るなぁ。。。
(半平太) 2015/09/12(土) 17:48
佳さん、投稿ありがとうございます。 頂いた投稿がこのOn Errorステートメントの使用にあたり何らかの指針になれば良いと思っています。
半平太さん
>最後の護りにすぐ頼るのは、やっぱり抵抗感が残ります…不思議。
これは、私も持つ感想です。
特にあまり使った経験のないメソッド・プロパティ 又は、ステートメント これでエラーになったとき、全部のメソッド・プロパティを調べている時間がない、 はたまた、他ではあまり使わないから これで(On Error)よいと判断してしまいますよね、きっと!!
これを続けると、トラップを拾う方法以外にあることを知らずに過ぎてしまう可能性もでてきます。
名無しさんの(どうして名無しにするんですか? 一度、投稿してからわざわざ消しているんですか? 管理者さんの意向もあるので HN付けて投稿されては いかがですか?)
> サロンで見つけた on error の使い方 >http://excelfactory.net/excelboard/excelvba/excel.cgi?mode=all&namber=171765&rev=0 私は、ほとんど見ないサイトですが、
これは、こういう使い方はどうなんだ という問題提起という事なんでしょうか?
実は、私も
Sub test() Dim a As Double, b As Variant b = Application.InputBox("input number", , , , , , , 2) If TypeName(b) <> "Boolean" Then On Error Resume Next a = 1 / b If Err.Number <> 0 Then MsgBox Err.Description Else MsgBox "a = " & a End If On Error GoTo 0 End If End Sub
以前このようなコードを これだって 問題ない というような主旨の投稿をしたことがあります。
On Error ステートメントを使わない方がよいという意見に対する 挑発だったかもしれません。
上記コード 変数bの値によっては、オーバーフローエラーになりますから、
On Error ステートメントで良いのですが、問題にしていたのは 0で割算時のエラーだったのです。
むやみやたら の 定義を行うとなると 難しい問題ですよね!!
(ichinose ) 2015/09/13(日) 20:42
>上記コード 変数bの値によっては、オーバーフローエラーになりますから、 >On Error ステートメントで良いのですが、問題にしていたのは 0で割算時のエラーだったのです。 >むやみやたら の 定義を行うとなると 難しい問題ですよね!!
サンプルコードに対して、挑発された「むやみやらに使うな」派からは、どんな反応があったのでしょうか。 「文字じゃない且つ0じゃない」のチェックを入れるベキと云ったのでしょうか。 そこでichinoseさんが、まだまだだね(テニスの王子様)てなこと言って、やり込めたのでしょうか。
ichinoseさんの解説を読むと成程なぁと思いますが、 通常のコーディングでは「文字か」「0以外の数値か」迄は考えるでしょうが、 「極端に小さい値か」は、頭をかすめないであろうと思われます。少なくとも私は思い付かないです。
もし、そこまで心配すベキとすると、変数が分母になる計算は常にOn Errorを検討すべし なんて一般論になりそうで、またまた、困るなぁ・・・・
多分、それが心配なケースでは、絶対値の大きさが一定以上であること、なんて条件を 入れることになるのでしょうけど。
(半平太) 2015/09/13(日) 23:05
私の、少なくともファイルIOに関する現在のスタンス、1粒で2度おいしいし、コードもすっきり簡単だから もしかしたら、これからは、あつかましく エラートラップを使うかも ということは、何度も述べていますし、(しかも、考え方によっては1粒で4度ほどおいしいかも) もう1つのスタンス、 人それぞれの判断でいけばいいという、これも何度も申し上げたので、これ以上、特に発言することもないと思っていたんですが
なんだか、もやぁっとした感覚になっています。
上のほうで、Changeイベントを例に出して、実行時エラーを伴う Target.Value参照というバグコードと 実行時エラーにはならないけど、それ以上の大きな【業務遂行上のバグ】の可能性が高い Target(1).Value 記述によるエラー回避を比べると 処理が先に進まない、前者のほうが、圧倒的に優れているとコメントしました。
ファイルIOについても、そのあたりで、もやもや感がでてきました。
あるフォルダに特定ファイルがあったら消す。
Dir-->判定-->Kill 派 VS On Error Resume Next --> Kill --> OnError Goto 0 派
現在のβのスタンスからいえば、後者のほうが簡単(実際に気を付けて書かなければいけないコードは1行だけ)。
ただ・・・・
もちろん、このトピで 後者のコードをアップされた seiya さんは、このコードの危うさ、
>>他のユーザーが使用中であった場合の処理はどうするのか。 等を考慮する必要がなければ
を踏まえたうえでのアップで、でも、危ういから、そこはどうするかという議論に発展することを想定されたわけですが 質問がそれ以上発展しなかったと、そういうことですが、もし、あまり、そこまで考えの至らない人が、何も考えないまま On Error Resume Next --> Kill --> OnError Goto 0 で、もしファイルがなければ、消す必要はないし、あれば【確実に消える】と、 安心しきっていると、後日、そのファイルが消えていなかったことによる大きなビジネス上の損害がでて、大騒ぎになる可能性も。
ここを、DIR だけのチェックにしておけば、万が一、そのファイルが利用中であれば【確実に実行時エラーになって先に進まない】 このほうが安全なのかなぁと。
まぁ、そういったことも含めて、人それぞれの判断でケースバイケースで、おおいに使用していく、 あるいは使用をなるべくおさえていく、ということでいいんじゃないですか。
(β) 2015/09/14(月) 01:11
『特定のファイルが見つかったら削除する。』 これに対して、On Error statement で処理するコードを提示したものです。 これから発展して、その他の想定できるエラーの処理が必要なら、その都度コードを変更する。
という流れを期待していたのですがね。 どうも、話が On Error... が気に入る・気に入らないような話になってしまっているので、残念です。
とにかく、言いっぱなしはしないようにしてほしいですね。 (seiya) 2015/09/14(月) 01:59
βさんの意見聞いてると、dirも必要ないと思うのですが、、、 だって、エラーが出たほういいのですよね?
パスがあるかないかのデバッグで役に立つから? ではkillで出たエラーはアクセス制限?使用中?
いや、言ってることはよくわかっているます もしファイルが残っていたら、誤って使われてしまうほうがリスクですから! それをわかった上で、on errorと、dirをそれぞれ使った 回避策を互いに検討する所だと、私は思っていたのですけど 押しつけるのでなく、見た人が選べるような記録になればいいなぁ 自分じゃ書けないので、、、 (稲葉) 2015/09/14(月) 05:51
>>βさんの意見聞いてると、dirも必要ないと思うのですが、、、 >>だって、エラーが出たほういいのですよね?
様々なトピで目にする稲葉さんのバランス感覚に富んだコメントや、その懐の深さには常々、敬服していますが どうも・・・・・なんだかなぁ・・・
↑で引き合いに出した稲葉さんのコメントにコメントを返すのはやめますが、 本トピに限っては、どうも、なんだか、やりこめるためのコメントと感じてしまうところが・・・
・回答のアップに、あれもこれも、いくつもコード案を出しすぎ
とか
・コードにコメントがない
とか。
まぁ、これらは、βが、【コード以前にシステムづくりには大事なものがたくさんある】と偉そうなニュアンスで コメントしていることに対し、それをいうなら、そちらはどうなんだ というリアクションなんだろうとは思いますが。
もともとは、βの「にんじん嫌い」から始まり、いやいや、そういうものではない、にんじんには優れた栄養がある。 このように料理すればとてもおいしく食べられるから是非 というのが、このトピを立ち上げられた ichinoseさんの本意ですし コメントしましたように、先に引き合いにだしたトピでも、seiyaさんは、そういう方向に、議論を発展させて リスクを踏まえたうえで間違いのない使い方をしようという流れを想定されたわけです。
だけど、どうも・・・ なんだか、・・・疲れてきましたねぇ。
トピの目的はそうだったはずなのに、【俺が正しい合戦】というか【お前は間違っている合戦】になりつつあるような・・・
繰り返しになりますが、様々なことを把握した上で、それぞれの考えでシステムを構築していけばいい。 ただ、そのメリットデメリットに関しては、このような情報交換の場で、発信しあいながら共有していこう。 そういうことでいいですよね?
テーマとしては、Hook や (こちらのほうはβとしては今なお躊躇しますが)ichinoseさんが、おりにつけ発信されるVBIDE 等についても 『フグ料理』は怖い とか 好き嫌い ではなく、正しく、おいしい料理法を検討しあう場があってもいいですね。
βとしての現在の感覚は、まだなおニンジンはきらいなので、なるべく食べたくないなぁ、でも栄養もあるし ちゃんと食べて、健康な体をつくるというのも大事なことかも・・・ と考えだしているところです。
●seiyaさんのコメント、
>>とにかく、言いっぱなしはしないようにしてほしいですね
これは、私に向けられたコメントですか? ではないですよね?
もし、私に向けられたコメントで、ファイルIOに関してのOn Error を否定するなら、それ以上のものを説明してはどうか ということでしたら、コードとしてのファイルIO処理に関しては(繰り返しになりますが)その簡素さと効能両面で On Error にまさるものはないのだろうなと思っています。
ただ、(先に参照したトピ内で seiyaさんが示唆されたように)そのリスクもしっかりと踏まえたベースを整えたうえで つかわないと、しったかぶりでつかえばやけどするということと、 コードは面倒になりますが DIR で存在チェックをしておけば、そのあとの IO で発生するエラーは、実行時エラーで中断。 担当が呼び出されて対処しなければいけないですし、ここをエラートラップで、エラー番号を解析して適切なメッセージをだして処理をおわらせたとしても、 業務としては、うっちゃっておくことは許されず、やはり その場で担当が、その原因を究明して、対策をうつ必要があるということは 同じなので、ここは、いずれでもいいのかなと、そう思っています。
(β) 2015/09/14(月) 08:05
すみません、私も過ぎたことを言いました。 嫌いなにんじんでも、食べられるように調理する方法、 具体的なレシピ(コード)を考えれば(提示できれば)、 みんなにんじんきらいもなくなるのになぁ程度です。 粘着してるようでごめんなさい。
βさんが間違ってるとは思ってません。 βさんのレシピが見たかっただけなんです。
これで退散致します。
(稲葉) 2015/09/14(月) 08:23
>挑発された「むやみやらに使うな」派からは、どんな反応があったのでしょうか。 いいえ、何もありませんでした。 申し訳ないですが、テニスの王子様を知らないので 笑う箇所がわからないですが・・・。 [[20111215140611]]
直近で話題になっているここでも中途半端に終わっています。 これなんて、最後の方、seiyaさんがなだめるような記述をされています。 私、相当言い方が悪かったのでしょうかねえ・・・。
結果はわかりませんが、どこかできちんとした方がよいと思っていたことです。良い機会だと思っています。
>そこまで心配すベキとすると、変数が分母になる計算は常にOn Errorを検討すべし > なんて一般論になりそうで、またまた、困るなぁ・・・・
前投稿で提示したコードでは、Inpuboxメソッドにて 1E-309 と指定すれば、オーバフローになります。
テキストボックス等でも 1E-309と入力できることは 間違いなく、 数値として、このように入力出来てしまう事が エラーになってしまう可能性があるなら 対処しなければなりません。
IsnumericもTrueが返ります。文字列「E」の存在をチェックをしますかねえ??
100%本処理に向かいたいなら、事前に最低でもこのような内容には対処する方法を考えておき、 場合によっては、チェックルーチンを作成したり、規約として方法を纏めておくなどしておき、 実際には事前対処として、用意しておいたものを流用することで、簡単に処理できるようにしておくことは 重要ですよね!!。
では、手法として、この場合や名無しさんが問題提起された箇所のOn Errorの是非は?
私は、是だとダメ、非だとダメという結論が簡単には出せません。
、
(ichinose ) 2015/09/14(月) 08:47
Dir 〜 Kill ならば、Dirの箇所か、Kill の箇所でエラーで止まりますので、原因調査が楽ですし、短いコードで済みます。
これなんか、むやみやたらにOn Error、の例になるのではないでしょうか。
とはいえ、ファイルI/Oエラーを拾うためには、実行結果を返す戻り値が無いBasic言語では、On Errorは必須の命令です。
使い方次第ですし、どこまでエラーをケアする必要があるか、コードが使用される状況次第ですよね。
(???) 2015/09/14(月) 09:26
> これは、私に向けられたコメントですか? ではないですよね? 勿論、違います。 リンク先での話です。
実際にDir関数でOn Error statementを使用しない処理コードを提示してからの話にしてほしいということです。 (seiya) 2015/09/14(月) 09:32
(ファイルI/Oを想定すると)
使用しない派(と云う括りは、もう遅れた概念かも知れませんが)は、 トラブルの原因になりそうなチェックを先に済ませる。
しかし、全ての原因を調査できる保証はない。 その後の対応方針として、 (1)プログラムが止まるのは基本的に是とする。 (2)プログラムを止めたくない業務は、 上記チェック後に、(結局)On Errorを更に入れ、プログラムを止めなくさせ 何らかの方法で、結果が保証されないことをユーザーに告知する。 この場合、トラブルの原因特定作業はより難しくなるが、甘受する。
使用を躊躇しない派は、 事前チェックには限界があると認識の上、 トラブルの有無は、On Error で捕捉する。 Err.Numberで原因を調査し、ケースによって分岐処理する。 原因特定面で必要とあらば、STOPを掛けることも視野に入れる。
以上を総括すると、処理の質には大差がないものの、 エラーが生じたことさえ分かればいいケースでは、後者の方が圧倒的に簡潔に書けるし、 エラーの場合分けが必要なケースでも、面倒な事前チェックの代わりに、 Err.Number(またはErr.Description)が利用できるのできる分、後者の方がメリットが大きい。
(半平太) 2015/09/14(月) 19:18
>では、手法として、この場合や名無しさんが問題提起された箇所のOn Errorの是非は?
その件に関しましては、質問者が消費税5%の時代は旨く行っていたとのことなので、 On Error時は、無視して先に進めて良し、との仕様と解釈されるので「是」です。
・・と云うか、「非」になることはないと思います。 「非」と言いたくなることがあるとすれば、その後の処理が不適切なだけだと思います。 On Errorに罪はない。
現実に目を向けると、低レベル者が書くと「不適切な使用」だらけですし、 そうでない者でも、不用意に使うと「不適切な使用」に陥るリスクが高まる。 ・・・なので On Errorが悪者・・・みたいに見えるだけと思われます。
正にこれです。 ↓ > > >On Error〜に限らず、どんなステートメントでも不適切に使えば問題が生じる > > 確かにそうですね。その問題が生じても気が付きにくいことが 問題なのでしょう > その意味では、使用時には、より注意深さが求められるかも知れません。
(半平太) 2015/09/14(月) 20:05
>非」になることはないと思います。
オーバーフローを考慮すれば、これしかないですね・。 たら れば になりますが、これを無視するとしたら、どっちがわかりやすいか
処理の効率などは殆ど同じなので、同じ場合は、このわかりやすいかという判断も重要かとは 思いますけどね!!
FileI/OやSpecialCellsメソッドは、On Errorを使わないと正確に OK NGの判断が付かない
コレクションは、使わないという判断はあるが、使うとすれば、On Errorのほうが効率がよい
On Errorを使う方法、使わない方法それぞれあるとき、しかも処理能力に差異がない場合、 コードのわかりやすさで判断する。
最後のわかりやすさは、場合によっては、迷うことがありそうです。
この辺りで判断するという事ででょうか?
(ichinose ) 2015/09/16(水) 08:35
済みません。ちょっと切り口を変えさせていただきます。
一般的な是非論として、 プログラムの目的(βさんなら、仕事の目的かも)を達成しているかどうかで、 On Errorの使用/不使用、アベンドする/しない、に拘らず、その「是非」が判定できる。 是案が複数あるなら、より簡潔な方、効率の良い方、分かり易い方が好ましい。 でもいいですよね。
実は、昨日このトピックを始めから読み返しました。長ーいですね。理解度80%。
皆さんの意見を個々に読むと、おかしいと思うことはありませんでした。 にも拘らず、なぜ議論(らしきこと)が成立しているのか、不思議でした。
で思ったことは、目的(仕様)が違う・・のではないか・・と云うものです。
質問者対回答者においては、アドバイスと云う形で、いくらでも質問者の仕様を変えさせることができる。 しかし、回答者同士では、相手が良いと信じている仕様を変えさせるのは至難と云っていいでしょう。
ichinoseさんの仕様を摘要すると以下かと思います。(一部推測・一部邪推) ファイル I/O処理においては、エラーの原因は様々あるので、 Open命令を掛けてシステムサイドのチェックに委ね、 返ってきたエラー情報をOn Errorで捕捉してその後の分岐処理を行うことにしたい。
On Errorを使わないコードがどう云うものになるか分からないと比較しようがないが、総じて 始めから様々なエラー原因を個々に調べようとするより、簡潔、効率的であろう。 分かり易いかどうかは、微妙。
第一、全てのエラーを捕捉するには、On Errorを使う以外になかろう。
一般的な処理においては、On Errorはエラー発生が想定内の時に掛ける。 理論的には発生する個所でも、事情により発生するハズがないと判断される時は掛けない。 それでも発生したとすれば、アベンドするのは望むところである。
Collectionのキー不存在を確認するには、On Errorで簡潔、効率的に行える。
ユーザーによるINPUTBOXへの入力データで、数値の代わりに文字しかも 1E-309なんてものだった場合もOn Errorは有効である。 客先が "オーバーフロー"では戸惑があるので"文字は入力しないでください" としたいと言って来たらどうするかって? そう言う客はぶっ飛ばす。
※いやー、トピが長すぎるので、下の方に読み進むと、もう上の方の内容を忘れちゃうので 私が興味を持った部分を中心にまとめております。
・・で、ichinoseさんに反論するには、
(1)ichinoseさんの仕様に自己矛盾がある。
(2)社会常識に反している 自分の"マニアック"な常識・仕様ではない。 ※概念としては曖昧ですが、ichinoseさんに成程と思わせる思想。
と云った裏がないと出来ないのではないかと考えます。
因みに、私は反論できません。
あと、「むやみやたら」「分かり易さ」に少し関係するかも知れませんが、 ご提示いただいた、下記サンプルでさえOn Error Resume Next 〜 Goto 0の間があり過ぎる気が私にはします。 その間にまた別のエラーが生じているのに見逃しはしないかと神経を使うのは嫌だなぁと思います。 間に挟むのは、実質1行だけにしたい。(当然、Errオブジェクトのデータは抜け出す前に取り置かないと始まりません)
> Sub test() > Dim a As Double, b As Variant > b = Application.InputBox("input number", , , , , , , 2) > If TypeName(b) <> "Boolean" Then > On Error Resume Next > a = 1 / b > If Err.Number <> 0 Then > MsgBox Err.Description > Else > MsgBox "a = " & a > End If > On Error GoTo 0 > End If > End Sub
(半平太) 2015/09/17(木) 14:47
なんか、ichinoseさんの仕様だけ検討したきらいがありました。m(__)m
明日、βさんの分を中心に読み返してみます。
なんせボリュームがあるので、しんどいです (^_^;)
(半平太) 2015/09/17(木) 22:36
こっくさんのトピまで戻って読んでみました。 ↓ 『プログラム全体で実行するとうまくいかない』(こっくさん) [[20150903144924]]
今回、ichinoseさんの考え方は、かなり理解している心算ですが、 そこでの、このコメントの括弧書きには疑問を感じます。 ↓ > ただ、今回のこっくさんの不具合を発見するための手段として、On Error 〜ステートメントを外してみる > (代わりにDir関数で対応する)という試みは やる価値はあるかもしれません。 >( ichinose) 2015/09/04(金) 05:39
「ファイルI/Oの一例でオープン処理(Open Workbooks.Open Workbooks.Opentext etc)では、 必ず ファイルの有無のチェックを中で行っています。加えて、その他のエラーも 厳正にチェックされた後にオープン処理が行われます。」
と云う見解ですから、On Errorを外せば、それだけでDir関数以上の情報が得られるハズなので、 "代わりにDir関数で対応する" という試みは必要ないのでは?
βさんに気を使われた表現なのでしょうか?
ーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーー βさんのそこでの、このコメント ↓ >「商品」ととらえておられるご発言で、商品としては、後述のように、(ものによっては)理解していますが > 基本的には、むやみやたらの エラートラップ(バイパス)は使うべきではないと考えています。 >(β) 2015/09/04(金) 07:10
エラートラップを使う事は、即バイパス処理なのではなく、エラー情報を把握して、その後の処理を決めるのですから、 「むやみやたら」が初心者のやりそうな「関所フリーパス」と同義なら、批判は当たらないと思います。
> 確かに、ファイルそのものは存在するけど、様々な原因で読めない場合もあります。 > だけど、それを言い出せば、エクセルの世界です。たとえばセルにエラー値が(計算式の結果だけではなく > 外部データのコピペ、あるいは何かしらの障害も含めて)入っている場合があります。 > それを考えると、そのセルの参照にもエラートラップが必要になるという理屈になります。 > 等々、エクセルの世界ですから、あれも、これも エラーの可能性を含みます。その対処をするとなると
私も当初、「何でもかんで」エラートラップに掛けて、エラー原因に対処しなければいけなくなったら そりゃ困ると思ったのですが、ichinoseさんはそこまで言っていないハズです。
「ファイルI/O」と「コレクションの指定キー有無の判断」については、On Errorを使うべしと明言されていますが、 一般的には、想定しないエラー原因については、トラップ外でアベンドするのは望むところのハズです。
勿論、その後、想定に入れなければいけないエラーだったと判断されれば、何らかの対処版が作成される事になる。 その時、On Errorを使うか、個別原因対応版にするかは、また別の問題です(普通は後者となるでしょう・・)。
ーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーー
上で述べたこの個所が正に、両者のスタンスが同じでありながら、仕様に違いが出てくるところの様に思われます。 ↓ >しかし、一般的には、想定しないエラー原因については、トラップ外でアベンドするのは望むところのハズです。
すなわち、ファイル不存在以外の原因を想定エラーにするか否か。 ichinoseさんは想定する。 βさんさんは想定しない。アベンドは望むところであり、何度も起きる様なら、その時初めて対応版を考える。
それはそれでいいと思うのですが、 (ichinoseさんは、それでいいとは思わない旨発言されていますが、 (想定しなければならない体験が、20年間一度も無い環境に居た者に (想定すべしとは、私は言えません。
ですが、エラー原因を把握するのにβさんがOn Errorを使わない、と云う理由がはっきりしません。
On Errorでエラー発生を把握し、Err.Descriptionで不存在が原因と判明すれば、従来の処理をし、 その他なら「予期しないエラーが発生しました。これこれこれの情報を受付デスクまでメールで連絡願います」 なんてメッセージを出す、でよさそうに思えます。
実際、本トピにも、下記発言がありましたが、 「むやみやたらに使わないポリシー」をも加味しなければ、 「On Errorは使わない」と云う結論にはならないと思います。
> いずれにしても ファイルIOのチェックをするなら On Error 〜 が一番簡単でわかりやすく > かつ、コードの有効性も高いということには異論はありません。 > 違いは > ichinoseさんは、「だから」それを使う。 > βは ファイルが読めないことは想定しない。だから【使わない】 > それだけだと思ってますが? > >(β) 2015/09/06(日) 20:08
実のところ、ichinoseさんに啓発いただくまでは、私も同じようなポリシーを持っていました。
その発想の元は、 (1)エクセル君に、石を投げつけて 痛めているような、そんな罪悪感(?) (2)初心者ほどじゃないにしろ、誤用し易い。(別原因のエラーまで見逃す危険性を感じる→心得として「なるべく使わない」)
・・で、
(1)については、今回、勘違いと判明しました。
(2)については、ちょっと悩ましい。 心得としては立派と云えなくもありません。 少なくとも、一般生活では大人の振る舞いです。
何か旨い誤用防止策・対策があれば、この心得は捨てられます。
例えばですけど(今までも同じようなことを言っておりますが) (1) On ErrorとGoto 0 の間にいろんなステートメントを入れない。 (2) 運用上支障がないなら、Err.NumとErr.Descriptionをイミディエイトペインに吐き出させる。
<サンプル>
Sub test() Dim a As Double, b As Variant Dim erNum As Long, erDsp As String
b = Application.InputBox("input number", , , , , , , 2)
If TypeName(b) <> "Boolean" Then On Error GoTo ErrHandler a = 1 / b On Error GoTo 0
Select Case erNum Case 0 MsgBox "a = " & a Case 6, 13 MsgBox erNum & " " & erDsp Case Else MsgBox "想定外エラー! この状態のまま責任者に要連絡 内(1234)" Stop End Select End If
Exit Sub
ErrHandler: Debug.Print Err.Number, Err.Description erNum = Err.Number erDsp = Err.Description Resume Next End Sub
(半平太) 2015/09/18(金) 16:43
半平太さん
ご指導ありがとうございます。
何度か書いておりますが、コードとしてファイルIO対応をするなら On Error Resume Next が最も効率がよくコードも簡便だということは 異論がありません。かつ、ichinoseさんの本トピでのご教示を拝読しながら【故のないβのにんじん嫌い】も、少し薄まってきていて ファイルIO以外にも適切に使えば、むしろ健康体になれるなぁと、厚かましくも、最近、別板ですけど
On Error Resume Next .Columns(4).SpecialCells(xlCellTypeConstants).ClearContents On Error GoTo 0
こんな回答案をアップしましたし、学校でも、
On Error Resume Next .Range(.Cells(4, lastCol + 48), .Cells(Rows.Count, lastCol + 95)).SpecialCells(xlCellTypeConstants).ClearContents On Error GoTo 0
こんな回答案をアップしています。従来であれば、いったん エラートラップではさんだRangeオブジェクトに取得してNothingかどうかを 判定していたんですが、そうか、直接、こうやればできるし、コードも楽ちんだと。
なので、じゃぁすべてエラートラップかということになると、しばらくは躊躇しながら、よし、これはエラートラップで問題ないなと 考えながら使っていくとは思いますが、そのうちに【にんじん結構好きかも】になっている可能性もあるかなと。
で、使う場合には、やっぱり、気を付けて、コード運びには十分留意していこうと(当然ですが)思っていて、特に慣れていない人であれば その意味もわからず、なんとなく他のコードをコピペして使うのではなく、しっかりと理解して使えるようなお手伝いはしていかなきゃなぁと 思っています。
とくに、半平太さんが指摘されるように、エラートラップは、そのエラー発生対象コードの直前でかけ、直後に(Errオブジェクトを変数等に取得した後) すみやかに、リセット。これは、重要ですよね。へたに If 分岐で、コードをずらずら続けて書いて、ずっと下のほうで リセットなんていうコードでは リセットのタイミングが不適切になったり、最悪は(コードバグですけど)リセットしないで、そのまま続いていったり。
あと2点、考えています。
1.ichinoseさんが例に出された、たとえば多くのファイルを処理するJOBで、1つの例外で後続全処理を停止させるのではなく、 他の正常なファイル処理は継続して、やっつけてしまい、結果をだしておく。 これは、βも、大昔、汎用機の世界でやっておりました。特に大昔は、OS,ハードともに性能が悪く、今ならあっという間の処理が 一晩中かかる。そういった環境で、少しでも、できる処理は片づけておきたいということで、IBMアセンブラーの特権命令のSPIをかけて 当該のデータのみバイパス。
ただ、この場合、コード以前にシステム設計といますか、データ処理設計を入念に行い、あるデータの処理バイパスによって 後続のデータ処理に矛盾がでないように、いいかえれば、それぞれのデータ処理から、どのように相互依存をなくせるか、 ここがキモで、実際には(大昔のβの実績では)どこかしらに矛盾が発生して、結局は、エラー原因を解消したあと、全データで リラン ということも結構ありました。
だからだめということではなく、しっかりと設計を行ったうえで使っていくということが重要だなと、思っている次第です。
2.すでに、コメントした件ですが、エラートラップで原因を把握してメッセージをだす。で、管理者に連絡する旨、オペレータに指示。 つまり、その時点では、これ以上進めず、処理を中断して管理者による対応を待つケース。 これについては、エラートラップをかけずにアベンドさせて中断。ここは、もうオペレータとしては管理者による対応を待たずには 何もできないわけですね。 つまり、いずれも、中断して、管理者による対応を待つということでは同じ。
となると
1)万が一、オペレータが管理者に連絡せずに後続処理を行ったらやばいなぁ・・・アベンドの場合も基本は同じことですが エラートラップの場合は、オペレータが、なんとなく、アベンドじゃないような錯覚を持ってしまうようなことはないだろうか? 2)アベンドで中断していれば、その時のメモリーの状態はすべて保持されている。一方エラートラップで、処理としては正常に終了させた場合 障害発生時点のメモリー状態の再現が、必ずしもできないかも。そうすると、原因解明と対応策の実施という意味ではアベンドでの中断が 有利ではないか?
そんなあたりは、もやもやしていますが、しっかりと考えながらケースバイケースで処理構造をきめていくことが必要なんだろうなと思っています。
(β) 2015/09/18(金) 18:12
>半平太さん >ご指導ありがとうございます。
「ご指導」はご容赦願います。 (^_^;)
βさんの経験・技量・知識には、遠く及びません。
>オペレータが、なんとなく、アベンドじゃないような錯覚を持ってしまうようなことはないだろうか?
これについては、ichinoseさんがちょっと上で示された Raiseメソッドで代用すると云う訳には行かないでしょうか?
私のサンプルで言えば、
> Case Else > MsgBox "想定外エラー! この状態のまま責任者に要連絡 内(1234)" > Stop ↓ Case Else Err.Raise 999, , "致命的エラー! この状態のままにして責任者に連絡を入れてください 内(1234)"
(半平太) 2015/09/18(金) 22:29
自分の中では、ほとんど、ファイルIOにエラートラップをかけることに違和感はなくなっているはずなんですが もや〜っとした、何か小骨がのどに引っかかったような感覚がぬぐいきれない状態が残っていました。
で、自分自身の書いたレスをたどって、その小骨の正体がわかりました。 自分のレスのなかで何度か、【1粒で2度おいしい、その2度め】と書いていたところです。
あるシステムからデータが作成される、あるいは、誰かの業務(作業)の結果としてデータが作成される。 それを受けて処理するマクロでは、データ有無のチェックをいれるのが理にかなっていると思いますし、チェックを入れるなら 不完全なDIRより、有無以外のエラーもカバーするエラートラップのほうがいいし、またコードもシンプル。
ここなんです。有無以外にも様々な例外がある。有無は、その中の1つだけにすぎない んですよね。
で、通常の(何が通常かは横におき)ケース。
たとえば、ABCD.xlsm というマクロブックで自分のシート上で結果を作成していた処理があったとして 更新の繰り返しによるブック破損リスクの分散、あるいは マクロ配布の課題のクリアの観点からマクロブックと データブックを分離して、ABCD.xlsx と マクロブック の構成で処理した場合。
マクロブックでは ABCD.xlsx を読みこむわけですが、この場合にエラートラップを掛けるべきか、あるいは これは、かけなくてもいい範疇になるのか?
データ有無以外に障害リスクが様々あるとすれば、エラートラップを掛けるべきか?
たとえば、質問があがり、そこにコードが掲載されている。
・変数はすべて宣言しましょう。 ・Slect/Selectionではなく・・・という記述をしましょう。 ・A65536 といったマジックナンバーの使用はやめましょう。
こんなアドバイスをしてきましたが、ファイルIOに対して、エラートラップをアドバイス対象にすべきかどうか。
ここです。もやぁっとしているところは。ガイドラインというものがあればいいなぁと。 (β) 2015/09/23(水) 09:16
> ・変数はすべて宣言しましょう。 > ・Slect/Selectionではなく・・・という記述をしましょう。 > ・A65536 といったマジックナンバーの使用はやめましょう。 > > こんなアドバイスをしてきましたが、ファイルIOに対して、エラートラップをアドバイス対象にすべきかどうか。
例示のケースとファイルIOのケースとでは、かなりな距離を感じます。 例示のケースなら対策も簡単であり、プログラムの目的を云々する必要もないので、"気楽"ですよね。
ところが、I/Oに関しては、エラーだったらどうするんだと云う仕様が明確じゃないと、対策は示せないし、 質問者によっては、「そこまで考えて貰ってありがとう」から「そこまで考える必要はない。余計なお世話だ」まで、 反応が分かれそう。 ※まぁ、個人的にはA65536も余計なお世話だと思っていますが。。 さもマジックナンバー風だから仕方がないのでしょうか。 A60000とでもしておけば誰に文句も言われることはなかったでしょうに。 因みに、私はパーソナルユースではA500でやっています。
Kill ファイル ステートメントに関しては、以下のケースでエラーが出る様です。 (1)ファイルが無かった場合 (2)ファイルが使用中だった場合 (3)WindowsマシンでMacID関数を使った場合 → Kill MacID("TEXT")
このうち、 (1)は、目的に照らすと無害なので、プログラムを止めずに進行して差し支えないでしょうが、 (2)は、止めるか/止めずに進行させるかは、プログラムの目的によって決まってくると思われ、 気軽にアドバイスできるような事態ではないと考えられます。 (3)は、当然止めることになると思います。
無理に捻り出すとこんな案?
Sub test001() Dim erNum, erDsp Dim i As Long, kfnA, kfp As String
kfnA = Array("Af.xlsm", "Bf.xlsm", "Cf.xlsm")
For i = 0 To UBound(kfnA) kfp = ThisWorkbook.Path & "\" & kfnA(i) On Error Resume Next Kill kfp ’ Debug.Print Time(), Err.Number, Err.Description erNum = Err.Number erDsp = Err.Description On Error GoTo 0
Select Case erNum Case 53 '不存在=無害 'go ahead Case 70 ' 使用中=結果が保証されない MsgBox kfnA(i) & " は使用中です。→爾後、仕様に従う" Case Else '想定外エラー Err.Raise erNum, , erDsp & vbCr & vbCr & _ "この状態のまま責任者に連絡を入れてください 内(1234)" End Select Next
End Sub
仕様をキチンと聞き出し、且つそこで生じ得るエラーの種類を熟知している回答者だけが 的確にアドバイスできるのではないでしょうか?
I/Oに限らず、一般論としてOn Errorを活用する場合、On Errorでエラーを把握したはいいが、 関連ステートメントでどんな種類のエラーが生じ得るのか知らない者、 また、たとえ知っていてもどんな分岐処理をしたらいいか分からない者には、 「成り行きアベント」と大差ない方策(=プログラムを止める)しか残されていない。
ただ、エラー原因の事前調査が面倒なケースでは、それをやらないでいい分だけ楽ちん。 逆に、事前調査が簡単で、それ以外のケースはアベンドさせてよければ、On Errorの出番はない。 (上の方「2015/09/14(月) 19:18」で述べた内容と共通する話ですけども。。。)
(半平太) 2015/09/23(水) 23:14
なるほどです。
回答時のアドバイスなんて、おこがましいコメントをしましたが、それはやめて 自分(達)が使うために書くコードというポイントに限定すると、
・参照のみにブックを開くときは、読み取り専用、書きこむために開く場合はふつうにした上で、 ・その場合にファイル有無を調べる必要がある(と自分が思う)場合は、DIRじゃなくエラートラップ。 ・ここでエラーが返れば、ファイルがない(だろう)というエラーをErr.Raiseでアップして処理終了。 ・ファイル有無を調べる必要がない(だろうと自分が思う)場合は、何も仕掛けをしないで Workbooks.Open ・エラーの場合はVBAに任せてアベンド。
一応、この程度ではじめて、走りながら考えていこうかなと思案中です。
余談ですが、A65536 の件。実態としては、(状況によりますが)A500 でも A1000 でも、問題はない。 でも、実際に、これをコードに書いてアップしている質問者の多くは、その意味をしらないまま 最終行を求める場合は、A66536 と書いたものを使うんだと、たとえば A1 を指定する場合は Range("A1") と書くんだというのと 同じような感覚で理解しているように思えて、たまたま、実態としては結果オーライという、そこがちょっと気になります。
質問によっては、「現在6万件をこえるデータがあり、今後も増えていきます」なんてのがあって(そういうものはエクセルかよ〜?と思いますが) でも、コードの中で A65536 を使っている。
もちろん、ちゃんと「確信犯?」として A40000 といったように書いている人も中にはいますよね。
(β) 2015/09/24(木) 00:12
済みません。間違えました。m(__)m
下の方にCase Else があるので、そこでerNumが0が処理されちゃいます。
> 無理に捻り出すとこんな案? > : > Case 53 '不存在=無害 ↓ Case 0, 53 '存在=正常、不存在=無害
この辺で私はフェードアウトします。
(半平太) 2015/09/24(木) 08:27
On Errorステートメントに関する多種多様なご意見ありがとうございました。
だいぶ以前から、On Errorステートメントを使わないほうが良い という記述には、疑問を感じていました。
VBAでは、On Errorステートメントを使わないでは、止まらないプログラムは作れない、ならば、 On Errorステートメントは上手に使う事が肝要です。 そのためには、「On Errorステートメントを使わないほうが良い」という記述は、邪魔のように私には感じられました。 むしろ、使い方を勉強するべきだと思いました。
このスレッドが その事のきっかけになれば、本当に意味があると思います。
FileI/Oにしてもそのきっかけにすぎません。
VBA以前に使用していた言語では、OSに対して命令をだせる言語でしたので、 On Errorステートメントの類など使用しなくても FileI/Oでエラーになることはありませんでした。結果は、 リターンコードという形で返ってきましたので、そのコードを見て 処理を行っていたのです。Openなら、Open処理を実行し、その結果がリターンコードとして確認できるので、それによって、判断します。
言語が変わってもやっていることは同じです。 VBA(Basic)では、On Errorステートメントを使わないとこのリターンコードが拾えないから、使う ただ。それだけです。
確かに VBAのエラーオブジェクトには、物足りないところはありますよね!! エラーオブジェクトの充実を強く希望したいですね
私は、とりあえず エラー-コードとエラー表現(Description)辺りを返す方針でいますが、 半平太さんが記述されたOn Errorステートメントを1行ぐらいで挟むという方法は、 ひとつの指針でしょうね!!
FileI/Oのエラーを管理する新たなクラスでも作ることも検討されますが、結構大変そうです。
前投稿でも書きましたが、私は、プログラム設計は、トップダウンとボトムアップの併用型で行っています。 ボトムアップ法では、トップダウン法で作成されたプログラム構造を眺めて、 随所で使いそうな機能を洗い出します。 それをモジュール化(クラス化)することでどこでも簡単に使えそうな機能やインターフェースを考えます。 この時、今(対象のプログラム)は重要ではないが、他のプログラムで使う場合は、 こんな機能があると便利かなあ なんていう拡張性や将来運用等を考慮します。 いくらよい将来性や拡張性案でもそれの実現にかなりの時間がさかれそうだと判断した場合は、断念することもありますが、いけると判断すれば、その機能やインタフェースを追加します。
Filei/Oというのは、ボトムアップ法では 私の場合、速い段階で考え付くプログラム群の一つです。 その事象では、ファイルが見つからないということも原則あり得ないような事象でも 他のプログラムでも使える事等の将来性を考慮し、 それぞれの処理では、On Errorステートメントを使って処理を行います。
処理の是非の結果を呼び出したプログラムがどのように使うかは呼び出しプログラムに委ねられます。正常処理されない場合、その趣旨のメッセージをだし、 その場でプログラムを停止するという仕様もあるでしょうし、プログラムは、次の処理を淡々とこなすという仕様もあるでしょう。
大事なことは、FileI/Oでは、何が起こるかわからないという事を意識したコードにしてあるかということです。 この点において、On Errorステートメントより良い方法を知らないので使っています。 FileI/Oでのエラー処理を今の段階では、もう少し詳細な情報を取得できる工夫は必要です。 例えば、On Errorステートメントとファイル確認の併用等。
バグを放置する これは、完成品でないものを使っている という見解です。
個人で使うものだから、しょせんVBAだから・・、 今はそうでも将来は、自分以外の人に使われる可能性や将 来性があることを考慮すれば、それほど時間のかかる処理ではないのです。施しておくべきです。
ここで閉めたいと思います。
On Error 〜ステートメントは、必要です。 On Error 〜ステートメントは、上手に使うことです。
数週間、皆様の投稿に感謝いたします。
(ichinose) 2015/09/26(土) 09:00
>>ここで閉めたいと思います。
と、おっしゃっているのに、恐縮ですが。
>.大事なことは、FileI/Oでは、何が起こるかわからないという事を意識したコードにしてあるかということです。
これは、(今までもコメントしていますが)理解しています。 まぁ、これからβが書くコードのすべてのファイルIOに、これを適用するかどうかは、ちょっと断言できませんが。
>>バグを放置する これは、完成品でないものを使っている という見解です。
障害が想定されるのに対処していないということですよね。
よく出てくる質問に、複数ブックの複数シートの内容を取り込んで、1枚のシートに、縦に、あるいは横にまとめたい というのがありますね。
無制限にまとめていくと、理屈としては、どこかで、列が仕様制限を超える、行が仕様制限をこえる。
これも、想定される障害だとすれば、たとえ、今はプライベートユースでも、いずれはエンタープライズなものとして使われていくかもしれない、 ここでも転記前に、チェックを掛けるなり、あるいは、転記コードの前後に エラートラップを配置すべき
というのがichinoseさんの見解ということでしょうか。
(β) 2015/09/26(土) 17:35
>無制限にまとめていくと、理屈としては、どこかで、列が仕様制限を超える、行が仕様制限をこえる。 ExcelシートをDBのテーブルのように大量のデータを貯めるような仕様は、私は、実際にはあまり経験がありません。
VBAのやり始めの早い段階でシートをテーブルに見立てたような仕様の時は、リミットを設けていました。
当初から 5000行程度と決めました。 ですから、5000行を超える場合のチェックはしていましたよ!!
アクセスには、行数は制限はありませんが、2GBという容量制限があります。
無制限に追加していけば、エラーになりますよね?
テーブルに、データを追加もしくは、同じ構造のテーブルの統合・・、βさんが例としてあげているような ことは何度か(いえ、何度も)行ってきました。
前投稿でも既述したように ADOI/O関連モジュールというプログラム群みたいなものは、先に作成しますし、実際にあります。 これには、On Error ステートメントを使っています。
データを追加するSQLは、これらADOI/O関連プログラムに渡すのですから、エラーチェックは行っていることになりますよね!!。
仕様上起こりえるなら、何らかの施しはするでしょうね!!
>>これだって 基本データというシートがないなどという事は仕様上あってはならない >>あるということは プログラム上に問題があるという判断になるならば、トラップはかけません。 と記述しましたが、ブックの保護などをかけており、シートがないという対策をしているという前提です。 これでも起こりえるよという指摘があるなら、また考えるのでしょうね!!
(ichinose) 2015/09/28(月) 07:11
コメントありがとうございました。
今は、おかげさまで、ほとんど、ichinoseさんが提唱されてこられたことに、違和感はなくなっておりますし、 それを、どのように、しっかり理解した上で上手に利用するか、ということにつきると思います。 かりに 今は、プライベートユースでも、いずれ【昇進】や【栄転】で、エンタープライズなものに なっていくかもしれない、あるいはエンタープライズなものの部品として再利用されるかもしれない。 そういうポイントで、(少なくとも、超面倒にならなければ、あるいは超面倒なものは 外だしの汎用的なチェック・対処のプロシジャを準備して) できる限り、しっかりしたものにしておくべきだということですよね。
あえて、追加でコメントした意図は、それに異議を唱えるということではなく、どのあたりまでカバーしておくのが 妥当なのかなと、 現在、納品物として製品を開発しておられる、ichinose さんの会社では、そのあたりの目安があるかも ということで おききしました。
ありがとうございました。βとしては、(ichinoseさんがふれられたように、βもある意味、本トピの当事者ですので)本トピ終了です。
(β) 2015/09/28(月) 07:30
[ 一覧(最新更新順) ]
YukiWiki 1.6.7 Copyright (C) 2000,2001 by Hiroshi Yuki.
Modified by kazu.