Excel VBA質問箱 IV

当質問箱は、有志のボランティア精神のおかげで成り立っています。
問題が解決したら、必ずお礼をしましょうね。
本サイトの基本方針をまとめました。こちら をご一読ください。

投稿種別の選択が必要です。ご注意ください。
迷惑投稿防止のため、URLの入力を制限しています。ご了承ください。


2330 / 13646 ツリー ←次へ | 前へ→

【68618】動的配列の初期化について Tacchi 11/3/30(水) 14:11 質問[未読]
【68619】Re:動的配列の初期化について Tacchi 11/3/30(水) 14:31 質問[未読]
【68620】Re:動的配列の初期化について momo 11/3/30(水) 15:13 発言[未読]
【68657】Re:動的配列の初期化について Tacchi 11/4/3(日) 15:35 質問[未読]
【68695】Re:動的配列の初期化について momo 11/4/7(木) 10:13 発言[未読]
【68623】Re:動的配列の初期化について neptune 11/3/30(水) 16:24 回答[未読]
【68626】Re:動的配列の初期化について neptune 11/3/30(水) 23:25 回答[未読]
【68658】Re:動的配列の初期化について Tacchi 11/4/3(日) 15:39 お礼[未読]
【68627】Re:動的配列の初期化について 角田 11/3/31(木) 0:31 回答[未読]
【68659】Re:動的配列の初期化について Tacchi 11/4/3(日) 15:43 お礼[未読]
【68628】Re:動的配列の初期化について Yuki 11/3/31(木) 10:08 発言[未読]
【68662】Re:動的配列の初期化について Tacchi 11/4/3(日) 16:00 お礼[未読]
【68634】Re:動的配列の初期化について UO3 11/3/31(木) 13:46 発言[未読]
【68660】Re:動的配列の初期化について Tacchi 11/4/3(日) 15:50 お礼[未読]
【68640】Re:動的配列の初期化について Hirofumi 11/3/31(木) 18:36 回答[未読]
【68642】Re:動的配列の初期化について Hirofumi 11/3/31(木) 20:29 回答[未読]
【68661】Re:動的配列の初期化について Tacchi 11/4/3(日) 15:58 お礼[未読]
【68696】解決済みですが。 Jaka 11/4/7(木) 12:36 発言[未読]
【68697】かぶっていたけど、まいっか。 Jaka 11/4/7(木) 12:50 発言[未読]

【68618】動的配列の初期化について
質問  Tacchi  - 11/3/30(水) 14:11 -

引用なし
パスワード
   どうしても配列のところでエラーがでてしまい
うまくいかなくて困ってます

エラー内容は
インデックスが有効範囲にありません

というものなのですが、

マクロの流れとしましては、
Loopしている中で、配列を使用しています
その為、データ数分Loopするのですが、
処理がループ内で回る毎に、動的配列を初期化させて、新たな値を動的配列に格納していこうとしています・・・が、途中でエラーになってしまいます

現在は、エラー回避で、エラーになったら飛ばそうとして設定してみたところ、
一回はエラー回避で飛ばせるものの、2回以降、エラーが発生すると、
エラー回避をしてくれません

どうしたら、うまくいくのでしょうか?

できれば、エラー回避での解決ではなく、
その動的配列が初期化されていたら、処理をする
初期化されていない場合は、処理をしない

といった形が理想なのです

どうかよろしくおねがいします

現在のコードは下記の通りです

Dim MyLN As Long
Dim MaxR As Long, 記録R As Long
Dim STAGE As Object
Dim MyKey
Dim MyString
Dim MySTAGE
Dim strFile As String
Dim MyFile As String
Dim MyVal, MyVal2
Dim lCnt As Long
Dim MyData()

'使用原紙の保管場所
strFile = "記録表"
MyFile = "C:\Temp\" & strFile & ".xls"


Set STAGE = CreateObject("scripting.dictionary")

MyLN = 50

With Sheets(1)
MaxR = .Range("Y" & Rows.Count).End(xlUp).Row

 For i = 3 To MaxR
  If Not .Cells(i, "Y").Value = Empty Then
   If Not STAGE.exists(.Cells(i, "Y").Value) Then
     STAGE.Add .Cells(i, "Y").Value, ""
   End If
  End If
 Next i
