Excel VBA 質問スレ Part52
■ このスレッドは過去ログ倉庫に格納されています
スレ立ての際は一行目に !extend:checked:vvvvv:1000:512 と入れてスレ立てして下さい ExcelのVBAに関する質問スレです コード書き込みや作成依頼もOK ※関連スレ VBAなんでも質問スレ Part2 http://mevius.2ch.net/test/read.cgi/tech/1432173164/ Access VBA 質問スレ Part1 http://mevius.2ch.net/test/read.cgi/tech/1328536426/ ※前スレ Excel VBA 質問スレ Part51 http://mevius.2ch.net/test/read.cgi/tech/1510107990/ 設計が悪いのは確かです。私がVBAド初心者の頃から改良・機能追加を繰り返したコードでぐちゃぐちゃしてます。Public変数だらけなんです。 まとめて初期化する方法はなさそうですね、残念ですがレスありがとうございました。 変数のスコープは短い方がメンテナンス性がいいと思います 変数の値なんて必要に応じてセットするもんだろ なんだよ「全変数初期化」って プログラミングの基礎からやりなおした方がいい >>88 が必要だと思ったから聞いてんだろw 何を訳のわからん事言ってんだコイツはw >>97 後からPublic変数を追加した場合、その"リセットするプログラム"も変更の必要ありますよね? Public変数を一括で初期化できるならいいんですが >>98 そうですよね。今更ですが実感しています。 >>99 そうです。変数の値を必要に応じてセットしたいんです。 必要な状態とは、変数宣言直後の値なんです。 できませんかね? >>100 一括で初期化するサブルーチンってできますか? >>102 それを検討する間にソースを書き換えた方が早そう。 それもできない程、巨大なモノならVBAの範疇を超えてると思う。 >>102 ひとこと言わせてもらうと そもそも「一括で全ての変数を初期化する」と言う 機能が必要になっている時点で多分構成的に 破綻していると思う。 変数を設定する前にどんなプログラムをどんな構成で組むのか、 そのためにはどんなライフサイクルの変数が必要で スコープはどうするのか 次回から考えて作るようにした方がいい。 もっとも、今そんなことを言っても 多分取り返しのつかないようなコードになっているだろうから とりあえず一つクラスを作っておいて 今パブリック変数として使っている変数を そのクラスのプロパティとして移してやって、 生成する際にコンストラクタで初期化してやったら どうだろうか。 条件によって初期値が変わるのならそのクラス内に そのためのメソッドを作ってもいいと思う。 あまりいい作りとは言えないけど そのクラスのインスタンスを モジュール変数にセットしておいて 初期化したいときは新たに生成したものを その変数に入れてやればとりあえずは 現状よりはもう少し手が入れやすくなると思う。 >>102 setvar, letvar, getvar, setarray, letarray, getarray みたいな共通関数を作りましょう。 型ごとに letstrvar, getintvar みたいなのも作っといたほうが便利かな。 まあ大きな関数5〜6個と小さな関数30個くらい実装すれば事足りるでしょう。 既存の g_FooBar, g_Baz みたいなPublic変数へのアクセスを全部 letstrvar("g_FooBar", local_str), local_int = getintvar("g_Baz") のような形式に書き換えましょう。 共通関数の中では、実際の値は巨大な配列に保持して、名前とインデックスを紐付けて管理しましょう。 こうすればグローバル変数の置換は機械的にできるし、変数の初期化も一発です。 健闘を祈ります。 >>103 >>104 レスありがとうございます。 このVBAコードは自分しか使わないし、自分以外がメンテすることはありえないので、 ろくにプログラミングの基本マナーなんてものを勉強しないまま、コード追加追加でわけわからなくなってしまったものです。 自分の書き込みから短時間で、お前のコードの設計が悪いんだとのレスを多く頂いて正直落ち込んでおります・・・ 薄々気付いてはいましたが、他の方から指摘されると流石にこたえました。 少しずつ改善していこうと思います。 その上で、>>104 をやっていこうと思います。 現時点で>>104 の内容は理解できていませんが、なんかすごい貴重なアドバイスなような気がします。参考にさせていただきます。ありがとうございました。 >>106 構成に関しては単一責任原則に沿って作れば 上達が早くなると思うので もし知らなければググってみた方がいいかも。 初期化というのは実はそこそこ奥の深い話で ファクトリーパターンやファクトリーメソッドとか ググると色々出てくるのだけれど かなりObject指向寄りの話になってしまうので ここではまぁ興味があればということで。 頑張ってくださいね。 >>102 > 後からPublic変数を追加した場合、その"リセットするプログラム"も変更の必要ありますよね? 変更すればいいじゃない そもそも今あるPublic変数はともかく今後もPublic変数バンバン増やしちゃるって思ってるの? j自分しか使わないってんなら現状使っているのはそのまま置いといて、いい機会だと 思って新たに作るぐらいの気持ちで、「少しずつ改善」なんて小手先のことやらずに1度徹底的 に作り直したほうが良い。 グローバル変数、「さっき設定した値を後でまた参照する」的な意味でどうしても必要になることは理解できるけど、 あったとして一つか二つがいいところ。 データセット的なものを配列使わず保持するならたくさん必要になるけど、さすがにそれは配列かコレクション使え。 毎回初期化が必要なグローバル変数があるならいっそ値をワークシートに保存して管理した方が安全だよな 誰か>>105 を巨大配列じゃなくてワークシートに記録するやり方で実装してください。 Worksheet_Calculateが頻繁に呼び出され、これが主な処理であるとき Public変数だらけになったことはある 初心者だから他にもっといい方法があったのかもしれんが でもみんなPublicな変数使ってて怖くない? 標準、あるいはクラスモジュール単位で 作られたメンバ変数が勝手に外部から 書き換えられちゃうんだよ? しかもブレークポイントにもかからないから いろんなところから頻繁に書き換えられたら 変な値が入っても犯人探しが大変だし。 前にも書いたけどプロパティにしたり 関数にしたりしてアクセサつけといた方がいいと思うよ。 読み取り専用にも出来るしね。 マクロの設計にもよるだろうけどグローバル変数使わずに大がかりなマクロ作ったことあるから、 絶対に避けられないというのは疑問だな。 だいたい初期化が必要な時点でグローバル変数にするのは不適当だし。 初心者あるあるだな 修正や追加を繰り返すうちにコードがスパゲッティになって、変数がどこで書き換えられたか追跡できなくなったもんだから特定のタイミングで一気に初期化したい すごい手間がかかってるから全面的な書き直しもやりたくない 保守性やモジュール化などの概念がわかってきたところで覚悟を決めて、設計からやり直した方が幸せになれるパターン おまえらにはグローバル変数は決して悪いものではないという事を知って欲しい コントロールのイベントプロシージャはプライベートっていう制約があるから シートと、ユーザーフォーム間のActiveXコントロールで変数の受け渡しをする場合は、パブリック変数を使用するしかいい方法がないと思う ワークシートはグローバル変数みたいなもん 印刷さえしなければたいがい便利に使える シートから直接取ればいいのでは?? 使い回すにしてもプライベート変数で事足りると思うけど。 パブリックにして読み書き自由にする必要はないんじゃないか? rangeオブジェクトを分割して2つのrangeオブジェクトにしたいのですがこれだと値がおかしいので解決方法教えてください Set obj = Range("a1:b100") set obj1 = obj.range(obj.cells(1,1),obj.cells(100,1)) set obj2 = obj.range(obj.cells(1,2),obj.cells(100,2)) >>122 範囲をセットしてるだけで 値をセットしているわけではない >>122 objに設定してるセルが存在するシートの参照式が省略されてるけど、シートはActiveSheetでいいの? あと、obj1とobj2には値を設定してるわけじゃないんだが、値がおかしいってどういうこと? vbaに関する質問か分からないけど、 他者が作成したexcel vbaマクロの中身をのぞこうとしたら、パスワードがかかって開けなかった。 どうやったらパスワードを特定できる? >>125 そんなもの他者が特定出来たらパスワードの意味がないだろう >>123-124 Set obj = Application.InputBox("2列選択", Type:=8) Set obj1 = obj.Range(obj.Cells(1, 1), obj.Cells(obj.Count / 2, 1)) Set obj2 = obj.Range(obj.Cells(1, 2), obj.Cells(obj.Count / 2, 2)) こんな風にした2列選択した後に1列毎のrangeオブジェクトに割り当てようとしています 試しにobj1.Selectとやって範囲を確認してみると関係のないセルが選択されてしまいました >>129 Set obj = Application.InputBox("2列選択", Type:=8) Set obj1 = obj.Worksheet.Range(obj.Cells(1, 1), obj.Cells(obj.Count / 2, 1)) Set obj2 = obj.Worksheet.Range(obj.Cells(1, 2), obj.Cells(obj.Count / 2, 2)) このほうが汎用的なのかな Set obj = Application.InputBox("2列選択", Type:=8) Set obj1 = Application.Range(obj.Cells(1, 1), obj.Cells(obj.Count / 2, 1)) Set obj2 = Application.Range(obj.Cells(1, 2), obj.Cells(obj.Count / 2, 2)) >>131 はだめか Application.Range プロパティ (Excel) >オブジェクト修飾子を指定せずにこのプロパティを使用すると、 ActiveSheet.Range のショートカットとなります。 >>130 でいいと思うけど、Range, Cellsの仕様がいまいち分からなかったから理論的になぜかは説明できない set obj = nothing は最後に入れるべきか、 ほったらかしでも問題無いのか どうなんだろう? 同じADOでもデルファイのクローズはナッシングだけど、VB系の場合はクローズだよねだいたい たしかクローズせずにナッシングするとクローズできなくなる >>129 例えばC1:D:4を選択した場合 1.obj.Rangeの中で更にobj.Cellsを指定するということは、C列目を基準にC列目を指定をしていることになる。 だからRangeの中にCellsを書く時はobj.Cellsではなく、obj.Worksheet.Cellsが良い。 2.obj1がC1:C4、obj2にD1:D4をセットしたいってことだよな? obj.Cells(obj.Count / 2, 1) この書き方だと行数を半分にしているのだがこれは一体・・・ もしC1:F10を指定した場合にC1:D10、E1:F10に分けたいわけじゃないの? 3.可読性が死ぬからRange使うのをやめよう。 範囲指定をする時はResize、Offsetを使ったほうが直感的です。 当初の質問を参考に2の通りだと仮定して Set obj1 = obj.Resize(, obj.Column / 2) Set obj2 = obj.Resize(, obj.Column / 2).Offset(,obj.Column / 2) Objには2列しか選択されない事が確定しているなら Set obj1 = obj.Resize(, 1) Set obj2 = obj.Resize(, 1).Offset(,1) >>136 1についての文献あれば教えて 2に関しては、2列選択中だから総セル数を2で割れば行数が算出できるってことやろ obj.Rows.Countのほうがいいとは思うけど >>137 >1についての文献 あ、想像で書いてるどっかのブログとかじゃなくて ちゃんとしたMicrosoftの公式文献ね 文献()読んでもわからんからこんな掃き溜めで質問しとんのやろw ややこしい奴やなあw >>140 俺は回答者側や(キリッ あのさあ〜お前羞恥心ないの?www ほったらかしで問題ない場合と そうでない場合があるので よくわからなければ明示的に Nothing入れといた方がいい >>138 > あ、想像で書いてるどっかのブログとかじゃなくて > ちゃんとしたMicrosoftの公式文献ね 偉そうにほざく前にMSDNぐらいググれ https://msdn.microsoft.com/ja-jp/library/cc408924.aspx >>143 どこにobj.Rangeの引数としてobj,Cellsが指定された場合の解釈のされ方が書いてあるの? どこみてもActiveSheet.Rangeの引数として使ってる例ばっかりだし、内部的にどういう形式に変換されてるのか全然読み取れないんだわ >>144 > 偉そうにほざく前にMSDNぐらいググれ って書いてあるのにバカすぎる URLは単なるヒントな まあ普通の理解力あればあれで充分だと思うが w >>145 煽りはいいから笑 結局探したけど見つけられなかったんだろ? 気持ちは分かるわ >>137 公式文献は知らないがそういうもんだと納得している。 想像()とか、こんなくらい知ってるわヴォケとか言われそうだが set ws = Worksheets(1)があると仮定して。 これはどれもRange("B2")への参照を示すわけだが ws.Cells(2,2) ws.Cells(2,2).Cells(1,1) ws.Cells(2,2).Range("A1") CellsとかRangeは同一ワークシート内であれば、いくらでも参照先を変更できる。 例えばこれはどれもRange("C3")への参照。 B2を基準(1,1)として(2,2)を見ていることになるからだ。 ws.Cells(2,2).Cells(2,2) ws.Cells(2,2).Range("B2") ws.Range(ws.Cells(2,2).Range("B2")) 問題の件はobjに既にRangeが格納されているからわかりづらいが Set obj = ws.Range("B2") の場合に obj.Range(ws.Cells(2,2)) というのは ws.Range("B2").Range(ws.Cells(2,2)) ってことだよね 単一セルの場合はws.Range("B2").Cells(2,2)と同一の結果になる。 範囲の場合も考え方は同じだがRange(a,b)でaとbに範囲を渡した場合 aとbを合わせた領域の最端の範囲を示すのは言わずもがな。 結局のところ何も難しい話ではなくて、Offsetの相対指定とResizeの範囲指定を同時にできるのがRangeだっていう話に戻る。 普段ws.Range("A1")と書いているものは、実はws.cells(1,1).Range("A1")の省略形だって考えて俺は納得した。 >>148 素直な君には正解を教えてあげる w For example, if the selection is cell C3, Selection.Cells(2, 2) returns cell D4 https://msdn.microsoft.com/en-us/vba/excel-vba/articles/range-item-property-excel >>148 ありがとう。 結局、想像でしかないけど、 obj.Range(obj.Cells(x, x), obj.Cells(x, x)) を独自関数 SelectionRangeCells(obj, obj.Cells(x, x), obj.Cells(x, x)) に置き換えると、こんな感じのことをしているのかね。 Function SelectionRangeCells(ByRef rng As Range, ByRef cell1 As Range, ByRef cell2 As Range) As Range Dim startRow As Long Dim endRow As Long Dim startColumn As Long Dim endColumn As Long startRow = WorksheetFunction.Min(cell1.Row, cell2.Row) endRow = WorksheetFunction.Max(cell1.Row, cell2.Row) startColumn = WorksheetFunction.Min(cell1.Column, cell2.Column) endColumn = WorksheetFunction.Max(cell1.Column, cell2.Column) Set SelectionRangeCells = rng.Resize(endRow - startRow + 1, endColumn - startColumn + 1).Offset(startRow - 1, startColumn - 1) End Function obj.Range("A1")という形式の場合は公式ドキュメントに明確に書かれてるね。 https://msdn.microsoft.com/ja-jp/vba/excel-vba/articles/range-range-property-excel obj.Range(obj.Cells(x, x), obj.Cells(x, x)) の場合はobj.Cells(x, x)が実体セルを指していてobjとの相対位置も分かってるわけだから Rangeプロパティ内で相対参照を算出する実装方法も考えられるしそのほうが使う側は分かりやすいはずだけど そう実装していないという明確な記述がどこにもなかったから実際に動かしてみての想像しかできなかった。 (ちなみに>>149 がなにやら勘違いしているようだがCells(x, x)【Cells._Default(x, x)】が何を返すかを知りたかったわけではない) >>150 > (ちなみに>>149 がなにやら勘違いしているようだがCells(x, x)【Cells._Default(x, x)】が何を返すかを知りたかったわけではない) その理解力で人を勘違い呼ばわりとか w >>151 俺からしたらおまえも>>150 と似たりよったりだからw まあ今回の件は僅差でお前の勝ちだけどw メゾット君も負けを認められるほど成長したかと感心したもののレス見ると何で勝利宣言してんのか分からなかった 今回の使い方は良いのか悪いのか ちょっと判断つきかねるなぁ 可読性がちょっとね 扱い方そのものは悪いとは思わない 書いてる人も気付いているか分からないけど やってることはデコレータパターンの それにかなり近いんだよね 洗練されればそれなりに便利かも知れないね タスクスケジューラで、Private Sub Workbook_Open()、の記述のあるブックを開くと、 Private Sub Workbook_Open()が実行されるときもあれば、実行されないときもある。 なんでなんだろ? その違いが今のところ掴めないでいるんですが、どなたか何か情報をお持ちではないですか? >>156 ユーザーがログオンしているときのみ実行する、にチェックが入っていますので違うと思います。 念のため、紹介してもらったサイトに従いフォルダを作成しましたがそれでも変わらずです。 他に情報ありましたらお知らせください。ありがとうございました。 >>155 実行されない時があるってことは実行される時もあるの? auto_open()も試してみてよ。 微妙に挙動が異なるので動くかも。 詳しくは違いでググって下さい あほやなあオープンマクロが動かんのはオープンしとらんからにきまっとるんやがw >>157 > 他に情報ありましたらお知らせください。ありがとうございました。 て言うかまずお前が情報書けよ > ユーザーがログオンしているときのみ実行する、にチェックが入っていますので違うと思います。 なんて後から言われても困る スケジュールで走らせるのが目的なら イベントにたよらなくても VBSからVBAのプロシージャを直で指定して呼べた気がする でVBSの方をスケジューラーに登録しておく ググれば方法が見つかると思うのでお試しあれ >>86 おまえはどんなショボいPCを使っているのか?Windows95でCPUは486DXか? >>161 COM経由で操作するなら似たようなもん へーsystemアカウント用のDesktopフォルダ作るのか なるほどね VBE起動したあとVBEのウィンドウを最大化するんだけど、 ブックを閉じてVBEを開くと小さいウィンドウで開くから、また最大化する・・・のが面倒なんですが、 前回終了時のウィンドウサイズを記憶させることってできます? vbaで正規表現書いていたのですが(?:)って使えないんでしょうか? キャプチャして欲しくない部分が結果に混ざるのでできれば最初から消しておきたいんですが 結果に対して処理するしかないんですかね >>167 いま手元に環境ないから試せないけどRegExp (Microsoft VBScript Regular Expressions 5.5)使ってるなら使えるはずだが... >>167 普通にキャプチャしてSubMachesで必要な部分だけ取り出せばいい 派遣先の親分がVBAの動作速度の改善のためにHDDをSSDに替えよう なんて言っているけれど VBAとSSDって動作速度の面で関係ありますか? これってSATA接続のHDD上のExcelファイルを編集するのとUSBメモリ(USB2.0接続)では速度差が出ると言っているようなものだろうけれど いかんせんUSBメモリはすべて捨ててしまって検証が出来ないです… >>173 派遣先の親分に対して何がしたくて、どういう裏付けのための「お言葉」を貰いに来たの? >>174 SSDには拘らずPC全てを新調する流れに誘導したいです ファイルを100個作って保存するとかなら効果あるかも? 誤差の範囲だと思うけど VBAの速度を上げるならCPUパワーとロジックに力をそそぐべき やっぱり、そう思いますよねー ちなみに10シート1000行50列をそれぞれ入力規則10種に違反しているものは無いかくまなく調べる… みたいなマクロなんですけど 開発者は条件分岐はとりあえずIf文をネストすれば良いみたいな考え方らしくSelect文がゼロなんですよ これってなんか工夫できそうな気がするんですけど まぁもうちょっと分析してみます たぶんループ内でセルアクセスしまくってるだろうから 一気に取ってきてメモリで処理して結果をズドン にすれば1000倍オーダーで高速化するでしょう 健闘をいのる とりあえず親分を立ててSSDにする ↓ ほとんど全て作業が快適になるけど肝心のマクロは変わらず ↓ やっぱりCPUか、でPC新調する ↓ もったいないので新PCにSSD載せる ↓ マクロも他の作業も爆速になる これしかないやろ >>179 セルアクセス!初耳にして今後のキーワードになりそうな言葉です! がんばります! >>180 そこまで景気よく行けたらいいなぁ… セル.valueを一旦全部配列に取り込んで メモリ上で処理して 配列を最後にまとめて貼り付けれ 画面描画とイベント抑止も追加な >>177 まぁIf文でもSelect文でも使いどころが見合ってればいいけど それより気を付けなければいけないのはOrやAndの扱いだね。 .Net系のOrElseやAndAlsoと違って前に設定した条件を満たしていても後続の条件を評価してしまう。 まだ単純な値を比較するなら誤差程度の時間差しか出ないけど 関数やメソッドの戻り値を条件にしていて、 その関数が重い処理であればそれだけ無駄な時間が掛かってしまう。 後はもしクラスに詳しければポリモーフィズムでなるべく無駄な分岐は避けた方が良いかな。 その分処理時間だけでなく改修やテストの時間も削減が見込める。 処理時間の高速化が目的なら セル範囲をバリアント型配列に代入して配列内で処理をしてから、処理後の結果を配列からセル範囲に出力すれば劇的に早くなるよ エクセルで家計簿作りたいんだけど、みんなどうしてるん? ものすっごい管理するのが面倒くさくなるんだけど… 家計簿を複式で書きたいから毎回商品名やら収入支出まで全部書くんだけど、 最近面倒臭くなってきたからこの辺の操作を簡略化したいのよ 商品名を全部書くのが面倒くさいから大雑把な商品項目を作って 商品項目の選択制にしたいんだけどどうすりゃいいんだ? 例えば、お茶買ったとしたら、飲み物って項目を選択して 収入支出のどっちかを選択して金額を入れればいいだけ、みたいな あと、飲み物のグループだけを表示できるようにしたりもしたい 自分がその月に何にお金を一番使ってるのか項目毎に分析したいんだ ユーザーフォーム作って選択すればいいんじゃね? あとはKey に該当する商品だけ合計するとかでおk >>185 セルを選択形式にしたいなら「入力規制」でググれ だがその項目を決まった位置に書いておけば それすら必要ないように思える。 多分VBA必要ない。 >>185 > 自分がその月に何にお金を一番使ってるのか項目毎に分析したいんだ スレチになるけどそう言う目的あるならフリーの家計簿ソフト使った方がいいと思うよ フリーの家計簿ソフトなんて作ってるのはみんな>>185 みたいな奴だぜ オレオレ便利機能が欲しいから作っちゃいましたみたいな 家計簿なんてそんなもん 作れるなら自分で作っちゃうアプローチのが正解 さらにスレチだけど、そもそも複式で書く意味って? 支出と費用計上のタイミングがずれる 収入と収益計上のタイミングがずれる なんてことがある場合に複式の利点があるんだろうけど そもそも家計簿作る人ってそこまでやんの? 米を10kg買ってきました 資産計上します 消費の都度費用計上しますとか? 家計簿の無料アプリなんて、いくらでもあるだろ 既にあるものは作るな。 世の中に無いものを作れ ++や+=や?みたいな演算子ないんでしょうか? 毎回代入やifしないと駄目? ■ このスレッドは過去ログ倉庫に格納されています
read.cgi ver 07.5.5 2024/06/08 Walang Kapalit ★ | Donguri System Team 5ちゃんねる