Excelの関数エラー取得について
投稿者 魔界の仮面弁士  (社会人)
投稿日時
2012/5/14 12:17:02
別シートに、"=IF(ISERR(Sheet1!RC),0,Sheet1!RC)" な数式を
FormulaR1C1 プロパティ等で作成して、その作業用シートから
Range.Value を取得するというのはどうでしょう。
FormulaR1C1 プロパティ等で作成して、その作業用シートから
Range.Value を取得するというのはどうでしょう。
投稿者 ちびぞ~  (社会人)
投稿日時
2012/5/14 15:57:19
魔界の仮面弁士さん
アドバイスありがとうございました。
やはり、VBのモジュールだけでは難しいのですね。
教えていただいた内容をもとに作成してみます。
ネットで調べるとExcelを読み込む際、行カウントとセルカウントでグルグル回して
1セルずつ二次元配列に入れて行くやりかたと、先に記載したRange.Valueでごっそりと
取得する方法があるようですが、やはりグルグル回す方式では読み込みは遅いようですね。
Excelは解放などvb.netになってから、何だか複雑になったような気がします。
アドバイスありがとうございました。
やはり、VBのモジュールだけでは難しいのですね。
教えていただいた内容をもとに作成してみます。
ネットで調べるとExcelを読み込む際、行カウントとセルカウントでグルグル回して
1セルずつ二次元配列に入れて行くやりかたと、先に記載したRange.Valueでごっそりと
取得する方法があるようですが、やはりグルグル回す方式では読み込みは遅いようですね。
Excelは解放などvb.netになってから、何だか複雑になったような気がします。
投稿者 魔界の仮面弁士  (社会人)
投稿日時
2012/5/14 16:48:14
> 教えていただいた内容をもとに作成してみます。
Excel には、『ワークシート関数』『マクロ』『オートメーション』『DDE』など、
複数の操作手法が用意されているため、目的に応じて使い分けてみてください。
以下蛇足。
> やはりグルグル回す方式では読み込みは遅いようですね。
これは VB.NET の問題ではなく、Excel 側と VB 側での“通信回数”の問題ですね。
たとえば、ワークシート上の 10x10 のセル範囲(100個)があったとして、
「それらの内容を一度に持ってきて、VB側で書き換えて一度に返す方法」と、
「個々のセルごとに、合計 100 回の参照と 100 回の書き込みを行う方法」とを比べると、
どうしても後者の方が遅くなります。
これは、VB6 や VB.NET などといった「外部」との通信がボトルネックになるためであり、
処理回数が増えるにつれて目に見えて遅くなってしまいます。
# ちなみに、もしこれが Excel VBA であった場合は、「自分自身」に対する操作なので、
# ループ処理で記述したとしても、速度低下は VB よりは比較的少なくて済みます。
# もちろんVBA であったとしても、一括処理した方が高速にはなりますけれどね。
この手の問題は、「Excel VBA」と「Excel マクロ」を比較した場合にも発生します。
たとえば、VBAの「PageSetup オブジェクト」および、マクロの「PAGE.SETUP コマンド」は、
いずれも印刷設定のためのコマンドであり、用紙サイズ/拡大率/左余白/右余白などを
指示するためのものです。
これらの調整には、プリンタードライバーとのやりとりを必要とするため、
項目数(書き換えたプロパティの数)だけ、再計算のための僅かな時間を必要とします。
たとえば、一つの項目に 0.4 秒の時間がかかる場合、PageSetup オブジェクトのプロパティを
10項目操作したとしたら、約 4 秒の待ち時間になる計算です。
(速度はドライバー依存であり、異なるメーカーのプリンターにすると処理速度も変化します)
これが PAGE.SETUP マクロであれば、複数の項目を一度にセットできるため、
通信回数も一回分だけで済みます。待ち時間は 0.4 秒だけで済む計算ですね。
http://www.keep-on.com/excelyou/2001lng4/200110/01100290.txt
http://park7.wakwak.com/~efc21/cgi-bin/exqalounge.cgi?print+200811/08110137.txt
http://www.microsoft.com/en-us/download/details.aspx?id=9253
Excel には、『ワークシート関数』『マクロ』『オートメーション』『DDE』など、
複数の操作手法が用意されているため、目的に応じて使い分けてみてください。
以下蛇足。
> やはりグルグル回す方式では読み込みは遅いようですね。
これは VB.NET の問題ではなく、Excel 側と VB 側での“通信回数”の問題ですね。
たとえば、ワークシート上の 10x10 のセル範囲(100個)があったとして、
「それらの内容を一度に持ってきて、VB側で書き換えて一度に返す方法」と、
「個々のセルごとに、合計 100 回の参照と 100 回の書き込みを行う方法」とを比べると、
どうしても後者の方が遅くなります。
これは、VB6 や VB.NET などといった「外部」との通信がボトルネックになるためであり、
処理回数が増えるにつれて目に見えて遅くなってしまいます。
# ちなみに、もしこれが Excel VBA であった場合は、「自分自身」に対する操作なので、
# ループ処理で記述したとしても、速度低下は VB よりは比較的少なくて済みます。
# もちろんVBA であったとしても、一括処理した方が高速にはなりますけれどね。
この手の問題は、「Excel VBA」と「Excel マクロ」を比較した場合にも発生します。
たとえば、VBAの「PageSetup オブジェクト」および、マクロの「PAGE.SETUP コマンド」は、
いずれも印刷設定のためのコマンドであり、用紙サイズ/拡大率/左余白/右余白などを
指示するためのものです。
これらの調整には、プリンタードライバーとのやりとりを必要とするため、
項目数(書き換えたプロパティの数)だけ、再計算のための僅かな時間を必要とします。
たとえば、一つの項目に 0.4 秒の時間がかかる場合、PageSetup オブジェクトのプロパティを
10項目操作したとしたら、約 4 秒の待ち時間になる計算です。
(速度はドライバー依存であり、異なるメーカーのプリンターにすると処理速度も変化します)
これが PAGE.SETUP マクロであれば、複数の項目を一度にセットできるため、
通信回数も一回分だけで済みます。待ち時間は 0.4 秒だけで済む計算ですね。
http://www.keep-on.com/excelyou/2001lng4/200110/01100290.txt
http://park7.wakwak.com/~efc21/cgi-bin/exqalounge.cgi?print+200811/08110137.txt
http://www.microsoft.com/en-us/download/details.aspx?id=9253
投稿者 ちびぞ~  (社会人)
投稿日時
2012/5/15 09:12:53
沢山のアドバイスありがとうございました。
色々な操作方法があるのですね。勉強になります。
>これは VB.NET の問題ではなく、Excel 側と VB 側での“通信回数”の問題ですね。
今回、作成してみたツールは複数ファイル、複数シートのセル計算だったので
1セルずつでは相当な通信回数で遅くなっていたのですね。
Range.Valueでごっそりを発見できてよかったです。
今は、Range.Valueでごっそりと二次元配列に入れたあと、
二次元配列の範囲を指定してセルに書きだす方法がわからず調査中です。
また御相談させていただくと思いますが、その時はよろしくお願いいたします。
色々な操作方法があるのですね。勉強になります。
>これは VB.NET の問題ではなく、Excel 側と VB 側での“通信回数”の問題ですね。
今回、作成してみたツールは複数ファイル、複数シートのセル計算だったので
1セルずつでは相当な通信回数で遅くなっていたのですね。
Range.Valueでごっそりを発見できてよかったです。
今は、Range.Valueでごっそりと二次元配列に入れたあと、
二次元配列の範囲を指定してセルに書きだす方法がわからず調査中です。
また御相談させていただくと思いますが、その時はよろしくお願いいたします。
投稿者 魔界の仮面弁士  (社会人)
投稿日時
2012/5/15 10:03:26
> 今は、Range.Valueでごっそりと二次元配列に入れたあと、
> 二次元配列の範囲を指定してセルに書きだす方法がわからず調査中です。
VB 側で、別サイズの配列をもう一つ作ってそこに転記してから、
その新配列と同形の Range に対してセットすれば OK かと。
Value のほか、数式(Formula 系プロパティ)に対しても同様です。
ちなみに、縦方向に同じ値を繰り返す場合は Value に一次元配列を渡すこともできますし、
.Range("B2:C6,E5:F9,A9:B14") のような非連続の範囲に書き込むことも可能です。
> 二次元配列の範囲を指定してセルに書きだす方法がわからず調査中です。
VB 側で、別サイズの配列をもう一つ作ってそこに転記してから、
その新配列と同形の Range に対してセットすれば OK かと。
Value のほか、数式(Formula 系プロパティ)に対しても同様です。
ちなみに、縦方向に同じ値を繰り返す場合は Value に一次元配列を渡すこともできますし、
.Range("B2:C6,E5:F9,A9:B14") のような非連続の範囲に書き込むことも可能です。
投稿者 ちびぞ~  (社会人)
投稿日時
2012/5/25 19:32:27
魔界の仮面弁士さま
回答が遅くなり、大変申し訳ありません。
> VB 側で、別サイズの配列をもう一つ作ってそこに転記してから、
> その新配列と同形の Range に対してセットすれば OK かと。
現在、Excelに書きだす内容を動的にしたいと考えています。
例えば、constなどで開始と終了位置を複数保持(A1:B2,F3:G6 などと自由に設定)
そこで質問なのですが、
二次元配列をarraylistに入れることは可能でしょうか。
イメージとしては、以下のような感じです。
※以下のやり方では「インデックスの数がインデックス付き配列の次元より少ない値です。」エラー。
Dim hairetu(2, 2) As String
Dim arylist As ArrayList = New ArrayList()
hairetu(0, 0) = "0-0"
hairetu(0, 1) = "0-1"
hairetu(1, 0) = "1-0"
hairetu(1, 1) = "1-1"
arylist.Add(hairetu(0))
↑が設定できたら、
arylist(0)に入っているhairetu(0,0),hairetu(0,1)
arylist(1)に入っているhairetu(1,0),hairetu(1,1)
をとってきたいと考えています。
説明が下手ですみません。
回答が遅くなり、大変申し訳ありません。
> VB 側で、別サイズの配列をもう一つ作ってそこに転記してから、
> その新配列と同形の Range に対してセットすれば OK かと。
現在、Excelに書きだす内容を動的にしたいと考えています。
例えば、constなどで開始と終了位置を複数保持(A1:B2,F3:G6 などと自由に設定)
そこで質問なのですが、
二次元配列をarraylistに入れることは可能でしょうか。
イメージとしては、以下のような感じです。
※以下のやり方では「インデックスの数がインデックス付き配列の次元より少ない値です。」エラー。
Dim hairetu(2, 2) As String
Dim arylist As ArrayList = New ArrayList()
hairetu(0, 0) = "0-0"
hairetu(0, 1) = "0-1"
hairetu(1, 0) = "1-0"
hairetu(1, 1) = "1-1"
arylist.Add(hairetu(0))
↑が設定できたら、
arylist(0)に入っているhairetu(0,0),hairetu(0,1)
arylist(1)に入っているhairetu(1,0),hairetu(1,1)
をとってきたいと考えています。
説明が下手ですみません。
投稿者 魔界の仮面弁士  (社会人)
投稿日時
2012/5/26 02:27:25
> 二次元配列をarraylistに入れることは可能でしょうか。
ArrayList に格納されるデータ型は「Object」なので、相手が
String でも Range でも 二次元配列でも五次元配列でもジャグ配列でも
自由に格納できます。求めている答えになっているかは分かりませんが。
> Dim hairetu(2, 2) As String
> hairetu(0, 0) = "0-0"
> hairetu(0, 1) = "0-1"
> hairetu(1, 0) = "1-0"
> hairetu(1, 1) = "1-1"
4項目だけなら、Dim hairetu(1, 1) As String で良いはずですよ。
(2, 2) まで確保しているなら、そこまで文字列を入れておかないと、
未設定の項目は Nothing のままになってしまいます。
hairetu(0, 2) = "0-2"
hairetu(1, 2) = "1-2"
hairetu(2, 0) = "2-0"
hairetu(2, 1) = "2-1"
hairetu(2, 2) = "2-2"
> list.Add(hairetu(0))
文字通りに二次元配列を格納するのであれば、
二次元構造のままで扱いたいなら、ジャグ配列を真似て、
とはいえ今となっては、Object でしか管理できない ArrayList を
使うメリットは無いので、List クラスを推奨しておきます。たとえば
あるいは
そのほか、DataTable で管理するのもおすすめです。
ソートや検索なども容易にできますしね。
http://hpcgi1.nifty.com/MADIA/VBBBS2/wwwlng.cgi?print+200808/08080009.txt
ArrayList に格納されるデータ型は「Object」なので、相手が
String でも Range でも 二次元配列でも五次元配列でもジャグ配列でも
自由に格納できます。求めている答えになっているかは分かりませんが。
> Dim hairetu(2, 2) As String
> hairetu(0, 0) = "0-0"
> hairetu(0, 1) = "0-1"
> hairetu(1, 0) = "1-0"
> hairetu(1, 1) = "1-1"
4項目だけなら、Dim hairetu(1, 1) As String で良いはずですよ。
(2, 2) まで確保しているなら、そこまで文字列を入れておかないと、
未設定の項目は Nothing のままになってしまいます。
hairetu(0, 2) = "0-2"
hairetu(1, 2) = "1-2"
hairetu(2, 0) = "2-0"
hairetu(2, 1) = "2-1"
hairetu(2, 2) = "2-2"
> list.Add(hairetu(0))
文字通りに二次元配列を格納するのであれば、
list.Add(hairetu)
となり、それを取り出すときは、'MsgBox( list(0)(1, 1) )
Dim x(,) As String = DirectCast(list(0), String(,))
MsgBox( x(1, 1) )
という感じになります。まぁ、求めている答えでは無いでしょうけれども…。二次元構造のままで扱いたいなら、ジャグ配列を真似て、
Dim a As New ArrayList()
Dim x As ArrayList
x = New ArrayList()
x.Add("0-0")
x.Add("0-1")
a.Add(x)
x = New ArrayList()
x.Add("1-0")
x.Add("1-1")
a.Add(x)
'MsgBox(a(1)(0))
MsgBox(CStr(DirectCast(a(1), ArrayList)(0)))
あるいは、Dim a As New ArrayList()
a.Add(New String(){ "0-0", "0-1" })
a.Add(New String(){ "1-0", "1-1" })
'MsgBox( a(1)(0) )
MsgBox(CStr(DirectCast(a(0), String())(1)))
のような形で取り込む手もあります。とはいえ今となっては、Object でしか管理できない ArrayList を
使うメリットは無いので、List クラスを推奨しておきます。たとえば
Dim a As New List(Of String())
a.Add(New String() {"0-0", "0-1"})
a.Add(New String() {"1-0", "1-1"})
MsgBox(a(0)(1))
あるいは
Dim a As New List(Of List(Of String))
a.Add(New List(Of String)() From {"0-0", "0-1"})
a.Add(New List(Of String)() From {"1-0", "1-1"})
a(0).Add("0-2")
a(0).Add("0-3")
MsgBox(a(0)(2))
といった感じです。そのほか、DataTable で管理するのもおすすめです。
ソートや検索なども容易にできますしね。
http://hpcgi1.nifty.com/MADIA/VBBBS2/wwwlng.cgi?print+200808/08080009.txt
投稿者 m190  (社会人)
投稿日時
2012/5/26 20:56:09
やりたいことがきちんと把握できていない可能性があるので
もしかすると余計に混乱させてしまう、かもしれませんが・・・
もし必要な情報が、開始と終了位置の 2つのみであれば、要素を 2つ持つ
構造体を作って、それを配列で保持する、ってのはお役に立ちませんか?
> 例えば、constなどで開始と終了位置を複数保持(A1:B2,F3:G6 などと自由に設定)
例)
日頃の不勉強が祟って私、(配列も苦手だしジェネリックが全然ダメなんです)
List(Of T)とかが扱えず、↑では String型の配列で保持してますが、これを
List(Of T)などのコレクション(?)で扱えれば、要素の追加や削除、検索など
もっと便利になるような気がします。
#ただその場合、わざわざ構造体にする意味は無いかもしれません。。。(汗
#List(Of T)の勉強をがんばって、自在に扱えるようになるのが一番近道かも
#・・・と、自分自身に言い聞かせております。。。 (/_;) <反省
もしお役に立ったら嬉しい限りなんですが、逆に的外れで
余計に混乱させてしまったような場合には本当にごめんなさい。
もしかすると余計に混乱させてしまう、かもしれませんが・・・
もし必要な情報が、開始と終了位置の 2つのみであれば、要素を 2つ持つ
構造体を作って、それを配列で保持する、ってのはお役に立ちませんか?
> 例えば、constなどで開始と終了位置を複数保持(A1:B2,F3:G6 などと自由に設定)
例)
Public Class Form1
'仮に開始と終了位置の情報 2つを1組として、それを複数個(数不定)保持する場合
Structure CELL_RANGE
Dim START_CELL As String '開始位置の情報
Dim END_CELL As String '終了位置の情報
Sub New(ByVal StartCell As String, ByVal EndCell As String)
Me.START_CELL = StartCell '初期化の時に第1引数を取り込む
Me.END_CELL = EndCell '初期化の時に第2引数を取り込む
End Sub
End Structure
Sub test()
Dim MyList(3) As CELL_RANGE 'コンストラクタを使った初期化
MyList(0) = New CELL_RANGE("0-0", "0-1") '開始位置,終了位置 の順番
MyList(1) = New CELL_RANGE("1-0", "1-1")
MyList(2) = New CELL_RANGE("2-0", "2-1")
MyList(3) = New CELL_RANGE("3-0", "3-1")
For Each i In MyList
Debug.Print("Start:{0}, End:{1}", i.START_CELL, i.END_CELL)
Next '個別のデータ取出し・設定 →変数名(n).START_CELL, 変数名(n).END_CELL
End Sub
'もしも "0-0"の文字列 1つで開始・終了の位置を表し、それを複数(要素数が不定)保持する場合
Structure CELL_RANGE_2
Dim START_END() As String
End Structure
Sub test2() Handles Me.Click
Dim MyList2(3) As CELL_RANGE_2 '初期化子を使った初期化
MyList2(0) = New CELL_RANGE_2() With {.START_END = {"0-0", "0-1"}} '要素数は不定
MyList2(1) = New CELL_RANGE_2() With {.START_END = {"1-0", "1-1", "1-2", "1-3"}}
MyList2(2) = New CELL_RANGE_2() With {.START_END = {""}} '←要素数が 0のとき
MyList2(3) = New CELL_RANGE_2() With {.START_END = {"3-0", "3-1", "3-2"}}
For n = 0 To UBound(MyList2) '要素が 0個でも""を入力↑してるのは↓でエラーが・・・
For Each m In MyList2(n).START_END
Debug.Print(m) '個別のデータ取出し・設定 →変数名(n).START_END(m)
Next
Next
End Sub
End Class
日頃の不勉強が祟って私、(配列も苦手だしジェネリックが全然ダメなんです)
List(Of T)とかが扱えず、↑では String型の配列で保持してますが、これを
List(Of T)などのコレクション(?)で扱えれば、要素の追加や削除、検索など
もっと便利になるような気がします。
#ただその場合、わざわざ構造体にする意味は無いかもしれません。。。(汗
#List(Of T)の勉強をがんばって、自在に扱えるようになるのが一番近道かも
#・・・と、自分自身に言い聞かせております。。。 (/_;) <反省
もしお役に立ったら嬉しい限りなんですが、逆に的外れで
余計に混乱させてしまったような場合には本当にごめんなさい。
投稿者 ちびぞ~  (社会人)
投稿日時
2012/5/28 10:05:27
魔界の仮面弁士さん、m190 さん、アドバイスありがとうございます。
説明が下手ですみません。
かなりの初心者なので、混乱している頭を少しずつ解決させています。
やりたい事は、
・Excelで対象範囲の計算をおこないたい。
たとえば、対象範囲が"A1:A2"の場合、
対象になるファイルのシートに対して
A1の集計
A2の集計
をそれぞれ出したい。
・Excelの対象は複数ファイルの複数シート。
・集計対象の範囲は複数あって、動的にしたい。
そこで、以前の質問などを踏まえ、
集計対象範囲(開始:終了)の開始と終了をconfigファイルに持って、
その配列をLoopさせようと思っています。
hani={A1:B2,F3:G6.....}
↑
を","区切りで対象ブロック毎に設定して
その範囲で計算しようかと(ここが二次元配列になるかと)。
また、
対象ブロックの範囲(上記の"hani")が動的で、
計算対象になるファイルとシートが複数あることから、
対象ブロックを1度ファイル・シート分読み込んで計算し、
その結果(二次元配列)をarraylist(list?)に、ひとまず全部入れてから
書き込んだ方が早いのかと。
なので、イメージとしては、
list(0)←最初のブロックの二次元配列(A1:B2)
list(1)←次のブロックの二次元配列(F3:G6)
・
・
・
と考えていました。
そもそも、この考えが間違っているのでしょうか。。。。
説明が下手ですみません。
かなりの初心者なので、混乱している頭を少しずつ解決させています。
やりたい事は、
・Excelで対象範囲の計算をおこないたい。
たとえば、対象範囲が"A1:A2"の場合、
対象になるファイルのシートに対して
A1の集計
A2の集計
をそれぞれ出したい。
・Excelの対象は複数ファイルの複数シート。
・集計対象の範囲は複数あって、動的にしたい。
そこで、以前の質問などを踏まえ、
集計対象範囲(開始:終了)の開始と終了をconfigファイルに持って、
その配列をLoopさせようと思っています。
hani={A1:B2,F3:G6.....}
↑
を","区切りで対象ブロック毎に設定して
その範囲で計算しようかと(ここが二次元配列になるかと)。
また、
対象ブロックの範囲(上記の"hani")が動的で、
計算対象になるファイルとシートが複数あることから、
対象ブロックを1度ファイル・シート分読み込んで計算し、
その結果(二次元配列)をarraylist(list?)に、ひとまず全部入れてから
書き込んだ方が早いのかと。
なので、イメージとしては、
list(0)←最初のブロックの二次元配列(A1:B2)
list(1)←次のブロックの二次元配列(F3:G6)
・
・
・
と考えていました。
そもそも、この考えが間違っているのでしょうか。。。。
投稿者 shu  (社会人)
投稿日時
2012/5/28 10:36:27
話が続いているようなので解決チェックをはずしてみました。
A1:B2
の形式で範囲が分かっているなら、それを文字列として情報をもてば
List(Of String)で範囲情報は管理できそうです。
後はファイル名、シート名の情報も持つ必要があるようなので
それらの情報についてシート毎にオブジェクトとするか範囲情報毎に
持つようにするか決めると良いと思います。
A1:B2
の形式で範囲が分かっているなら、それを文字列として情報をもてば
List(Of String)で範囲情報は管理できそうです。
後はファイル名、シート名の情報も持つ必要があるようなので
それらの情報についてシート毎にオブジェクトとするか範囲情報毎に
持つようにするか決めると良いと思います。
投稿者 (削除されました)  ()
投稿日時
2012/5/28 11:19:55
(削除されました)
投稿者 ちびぞ~  (社会人)
投稿日時
2012/5/28 11:34:21
本当に文章が下手ですみません。
やりたい事だけをお伝えすると、
・集計対象範囲をconfigファイルで変更できるように以下のようにもちたい
hani={A1:B2,F3:G6.....}
・Excelで対象範囲の計算をおこないたい。
たとえば、対象範囲が"A1:A2"の場合、
対象になるファイルのシートに対して
A1の集計
A2の集計
をそれぞれ出したい。
・集計対処のExcelは複数ファイルの複数シート。
・集計対象の範囲(上記のhaniの部分)は複数あって、動的にしたい。
・集計結果を別ファイルに同じレイアウトで出力したい。
hani={A1:B2,F3:G6.....}だったら、
入力ファイルのA1の結果=>出力ファイルのA1へ
簡単なのは、1ブロック目の結果をファイル数のシート数分、2次元配列に入れて
書き込む。
次に2ブロック目・・・
とやれば、2次元配列だけで済みそうですが、遅くなるのではと懸念しています。
プログラミングが苦手なので楽しいけど、難しいです。
やりたい事だけをお伝えすると、
・集計対象範囲をconfigファイルで変更できるように以下のようにもちたい
hani={A1:B2,F3:G6.....}
・Excelで対象範囲の計算をおこないたい。
たとえば、対象範囲が"A1:A2"の場合、
対象になるファイルのシートに対して
A1の集計
A2の集計
をそれぞれ出したい。
・集計対処のExcelは複数ファイルの複数シート。
・集計対象の範囲(上記のhaniの部分)は複数あって、動的にしたい。
・集計結果を別ファイルに同じレイアウトで出力したい。
hani={A1:B2,F3:G6.....}だったら、
入力ファイルのA1の結果=>出力ファイルのA1へ
簡単なのは、1ブロック目の結果をファイル数のシート数分、2次元配列に入れて
書き込む。
次に2ブロック目・・・
とやれば、2次元配列だけで済みそうですが、遅くなるのではと懸念しています。
プログラミングが苦手なので楽しいけど、難しいです。
投稿者 魔界の仮面弁士  (社会人)
投稿日時
2012/5/28 16:01:46
> hani={A1:B2,F3:G6.....}
Dim r As Excel.Range = Sheet1.Range("A1:B2,F3:G6")
で、対象範囲を取得できます。
個々のエリアは、Areas プロパティ経由で切り出せます。
Dim oAreas As Excel.Areas = r.Areas
Dim r1 As Excel.Range = oAreas(1) 'A1から始まる横2×縦2の範囲
Dim r2 As Excel.Range = oAreas(2) 'F3から始まる横2×縦4の範囲
直接切り出してももちろん OK です。
Dim r1 As Excel.Range = Sheet1.Range("A1:B2")
Dim r2 As Excel.Range = Sheet1.Range("F3:G6")
実際には切り出す個数が不定なので、r1,r2,r3… といった変数名では無く、
ループ処理するか、コレクションに蓄えるかすることになるでしょうけれども。
> hani={A1:B2,F3:G6.....}
> たとえば、対象範囲が"A1:A2"の場合、
hani が A1:B2,F3:G6 で、対象範囲が "A1:A2" ということは、
B1:B2 などが含まれないことになりますよね。一列ずつ処理するという事ですか?
> A1の集計
> A2の集計
A1 や A2 を集計するのですか? 集計結果を A1 や A2 に書くのではなく?
"A1" も "A2" も単一セルなので、配列にする意味が思い当たらないのですが…。
("A1:B2" や "A1:B2,F3:G6" なら複数セルなので、集計という話も分かるのですが)
> ・集計対処のExcelは複数ファイルの複数シート。
Book1.xls(3シート) 上の Shet1!A1 + Sheet2!A1 + Sheet3!A1 と
Book2.xls(2シート) 上の Shet1!A1 + Sheet2!A1 を足し合わせた Book3 を作る感じですか?
あるいは、Book1.xls や Book2.xls を Result1.xls や Result2.xls などへと
個々のファイルごとに別名で保存する感じですか?
> ・集計対象の範囲(上記のhaniの部分)は複数あって、動的にしたい。
複数というのは、hani1 と hani2 と hani3 という意味では無く、
hani が複数のセル範囲("A1:B2,F3:G6" など)を含んでいるという意味ですか?
> 1ブロック目の結果をファイル数のシート数分、2次元配列に入れて書き込む。
> 次に2ブロック目・・・
状況がよく分かりませんが、同一シート上への書き込みであれば、
指定された A1:B2,F3:G6 という範囲を A1:G6(6行7列) に拡張してから
6行2列の二次元配列で処理することで、読み書きを一回で済ませられます。
もちろん、Areas などを通じて、A1:B2 エリアの処理と
F3:G6 エリアの処理に分けて処理しても構わないとは思いますが。
> 遅くなるのではと懸念しています。
まずは測定してみてください。
現在どの程度の時間がかかっていて、それをどの程度まで縮めたいのでしょうか?
> 苦手なので楽しい
被虐的…? (^^;
Dim r As Excel.Range = Sheet1.Range("A1:B2,F3:G6")
で、対象範囲を取得できます。
個々のエリアは、Areas プロパティ経由で切り出せます。
Dim oAreas As Excel.Areas = r.Areas
Dim r1 As Excel.Range = oAreas(1) 'A1から始まる横2×縦2の範囲
Dim r2 As Excel.Range = oAreas(2) 'F3から始まる横2×縦4の範囲
直接切り出してももちろん OK です。
Dim r1 As Excel.Range = Sheet1.Range("A1:B2")
Dim r2 As Excel.Range = Sheet1.Range("F3:G6")
実際には切り出す個数が不定なので、r1,r2,r3… といった変数名では無く、
ループ処理するか、コレクションに蓄えるかすることになるでしょうけれども。
> hani={A1:B2,F3:G6.....}
> たとえば、対象範囲が"A1:A2"の場合、
hani が A1:B2,F3:G6 で、対象範囲が "A1:A2" ということは、
B1:B2 などが含まれないことになりますよね。一列ずつ処理するという事ですか?
> A1の集計
> A2の集計
A1 や A2 を集計するのですか? 集計結果を A1 や A2 に書くのではなく?
"A1" も "A2" も単一セルなので、配列にする意味が思い当たらないのですが…。
("A1:B2" や "A1:B2,F3:G6" なら複数セルなので、集計という話も分かるのですが)
> ・集計対処のExcelは複数ファイルの複数シート。
Book1.xls(3シート) 上の Shet1!A1 + Sheet2!A1 + Sheet3!A1 と
Book2.xls(2シート) 上の Shet1!A1 + Sheet2!A1 を足し合わせた Book3 を作る感じですか?
あるいは、Book1.xls や Book2.xls を Result1.xls や Result2.xls などへと
個々のファイルごとに別名で保存する感じですか?
> ・集計対象の範囲(上記のhaniの部分)は複数あって、動的にしたい。
複数というのは、hani1 と hani2 と hani3 という意味では無く、
hani が複数のセル範囲("A1:B2,F3:G6" など)を含んでいるという意味ですか?
> 1ブロック目の結果をファイル数のシート数分、2次元配列に入れて書き込む。
> 次に2ブロック目・・・
状況がよく分かりませんが、同一シート上への書き込みであれば、
指定された A1:B2,F3:G6 という範囲を A1:G6(6行7列) に拡張してから
6行2列の二次元配列で処理することで、読み書きを一回で済ませられます。
もちろん、Areas などを通じて、A1:B2 エリアの処理と
F3:G6 エリアの処理に分けて処理しても構わないとは思いますが。
> 遅くなるのではと懸念しています。
まずは測定してみてください。
現在どの程度の時間がかかっていて、それをどの程度まで縮めたいのでしょうか?
> 苦手なので楽しい
被虐的…? (^^;
投稿者 m190  (社会人)
投稿日時
2012/5/28 23:52:51
なんとなく、ここら辺がはっきりすると分かり易くなるんじゃないかな?
・・・って感じがする点を 3つほど
おそらく現状でも集計は出来てるんだけれども、VB と Excel の通信回数を減らすため
ファイルを丸ごと読み込んで、書き込みの時にも丸ごと(まとめて)書き込みたい。
その時に、
> 1ブロック目の処理結果をファイル数のシート数分、2次元配列に入れて書き込む
hani(0)={A1:B2,F3:G6.....} →複数ファイルの複数シートへ書き込み、次にまた
hani(1)={B3:G4,E3:F6.....} →複数ファイルの複数シートへ書き込み、次にまた
・・・n 回分繰り返し・・・
hani(n)={A2:B3,F6:G9.....} →複数ファイルの複数シートへ書き込み、ようやく終了
これを
hani(0)={A1:B2,F3:G6.....} →1ブロックごとに結果をいちいち書き込まず
・・・n 回分繰り返し・・・
hani(n)={A2:B3,F6:G9.....} →全ての処理終わったら、最後にまとめて書き込みたい
そのために、
計算結果が格納されている配列を、リストなどに一時保存しておきたい
・・・って感じなんですよね?(ご要望としてはあってますか?)
で、そのときの集計対象なんですが、もし串刺し計算みたいな感じだと
こんな↓ようなパターンなのか("A"一文字が開始・終了位置を表します)、
1) Book1
Sheet(1) A,B,C '←計算範囲は Book内でみな同じ
Sheet(2) A,B,C
・・・
Sheet(n) A,B,C
Book2
Sheet(1) V,B '←計算範囲は Book内でみな同じ
・・・
Sheet(m) V,B
あるいは↓こんな感じの、
2) Book1
Sheet1(A,B) '←計算範囲が Book毎にバラバラ
Book2
Sheet1(V,i,s,u,a)
Sheet2(l,B)
Sheet3(a,s,i,c)
Book3
Sheet1(V,B,中,学,校,)
> パターンとしては 1)と 2)とでは、どちらに近いのか
#対象範囲が Book内でバラバラだと串刺し無理ですよね?
#・・・もしかして 1)と 2)のあわせ技!?←その場合は 2)ってことで・・・
それから、1ブロック目の処理を行う際に、(集計に必要なデータを集めるために)
> 3)Book1 を丸ごと読み込んでくれば、 1ブロック目の処理が行えるのか、それとも
> 4)Book1 も Book2 も Book3 も読み込まないと、1ブロック目の処理が行えないのか
3)と 4)だと、どちらに近いですか?
#結果を書き込む時にだけ複数ファイルに書き込む必要がある感じでしょうか?
最後にもう1つ、対象範囲について
> 5)処理の途中で、集計の対象範囲が追加・削除されるなどの変更はありますか?
あるいは
> 6)処理開始の前に、一旦 configファイル等から対象範囲を読み込んでしまえば、
> とりあえずは、その処理の最中に対象範囲の変更が起こる心配はない、
といった感じですか?計算の対象範囲は、5)と 6)と、どちらに近いですか?
・・・見たいな部分がはっきりすると
きっと解決への道筋も探し易くなるんじゃないかな、って思いました。
・・・って感じがする点を 3つほど
おそらく現状でも集計は出来てるんだけれども、VB と Excel の通信回数を減らすため
ファイルを丸ごと読み込んで、書き込みの時にも丸ごと(まとめて)書き込みたい。
その時に、
> 1ブロック目の処理結果をファイル数のシート数分、2次元配列に入れて書き込む
hani(0)={A1:B2,F3:G6.....} →複数ファイルの複数シートへ書き込み、次にまた
hani(1)={B3:G4,E3:F6.....} →複数ファイルの複数シートへ書き込み、次にまた
・・・n 回分繰り返し・・・
hani(n)={A2:B3,F6:G9.....} →複数ファイルの複数シートへ書き込み、ようやく終了
これを
hani(0)={A1:B2,F3:G6.....} →1ブロックごとに結果をいちいち書き込まず
・・・n 回分繰り返し・・・
hani(n)={A2:B3,F6:G9.....} →全ての処理終わったら、最後にまとめて書き込みたい
そのために、
計算結果が格納されている配列を、リストなどに一時保存しておきたい
・・・って感じなんですよね?(ご要望としてはあってますか?)
で、そのときの集計対象なんですが、もし串刺し計算みたいな感じだと
こんな↓ようなパターンなのか("A"一文字が開始・終了位置を表します)、
1) Book1
Sheet(1) A,B,C '←計算範囲は Book内でみな同じ
Sheet(2) A,B,C
・・・
Sheet(n) A,B,C
Book2
Sheet(1) V,B '←計算範囲は Book内でみな同じ
・・・
Sheet(m) V,B
あるいは↓こんな感じの、
2) Book1
Sheet1(A,B) '←計算範囲が Book毎にバラバラ
Book2
Sheet1(V,i,s,u,a)
Sheet2(l,B)
Sheet3(a,s,i,c)
Book3
Sheet1(V,B,中,学,校,)
> パターンとしては 1)と 2)とでは、どちらに近いのか
#対象範囲が Book内でバラバラだと串刺し無理ですよね?
#・・・もしかして 1)と 2)のあわせ技!?←その場合は 2)ってことで・・・
それから、1ブロック目の処理を行う際に、(集計に必要なデータを集めるために)
> 3)Book1 を丸ごと読み込んでくれば、 1ブロック目の処理が行えるのか、それとも
> 4)Book1 も Book2 も Book3 も読み込まないと、1ブロック目の処理が行えないのか
3)と 4)だと、どちらに近いですか?
#結果を書き込む時にだけ複数ファイルに書き込む必要がある感じでしょうか?
最後にもう1つ、対象範囲について
> 5)処理の途中で、集計の対象範囲が追加・削除されるなどの変更はありますか?
あるいは
> 6)処理開始の前に、一旦 configファイル等から対象範囲を読み込んでしまえば、
> とりあえずは、その処理の最中に対象範囲の変更が起こる心配はない、
といった感じですか?計算の対象範囲は、5)と 6)と、どちらに近いですか?
・・・見たいな部分がはっきりすると
きっと解決への道筋も探し易くなるんじゃないかな、って思いました。
投稿者 ちびぞ~  (社会人)
投稿日時
2012/5/29 08:54:43
皆さんアドバイスとご質問ありがとうございました。
複数ファイル複数シートの同じ位置の値の足し算をして、
違うファイルに結果を出力したかっただけなんです。
A1だったら、対象ファイル・対象シートのA1を全部足して、出力ファイルに書き出す感じを
想定していました。
m190さんのおっしゃる通り、現状ではできているのですが、なるべく通信回数を減らしたい
というのもありました。
おかげさまで書き込んでいただいた内容で自分の考えなどをまとめ直して、
できそうなめどが立ちました。
本当に説明が下手くそですみません。
複数ファイル複数シートの同じ位置の値の足し算をして、
違うファイルに結果を出力したかっただけなんです。
A1だったら、対象ファイル・対象シートのA1を全部足して、出力ファイルに書き出す感じを
想定していました。
m190さんのおっしゃる通り、現状ではできているのですが、なるべく通信回数を減らしたい
というのもありました。
おかげさまで書き込んでいただいた内容で自分の考えなどをまとめ直して、
できそうなめどが立ちました。
本当に説明が下手くそですみません。
投稿者 shu  (社会人)
投稿日時
2012/5/29 09:47:43
範囲というのは集計上は関係なく
範囲がA1:A3となっていて、シートがSheet1,Sheet2,Sheet3で書込み先シートをSheet4とすると
Sheet4.A1 = Sheet1.A1 + Sheet2.A1 + Sheet3.A1
Sheet4.A2 = Sheet1.A2 + Sheet2.A2 + Sheet3.A2
Sheet4.A3 = Sheet1.A3 + Sheet2.A3 + Sheet3.A3
ということですか?
だとするとSheet4の範囲を2次元配列Result(,)に取得して
0で初期化
Sheet1の範囲を配列A(,)に取得
ResultにAの要素を足す
これをSheet2,3についても同様に行い
最後に配列の内容をSheet4に書き戻す
でどうでしょう?
範囲がA1:A3となっていて、シートがSheet1,Sheet2,Sheet3で書込み先シートをSheet4とすると
Sheet4.A1 = Sheet1.A1 + Sheet2.A1 + Sheet3.A1
Sheet4.A2 = Sheet1.A2 + Sheet2.A2 + Sheet3.A2
Sheet4.A3 = Sheet1.A3 + Sheet2.A3 + Sheet3.A3
ということですか?
だとするとSheet4の範囲を2次元配列Result(,)に取得して
0で初期化
Sheet1の範囲を配列A(,)に取得
ResultにAの要素を足す
これをSheet2,3についても同様に行い
最後に配列の内容をSheet4に書き戻す
でどうでしょう?
投稿者 m190  (社会人)
投稿日時
2012/5/29 21:36:10
ファイル毎に異なるんでしょうが、平均で10個の処理があったとして、1つの処理に
平均 10個のファイル読み込みが必要、結果を書き込む際にも 10個のファイルを、、、
なんて考えたら、10×10の読み書き×処理回数 10回分 = 1000回分の通信(!)
を繰り返す訳ですから、(←チョッと極端な数字の例ですが・・・)、もしこれが、
10回の読み込み →処理100回分終了 →10回の書き込み、で可能になれば、
10+10 = 20回(!)の通信で済ませられるようになる訳で、これはもう十分に
チャレンジのし甲斐がありそうですよね。
だからその分、
頭の中が、ゴチャゴチャこんがらがって来るのは、むしろ当然のことと思います。
で、掲示板って、単に答えを教えてもらえて有難いってのもあるかと思いますが、
質問するにも一旦、自分の頭の中で整理しないと質問ができなかったりする訳で
これが意外と、考えをまとめている間に自分自身でヒントが発見できちゃった!
なーんてケースもあるんですよね
そんなふうに考えると、
この掲示板って案外、質問する側も答える側も閲覧する側も、三方一両得の
可能性を秘めた夢のような(?)システムじゃないか、って気がしてきます。
おかげ様で私もジェネリックというか List(Of T)、ヘルプ頑張って読みました。
きっと今回の件がなかったら、ずーっと先送りになっていたように思います(汗
#まだまた配列もジェネリックも不安ですが、、、
そんな訳で、
プログラムに限らず様々なことが学べるここの掲示板には、本当に感謝してます。
#だけどこれを欲張って自分だけ得しようなんて皆が考えだしたりすると、途端に
#三方一両損の悪夢のような仕組みになっちゃったり・・・世の中難しいですね(笑
色々難しいこと山積みですし、
プログラミング大変だと思いますが、便利なプログラムが完成すると良いですね!
#楽するためにはどんな苦労も厭わない(?)って人が良いプログラマだそうで。。。
平均 10個のファイル読み込みが必要、結果を書き込む際にも 10個のファイルを、、、
なんて考えたら、10×10の読み書き×処理回数 10回分 = 1000回分の通信(!)
を繰り返す訳ですから、(←チョッと極端な数字の例ですが・・・)、もしこれが、
10回の読み込み →処理100回分終了 →10回の書き込み、で可能になれば、
10+10 = 20回(!)の通信で済ませられるようになる訳で、これはもう十分に
チャレンジのし甲斐がありそうですよね。
だからその分、
頭の中が、ゴチャゴチャこんがらがって来るのは、むしろ当然のことと思います。
で、掲示板って、単に答えを教えてもらえて有難いってのもあるかと思いますが、
質問するにも一旦、自分の頭の中で整理しないと質問ができなかったりする訳で
これが意外と、考えをまとめている間に自分自身でヒントが発見できちゃった!
なーんてケースもあるんですよね
そんなふうに考えると、
この掲示板って案外、質問する側も答える側も閲覧する側も、三方一両得の
可能性を秘めた夢のような(?)システムじゃないか、って気がしてきます。
おかげ様で私もジェネリックというか List(Of T)、ヘルプ頑張って読みました。
きっと今回の件がなかったら、ずーっと先送りになっていたように思います(汗
#まだまた配列もジェネリックも不安ですが、、、
そんな訳で、
プログラムに限らず様々なことが学べるここの掲示板には、本当に感謝してます。
#だけどこれを欲張って自分だけ得しようなんて皆が考えだしたりすると、途端に
#三方一両損の悪夢のような仕組みになっちゃったり・・・世の中難しいですね(笑
色々難しいこと山積みですし、
プログラミング大変だと思いますが、便利なプログラムが完成すると良いですね!
#楽するためにはどんな苦労も厭わない(?)って人が良いプログラマだそうで。。。
今回は、Excel操作について教えてください。
vb2008でのExcel操作を学習中です。
そこで、以下の不明点についてアドバイスをお願いいたします。
----
Excelにて複数のワークシートに設定されている値をそれぞれ読み込んで
足し算をおこないたい。
その際、計算対象シートに関数エラー(#VALUEなど)が入っていたら、"0"として
計算したい。
----
当初、1セルずつ見て計算をしていったのですが、
1セルずつ読み込むと時間がかかるようなので、
以下のように、対象となるシートを一気に配列にもってきてから計算をおこなうようにしました。
Dim xlCells As Excel.Range = Nothing
Dim xlRange1 As Excel.Range = Nothing
Dim xlRange2 As Excel.Range = Nothing
Dim xlRange As Excel.Range = Nothing
Dim data(,) As Object
xlCells = xlSheet.Cells
xlRange1 = DirectCast(xlCells(row1, cell1), Excel.Range)
xlRange2 = DirectCast(xlCells(row2, cell2), Excel.Range)
xlRange = xlSheet.Range(xlRange1, xlRange2)
data = xlRange.Value
※以降、dataの内容を集計対象として計算。
ただ、こうすると読み込みは早くのですが関数エラー(#VALUEなど)を取得することが
できず困っています。
関数エラーを取得するには、1セルずつ読んでいくしか方法は無いのでしょうか。
よろしくお願いいたします。