End With

'記録表ファイルを開く
If Dir(MyFile) <> "" Then
  Workbooks.Open MyFile
 
  MyKey = STAGE.keys

  For i = 0 To UBound(MyKey)
   MySTAGE = MyKey(i)
   
   Call シート挿入(MySTAGE)
  
   z = 0
   With ThisWorkbook.Sheets(1)
     For q = 3 To MaxR
      If .Cells(q, "Y").Value = MySTAGE Then
       If .Cells(q, "I").Value <> "" And .Cells(q, "J").Value <> "" Then
         If .Cells(q, "I").Value <= MyLN And .Cells(q, "J").Value >= MyLN Then
          MyVal = Array(.Cells(q, "N").Value)
          For x = 15 To 24
           lCnt = UBound(MyVal) + 1
           ReDim Preserve MyVal(lCnt)
           MyVal(lCnt) = .Cells(q, x).Value
          Next x
          ReDim Preserve MyData(z)
          MyData(z) = MyVal
          z = z + 1

         End If
       End If
      End If
     Next q
   End With

  On Error GoTo MyErr
    '↓ココでエラー発生(MyDataに値がないとき)
    For e = 0 To UBound(MyData)
     記録R = Range("C" & Rows.Count).End(xlUp).Row + 1

         Cells(記録R, "C").Value = (MyData(e)(0))
         Cells(記録R, "G").Value = (MyData(e)(1))
         Cells(記録R, "K").Value = (MyData(e)(2))
         Cells(記録R, "N").Value = (MyData(e)(3))
         Cells(記録R, "Q").Value = (MyData(e)(4))
         Cells(記録R, "U").Value = (MyData(e)(5))
         Cells(記録R, "V").Value = (MyData(e)(6))
         Cells(記録R, "Y").Value = (MyData(e)(7))
         Cells(記録R, "AB").Value = (MyData(e)(8))
         Cells(記録R, "AE").Value = (MyData(e)(9))
         Cells(記録R, "AH").Value = (MyData(e)(10))

       記録R = 記録R + 1
    Next e
     Erase MyData ’←初期化

MyErr:
   On Error GoTo 0
  Next i

Else
 MsgBox "指定ファイルが見つからない為、処理を終了します"
End If

【68619】Re:動的配列の初期化について
質問  Tacchi  - 11/3/30(水) 14:31 -

引用なし
パスワード
   すみません・・・

付け加えます
指定回数Loopさせるのですが、
中には、検索した結果、条件にあてはまるデータが無い為、0件となったときに、
動的配列には何も入っていない状態になってしまう為、

そのときにエラーが発生してしまいます
ココの部分です↓
For e = 0 To UBound(MyData)

どうかよろしくお願いします

【68620】Re:動的配列の初期化について
発言  momo  - 11/3/30(水) 15:13 -

引用なし
パスワード
   ▼Tacchi さん:

最初に最大数のMaxR分で配列を確保しておいて
最後にデータがあった分のz-1の大きさに変えてあげたらどうでしょう?

ReDim MyData(MaxR)
z = 0
For q = 3 To MaxR
  ・
  ・
 MyData(z) = MyVal
 z = z + 1
  ・
  ・
Next q
ReDim Preserve MyData(z - 1)

データがあったかどうかは

Ubound(MyData)をする前に
If z > 0 then なんかで判定できますね。

【68623】Re:動的配列の初期化について
回答  neptune  - 11/3/30(水) 16:24 -

引用なし
パスワード
   ▼Tacchi さん:

>できれば、エラー回避での解決ではなく、
エラーが出るのはVBの仕様です。

理由とその対応策を書いてくれていますので参考にして下さい。
ht tp://yaplog.jp/orator/archive/43

【68626】Re:動的配列の初期化について
回答  neptune  - 11/3/30(水) 23:25 -

引用なし
パスワード
   ▼neptune さん:
さっき、ふと、思いついたんですが、
条件を満足した時だけ、再定義してますが、満足しない場合は
最少要素数で再定義しておけばどうですか?
redim 配列(0)

後での判定の時uboundで0が返ってくればOKか、0の時もあるなら
そのデータで判断するようにすれば簡単ですね。

【68627】Re:動的配列の初期化について
回答  角田  - 11/3/31(木) 0:31 -

引用なし
パスワード
   > On Error GoTo MyErr
> For e = 0 To UBound(MyData)
>
> MyErr:
>  On Error GoTo 0
> Next i

[On Error GoTo xxx]でエラー処理ルーチンを開始した後に、
エラー処理ルーチンを抜ける命令は[On Error GoTo 0]ではありません。
それではエラー処理ルーチンは抜け出せません。
その後も、ず〜っとエラー処理ルーチンの『中』です。

エラー処理ルーチン実行中には「更なるエラー処理ルーチンの実行」は
行なわれません。その為
> 一回はエラー回避で飛ばせるものの、2回以降、エラーが発生すると、
> エラー回避をしてくれません
となります。

[On Error GoTo xxx]でエラー処理ルーチンを開始した後に
エラー処理ルーチンを抜ける命令は(頭に On Error が付かない)[Resume xxx]です。


こういう時のエラー対処は、普通は下記のようにします。
j = -1
On Error Resume Next
j = UBound(MyData)
On Error GoTo 0
If (j <> -1) Then
 For e = 0 To j
  (中略)
 Next e
 Erase MyData
End If

【68628】Re:動的配列の初期化について
発言  Yuki  - 11/3/31(木) 10:08 -

引用なし
パスワード
   ▼Tacchi さん:
こんにちは。
追加の行(2行)を追加してみてはどうですか。

>どうしても配列のところでエラーがでてしまい
>うまくいかなくて困ってます
>
>エラー内容は
>インデックスが有効範囲にありません
>
>      '↓ココでエラー発生(MyDataに値がないとき)
      if sgn(mydata) <> 0 then     追加
>        For e = 0 To UBound(MyData)
        

        next
      end if              追加

【68634】Re:動的配列の初期化について
発言  UO3  - 11/3/31(木) 13:46 -

引用なし
パスワード
   ▼Tacchi さん:

こんにちは

横から失礼します。
錚々たるエキスパートの方々からの回答もあり、私なんぞのでる幕ではありませんが

・まず、動的配列を規定したかどうかの判断を行いたい場合、一番簡単な方法は
 配列にセットするループの最初でブール型の変数あたりをFalseにしておき、RedimかけたらTrue。
 配列から取り出すループの前に、この変数の値を判定する。

・そうではなく、どうしても配列が初期化されているかどうかの判定をしたいということなら
 以下のコードは、以前に別のところでyukiさんに紹介いただいたもの(と記憶)ですが。
 判定プロシジャ GetDim は本来は次元数を取得するものですが、ここでの戻り値が 0 なら
 動的配列だけど初期化されていないということになります。
 あるいは今回の場合は1次元配列ですから戻り値が 1 かどうかの判定のほうがいいかもですね。
 (2003で検証済み。2007以降では未検証です)

・いずれにしても【めったやたらに】エラートラップを使うのはどうなんでしょうか。
 本来のバグまでもが隠されてしまいますよね。

Option Explicit

Private Declare Function SafeArrayGetDim Lib "oleaut32.dll" (ByVal psa As Long) As Long
Private Declare Sub GetMem4 Lib "msvbvm60" (ByVal ptr As Long, ByRef ret As Long)

Sub Test()
  Dim pas As Long
  
  Dim a As Variant
  ReDim a(2, 2)      '2次元動的配列
  
  Dim b As Variant    '配列ではない
  
  Dim c() As Variant
  ReDim c(2, 2, 2)    '3次元動的配列
  
  Dim d() As Variant   '初期化されていない配列
  
  Dim e(1) As Variant   '1次元静的配列
  
  Dim f(2, 3) As Variant '2次元静的配列
  
  MsgBox "a:" & GetDIm(a)
  MsgBox "b:" & GetDIm(b)
  MsgBox "c:" & GetDIm(c)
  MsgBox "d:" & GetDIm(d)
  MsgBox "e:" & GetDIm(e)
  MsgBox "f:" & GetDIm(f)

End Sub

Private Function GetDIm(ByVal sa As Variant) As Long
  Dim p As Long
  
  If IsArray(sa) Then
    GetMem4 VarPtr(sa) + 8, p
    If p = 0 Then
      GetDIm = 0 '初期化されていない配列
    Else
      GetDIm = SafeArrayGetDim(p) '配列については次元数を返す
    End If
  Else
    GetDIm = -1 '配列ではない
  End If
  
End Function

 

【68640】Re:動的配列の初期化について
回答  Hirofumi  - 11/3/31(木) 18:36 -

引用なし
パスワード
   配列のIndex最大値をzで表しているのだから
こんなんでも善いのでは?

Option Explicit

Public Sub Test()

  Dim i As Long
  Dim z As Long
  Dim q As Long
  Dim x As Long
  Dim e As Long
  
  Dim MyLN As Long
  Dim MaxR As Long, 記録R As Long
  Dim STAGE As Object
  Dim MyKey
  Dim MyString
  Dim MySTAGE
  Dim strFile As String
  Dim MyFile As String
  Dim MyVal, MyVal2
  Dim lCnt As Long
'  Dim MyData()
  Dim MyData() As Variant
  
  '使用原紙の保管場所
  strFile = "記録表"
  MyFile = "C:\Temp\" & strFile & ".xls"

  Set STAGE = CreateObject("scripting.dictionary")
  
  MyLN = 50
  
  With Sheets(1)
    MaxR = .Range("Y" & Rows.Count).End(xlUp).Row
    For i = 3 To MaxR
'      If Not .Cells(i, "Y").Value = Empty Then
      If Not IsEmpty(.Cells(i, "Y").Value) Then
        If Not STAGE.exists(.Cells(i, "Y").Value) Then
          STAGE.Add .Cells(i, "Y").Value, ""
        End If
      End If
    Next i
  End With
  
  '記録表ファイルを開く
  If Dir(MyFile) <> "" Then
    Workbooks.Open MyFile
    MyKey = STAGE.keys
    For i = 0 To UBound(MyKey)
      MySTAGE = MyKey(i)
      Call シート挿入(MySTAGE)
'      z = 0
      With ThisWorkbook.Sheets(1)
        For q = 3 To MaxR
          If .Cells(q, "Y").Value = MySTAGE Then
            If .Cells(q, "I").Value <> "" And .Cells(q, "J").Value <> "" Then
              If .Cells(q, "I").Value <= MyLN And .Cells(q, "J").Value >= MyLN Then
                MyVal = Array(.Cells(q, "N").Value)
                For x = 15 To 24
                  lCnt = UBound(MyVal) + 1
                  ReDim Preserve MyVal(lCnt)
                  MyVal(lCnt) = .Cells(q, x).Value
                Next x
                z = z + 1
                ReDim Preserve MyData(z)
                MyData(z) = MyVal
'                z = z + 1
              End If
            End If
          End If
        Next q
      End With

'      On Error GoTo MyErr
      '↓ココでエラー発生(MyDataに値がないとき)
'      For e = 0 To UBound(MyData)
      For e = 0 To z 'zが-1なら(MyDataに値がないとき)Forは回らない
        記録R = Range("C" & Rows.Count).End(xlUp).Row + 1
        Cells(記録R, "C").Value = (MyData(e)(0))
        Cells(記録R, "G").Value = (MyData(e)(1))
        Cells(記録R, "K").Value = (MyData(e)(2))
        Cells(記録R, "N").Value = (MyData(e)(3))
        Cells(記録R, "Q").Value = (MyData(e)(4))
        Cells(記録R, "U").Value = (MyData(e)(5))
        Cells(記録R, "V").Value = (MyData(e)(6))
        Cells(記録R, "Y").Value = (MyData(e)(7))
        Cells(記録R, "AB").Value = (MyData(e)(8))
        Cells(記録R, "AE").Value = (MyData(e)(9))
        Cells(記録R, "AH").Value = (MyData(e)(10))
        記録R = 記録R + 1
      Next e
      Erase MyData '←初期化
      z = -1
'MyErr:

'      On Error GoTo 0
    Next i
  Else
    MsgBox "指定ファイルが見つからない為、処理を終了します"
  End If

End Sub

【68642】Re:動的配列の初期化について
回答  Hirofumi  - 11/3/31(木) 20:29 -

引用なし
パスワード
   変数zの初期化が、もう一つ必要でした

  '記録表ファイルを開く
  If Dir(MyFile) <> "" Then
    Workbooks.Open MyFile
    MyKey = STAGE.keys
     z = -1 'ここいら辺にもう一つ必要
    For i = 0 To UBound(MyKey)
      MySTAGE = MyKey(i)

【68657】Re:動的配列の初期化について
質問  Tacchi  - 11/4/3(日) 15:35 -

引用なし
パスワード
   ▼momo さん:

アドバイスありがとうございます
配列について、まだまだ勉強中ですが、
配列の最大数について質問ですが、

たとえば、10個を最大数として、配列の要素数を決めていて、
実際は、8個しか要素数がなかったとしたら、
残り2個分の要素数に対しては、何も問題がないのでしょうか?


>▼Tacchi さん:
>
>最初に最大数のMaxR分で配列を確保しておいて
>最後にデータがあった分のz-1の大きさに変えてあげたらどうでしょう?
>
>ReDim MyData(MaxR)
>z = 0
>For q = 3 To MaxR
>  ・
>  ・
> MyData(z) = MyVal
> z = z + 1
>  ・
>  ・
>Next q
>ReDim Preserve MyData(z - 1)
>
>データがあったかどうかは
>
>Ubound(MyData)をする前に
>If z > 0 then なんかで判定できますね。

【68658】Re:動的配列の初期化について
お礼  Tacchi  - 11/4/3(日) 15:39 -

引用なし
パスワード
   ▼neptune さん:

アドバイスありがとうございました。

データで判断させることが、うまくできなくて、
今回は、フラグをたててみることにしました。

Trueだったら、問題となっている、場所の処理を通り、
Falseだったら、スルーさせる…といった感じです

結果、なんとかうまくいけたので、今回はこれでいってみます


>▼neptune さん:
>さっき、ふと、思いついたんですが、
>条件を満足した時だけ、再定義してますが、満足しない場合は
>最少要素数で再定義しておけばどうですか?
>redim 配列(0)
>
>後での判定の時uboundで0が返ってくればOKか、0の時もあるなら
>そのデータで判断するようにすれば簡単ですね。

【68659】Re:動的配列の初期化について
お礼  Tacchi  - 11/4/3(日) 15:43 -

引用なし
パスワード
   ▼角田 さん:

エラー処理の使い方について、完全に間違っていました
角田さんのアドバイスを聞いて、初めて知りました

On Error GoTo xxx
のときには、
On Error GoTo 0
は使えないんですね

もうすこし、エラー処理のところを勉強してみます

二回目以降のエラーが回避できない理由を知ることができて
よかったです

ありがとうございました


>> On Error GoTo MyErr
>> For e = 0 To UBound(MyData)
>>
>> MyErr:
>>  On Error GoTo 0
>> Next i
>
>[On Error GoTo xxx]でエラー処理ルーチンを開始した後に、
>エラー処理ルーチンを抜ける命令は[On Error GoTo 0]ではありません。
>それではエラー処理ルーチンは抜け出せません。
>その後も、ず〜っとエラー処理ルーチンの『中』です。
>
>エラー処理ルーチン実行中には「更なるエラー処理ルーチンの実行」は
>行なわれません。その為
>> 一回はエラー回避で飛ばせるものの、2回以降、エラーが発生すると、
>> エラー回避をしてくれません
>となります。
>
>[On Error GoTo xxx]でエラー処理ルーチンを開始した後に
>エラー処理ルーチンを抜ける命令は(頭に On Error が付かない)[Resume xxx]です。
>
>
>こういう時のエラー対処は、普通は下記のようにします。
>j = -1
>On Error Resume Next
>j = UBound(MyData)
>On Error GoTo 0
>If (j <> -1) Then
> For e = 0 To j
>  (中略)
> Next e
> Erase MyData
>End If

【68660】Re:動的配列の初期化について
お礼  Tacchi  - 11/4/3(日) 15:50 -

引用なし
パスワード
   ▼UO3 さん:

アドバイスありがとうございました


>・まず、動的配列を規定したかどうかの判断を行いたい場合、一番簡単な方法は
> 配列にセットするループの最初でブール型の変数あたりをFalseにしておき、RedimかけたらTrue。
> 配列から取り出すループの前に、この変数の値を判定する。
>
U03さんのアドバイスを聞いて、
やってみました、

フラグを立てて、TrueかFalseか?で判定させてやってみたところ、
悩みは解決して、うまくいくようになりました

本当に助かりました

もう少し、配列を勉強する必要があるようです。

ありがとうございました

【68661】Re:動的配列の初期化について
お礼  Tacchi  - 11/4/3(日) 15:58 -

引用なし
パスワード
   ▼Hirofumi さん:

ご教授ありがとうございます

z=-1
にしておいて、
ループを回さないというやり方があることを初めて知りました
そういう方法は頭にありませんでした…

いろんな方法があるんですね
勉強になります

今回はフラグを立てて、判断させるというやり方でやってみました

Hirofumiさんの考え方でも、一度確認してみます
他の場面でも、使えそうなので、教えてもらって感謝です


ありがとうございました

【68662】Re:動的配列の初期化について
お礼  Tacchi  - 11/4/3(日) 16:00 -

引用なし
パスワード
   ▼Yuki さん:

アドバイスありがとうございます
今回はフラグのTrueとFalseで判断させてみる方法でやることにしました。

いろいろな考え方が聞けて大変勉強になります

ありがとうございました

【68695】Re:動的配列の初期化について
発言  momo  - 11/4/7(木) 10:13 -

引用なし
パスワード
   ▼Tacchi さん:
>▼momo さん:
>
>アドバイスありがとうございます
>配列について、まだまだ勉強中ですが、
>配列の最大数について質問ですが、
>
>たとえば、10個を最大数として、配列の要素数を決めていて、
>実際は、8個しか要素数がなかったとしたら、
>残り2個分の要素数に対しては、何も問題がないのでしょうか?

回答が遅くなりました。
特に問題無いんじゃないでしょうか?
あまりにも大きすぎてメモリーを切迫するようなら問題ですが
最近のPCなら大丈夫だと思います。
それに最後にカウンター変数でRedimすれば実際の配列数の要素になりますし。

【68696】解決済みですが。
発言  Jaka  - 11/4/7(木) 12:36 -

引用なし
パスワード
   ▼>その動的配列が初期化されていたら、処理をする
>初期化されていない場合は、処理をしない

初期化の意味が良く解りません。
配列の初期化とは、配列の各要素が初期値で埋まっている状態だと思っていますけど...?

例えば、
配列の宣言で、

Dim 配列() As long

と宣言しただけだと、配列は初期化されていないと私は考えます。

Redim 配列(&#9747;&#9747;)、Redim 配列(1)、Redim 配列(1 to 2)
とかされて、初めて初期化されたと考えます。

なので、VBの意味の解らない仕様がこれ。

Sub aaaaa()
Dim 配列()
MsgBox IsArray(配列)  'Trueが返りますよね。
End Sub

IsArrayってなのためにあるのか?
と考えた結果
Dim 変数 As Variant
変数 = Range("A1:A5").Value
とかなどの変数の判断材料なのかな?...と。
なもので、こんな風に使ってみたり。
ht tp://www.vbalab.net/vbaqa/c-board.cgi?cmd=one;no=182;id=FAQ

Sub 静的配列初期化()
Dim TB(1 To 5) As Long
Erase TB '←初期化
MsgBox TB(1)
End Sub

Sub 動的配列初期化()
Dim TB() As Long
ReDim TB(1 To 5)  '←初期化
MsgBox TB(1)
End Sub

とまあ、こんな風に考えてます。

【68697】かぶっていたけど、まいっか。
発言  Jaka  - 11/4/7(木) 12:50 -

引用なし
パスワード
   >Sub 静的配列初期化()
>Dim TB(1 To 5) As Long
>Erase TB '←初期化
>MsgBox TB(1)
>End Sub

↑動的の場合宣言だけで初期化がすんでますが、あえて初期化する場合
ですね。

Dim TB(1 To 5) As Long '初期化済み
MsgBox TB(1)

2330 / 13646 ツリー ←次へ | 前へ→
ページ:  ┃  記事番号:
2610219
(SS)C-BOARD v3.8 is Free