• ベストアンサー

VBで配列の差分を取りたいのですが

VB2005を使用しています。 エクセルに2つのシートには更新前と更新後のデータがあって、 それの差分を取るために一度2次元配列に確保してから処理を行い たいのですが、配列を入れ子にして順にループ、比較させると時間がかかりすぎてしまいます。 どうしたらよいのでしょうか?

noname#68570
noname#68570

質問者が選んだベストアンサー

  • ベストアンサー
回答No.2

>配列を入れ子にして順にループ せっかくエクセルを利用しているのであれば、エクセルに比較部分を処理したらよいと思います。 「EXACT」というエクセルの文字列比較関数を利用しましょう。 それと、エクセルをループで読み取るだけでも時間がかると思いますが、データベースとしてエクセルブックを扱い、相違部分だけのデータを抽出できます。 下の例は ※ブック1に二つのシート「変更前」「変更後」後が存在している ※「変更前」「変更後」のそれぞれのA列を比較する ※比較結果を、別のエクセルブック2として保存する という処理になっています。 もし「変更前」「変更後」が別のブックである場合は、以下のコードを理解し、改造を加えてください。 Private Const DEF_ブックネタパス As String = "C:\Book1.xls" Private Const DEF_ブック結果パス As String = "C:\Book2.xls" Private Const DEF_シート変更前 As String = "変更前" Private Const DEF_シート変更後 As String = "変更後" Sub Main()   Dim l_dtb As DataTable = エクセル比較()   Dim l_strMsg As String = ""   If l_dtb.Columns.Count.Equals(0) Then     'カラムが存在しないということは、比較対象のデータ件数が0件     l_strMsg = "データが存在しない"   Else     Dim l_ary As New System.Collections.ArrayList     l_ary.Add(String.Format("データ相違数数{0}件", l_dtb.Rows.Count))     For Each l_drw As DataRow In l_dtb.Rows       Dim l_strWK As String       l_strWK = String.Format("{0}行目", l_drw.Item("F1").ToString())       l_strWK &= vbTab & String.Format("変更前「{0}」", l_drw.Item("F2").ToString())       l_strWK &= vbTab & String.Format("変更後「{0}」", l_drw.Item("F3").ToString())       l_ary.Add(l_strWK)     Next     l_strMsg = Join(CType(l_ary.ToArray(GetType(String)), String()), vbCrLf)   End If   MsgBox(l_strMsg) End Sub Function エクセル比較() As DataTable   Dim l_xlsApp As Object = CreateObject("Excel.Application")   l_xlsApp = CreateObject("Excel.Application")   'データの件数を取得する   Dim l_int件数 As Integer = 件数取得(l_xlsApp)   '比較情報ブックを作成する   比較作成(l_xlsApp, l_int件数)   'エクセルを閉じる   l_xlsApp.Quit()   Return 結果読み込み() End Function Function 件数取得(ByVal p_xlsApp As Object) As Integer   Dim l_xlsBook As Object = p_xlsApp.Workbooks.Open(DEF_ブックネタパス)   Dim l_xlsSheet1 As Object = l_xlsBook.Worksheets(DEF_シート変更前)   Dim l_xlsSheet2 As Object = l_xlsBook.Worksheets(DEF_シート変更後)   'シートのデータ件数の多い方を件数とする   Dim l_int件数 As Integer = System.Math.Max(データ範囲行(l_xlsSheet1), データ範囲行(l_xlsSheet2))   l_xlsBook.Close()   Return l_int件数 End Function Function データ範囲行(ByVal p_xlsCells As Object) As Integer   Dim l_xlsCellsTop As Object = p_xlsCells.Cells(1)   Dim l_xlsCellsBottom As Object = l_xlsCellsTop.SpecialCells(11)   Return l_xlsCellsBottom.Row End Function Sub 比較作成(ByVal p_xlsApp As Object, ByVal p_int件数 As Integer)   '結果ブックを削除する   If System.IO.File.Exists(DEF_ブック結果パス) Then     Kill(DEF_ブック結果パス)   End If   '件数が存在しない場合、処理を行わない   If p_int件数.Equals(0) Then     Exit Sub   End If   '比較用のワークシート   Dim l_xlsBook As Object = p_xlsApp.Workbooks.Add   '比較用のワークシート   Dim l_xlsSheet As Object = l_xlsBook.Worksheets(1)   Dim l_xlsRangeA As Object = l_xlsSheet.Cells(1, 1)   Dim l_xlsRangeB As Object = l_xlsSheet.Cells(1, 2)   Dim l_xlsRangeC As Object = l_xlsSheet.Cells(1, 3)   Dim l_xlsRangeD As Object = l_xlsSheet.Cells(1, 4)   'A1~D1   Dim l_xlsRangeFil1 As Object = l_xlsSheet.Range(l_xlsRangeA, l_xlsRangeD)   'A1~Dのデータ件数分   Dim l_xlsRangeFil2 As Object = l_xlsSheet.Range(l_xlsRangeA, l_xlsRangeD.Offset(p_int件数))   'ブックのファイル情報   Dim l_intFileInfo As New System.IO.FileInfo(DEF_ブックネタパス)   'ブックファイル名   Dim l_strBookName As String = l_intFileInfo.Name   'ブックファイルパス   Dim l_strBookPath As String = l_intFileInfo.FullName.Substring(0, l_intFileInfo.FullName.Length - l_intFileInfo.Name.Length)   '関数を作成するワーク   Dim l_strRep() As String = New String() {l_strBookPath, l_strBookName, DEF_シート変更前, DEF_シート変更後}   '比較を行うエクセル関数文字列   Dim l_str変更前セル As String = String.Format("'{0}[{1}]{2}'!RC[-1]", New String() {l_strBookPath, l_strBookName, DEF_シート変更前})   Dim l_str変更後セル As String = String.Format("'{0}[{1}]{2}'!RC[-2]", New String() {l_strBookPath, l_strBookName, DEF_シート変更後})   Dim l_str関数書式 As String = "=IF(ISNUMBER({0}),TEXT({0}, ""@""),T({0}))"      'A1に行番号   l_xlsRangeA.FormulaR1C1 = "=Row()"   'B1に変更前シートの情報   l_xlsRangeB.FormulaR1C1 = String.Format(l_str関数書式, l_str変更前セル)   'C1に変更後シートの情報   l_xlsRangeC.FormulaR1C1 = String.Format(l_str関数書式, l_str変更後セル)   'D1に比較結果   l_xlsRangeD.FormulaR1C1 = "=EXACT(RC[-2], RC[-1])"   'オートフィル機能により、比較結果をデータ件数分反映する   l_xlsRangeFil1.AutoFill(l_xlsRangeFil2)   '結果を保存する   l_xlsBook.Close(True, DEF_ブック結果パス) End Sub Public Function 結果読み込み() As DataTable   Dim l_dtbRet As New DataTable   If IO.File.Exists(DEF_ブック結果パス) Then     'ブックをデータベースとして接続する文字列     Dim l_strCn As String = String.Format("Provider=Microsoft.Jet.OLEDB.4.0;Data Source={0};Extended Properties='Excel 5.0;HDR=No;'", DEF_ブック結果パス)     '比較結果がFALSEのものだけを抽出(WHERE句をはずすと、全件取得)     Dim l_strSQL As String = "SELECT * FROM [Sheet1$] WHERE F4 = FALSE"     Dim l_fil As New System.Data.OleDb.OleDbDataAdapter(l_strSQL, l_strCn)     l_fil.Fill(l_dtbRet)   End If   Return l_dtbRet End Function

noname#68570
質問者

お礼

返事が遅くなり申し訳ありません。 大変参考になりました。 >せっかくエクセルを利用しているのであれば、エクセルに比較部分を処理したらよいと思います。 確かにおっしゃられている通り、EXCELのワークシート関数とか操作を上手く利用したほうが格段に効率が良いですね。 結局、同じシートにデータを貼り付けてソートしてフィルタして実現できました。 テーブルとして使えるのも始めて知りました。 ありがとうございます。

その他の回答 (1)

回答No.1

vbは、特に最近の.netのvbは知らないのですが・・・。 vba風で良ければこんなのはどうでしょう。 内容が詳しくわからないのですが、 >エクセルに2つのシートには更新前と更新後のデータがあって、 とあるので、同じブック内の2つのシートだとして、比較結果のシートを作ります。 2つのシートの同じ位置のデータを比較するとします。 book1.xlsのsheet1とsheet2の(1,1)-(10,10)の範囲を比較するとします。 vbで、formにButton1を配置します。 Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click 'bookを開く Dim wb As Object wb = CreateObject("Excel.Application").Workbooks.Open(Filename:="c:\book1.xls") '新しいシートを作って差分の計算式を代入(ここでは(1,1)-(10,10)の範囲 Dim ws As Object ws = wb.Worksheets.Add() ws.Range(ws.Cells(1, 1), ws.Cells(10, 10)).FormulaR1C1 = "=Sheet1!RC-Sheet2!RC" '各種操作 '配列に入れても良いが、そのままws.Cells(row, col).valueでアクセスできる '例えば結果表示 MessageBox.Show(ws.Cells(1, 1).value) '後始末 ws = Nothing wb.Close(savechanges:=False) wb = Nothing End Sub

noname#68570
質問者

お礼

回答していただきありがとうございました。 私の質問の内容がちょっとわかりずからったですね。 もうしわけございません。

関連するQ&A

  • VBAにて、配列のデータを一度にシート上に貼り付ける方法

    シートにデータを貼り付けるための高速化を検討しています。 現在、VBからエクセルの指定セルに直接データを書いていますが、遅くてしかたありません。  (こんな感じです ⇒ Cell(○、□) = データ) 一応、表示更新は、止めています。(ScreenUpdatingプロパティを使っています。) 配列のデータを一度に、あるシート上に貼り付けたいのですが、ループ処理で回すしか方法がありませんか? すいませんが、宜しくお願いします。

  • JavaのString配列で隣り合うデータを順に比較する

    JavaのString配列で隣り合うデータを順に比較する Javaのバージョンは1.5です。 配列の隣り合うデータを順に比較し、異なるデータがあったら フラグのカウントを+1したいです。(下記の配列の場合のカウントは3になります) 特に並べ替えは行いません。 String[7] color = {"aka","aka","aka","ao","ao","aka","kuro"} 配列のデータ数は固定なので、 for文などのループ処理で比較を繰り替えすものだとは思うのですが、 うまくカウントできません。 一つ目の不一致(3つ目と4つ目の比較)が見つかった後、 次の比較へ移るところ(4つ目と5つ目の比較)がうまくいきません どのように記述すればよいでしょうか?

    • ベストアンサー
    • Java
  • アクセスのテーブルの差分の抽出について

    アクセスの初心者です。今、仕事でデーターの差分の抽出の仕方についてわからず困っています。 2つのテーブルがあります。 テーブルには100以上の項目と500件ほどのレコードがそれぞれ入っています。 この2つのテーブルを比較して差分を出したいのですが方法が思いつきません。 出したいものは、テーブル1とテーブル2の共通のIDを比較し、 テーブル1からテーブル2で変更、追加されたものを出したいのです。 レコードの追加だけなら差分クエリを利用してやれると思うのですが、 100以上の全フィールドをそれぞれ比較するとなるとどうすれば良いのかわからないのです。 2つのテーブルの項目は基本一緒ですが、追加されたり、変更されたりします。 もともとはエクセルのデータで毎週やりとりされるもので、 エクセル上で手作業で、 シート1とシート2でマッチングを行い、シート3に追加、変更されたものを書き出しています。 100列ほどの項目も追加や変更されるので、前回データと今回データで (1)列の比較をして2つの列数を揃えてから、 (2)KEYになる列でVLOOKUP関数を使い、追加されたデータをよけて、 (3)前回データと今回データが一緒のデーター並べ替え、揃えて、シートを比較して変更、追加を探す という作業を行っています。 データー数が多く、エクセルでは限界がでてきました。 また、アクセスを使えば簡単にできる。と言われましたが、 まだまだ初心者のため、この大量のデータをどう処理すればいいのかわかりません。 質問は、 (1)アクセスで100項目以上あるテーブルのそれぞれの差分をだすことができるのでしょうか? (2)その方法はどうすればよいのでしょうか? また、アクセス初心者でも勉強すればすぐにできるのでしょうか? と、いうことです。 会社に迷惑をかけないためにも、可能、不可能を判断したいです。 よろしくお願いいたします。

  • VBで配列の初期化について

    VBやVBAで1次元・2次元配列を、プログラムの中の 値の(リテラル的)記述で初期化する方法はどうすれば良いのでしょうか。1次元配列にはx=Array()で代用してきましたが、さて、行と列がある表のような、値データを2次元配列にセットしようとして、ハテナと行き詰まりました。配列名(要素)=値を要素数だけ繰り返す方法以外に、例えば A[][]={(a、b、c)(d、e、f)(g,h,i)}のような、書き方は出来ないのでしょうか。 複数のArrayをArreyの()内に入れてみましたがも上手く行かないようですが。 有り無しについて教えていただけたらと思います。 市販の解説書数書には触れた記述は見つかりませんでした。

  • VB.netの配列とVB6の配列の違い

    VB6で画面を作成し、演算処理を行うDLLをVC6で作成しています。 下記のコードでVB6でSingle型の2次元配列を宣言しま、VCのDLLでエクスポートしている関数に渡 します。 VC6DLL側のコード---------- EXPORT void __stdcall TESTFUNC (float *pfData,long nSize{ } VB側のコード---------- 宣言 Declare Function TESTFUNC Lib "TEST.dll" (ByRef pfData As Single, ByVal nSize As Long) As Long 配列宣言 Public sngDat(999,1) as single 呼び出し TESTFUNC sngDat(0,0),1000 このように呼び出した場合、VC6のTESTFUNC にブレークポイントを設定し、停止するとpfDataの ポインタをインクリメントした場合、sngDat(0,0),sngDat(1,0),sngDat(2,0)と、1次元目の添え字を インクリメントした状態になるように、メモリに格納されています。 この動作を前提として、VB.net側でも同じように呼び出してみました。 VB.net側のコード---------- 宣言 Declare Function TESTFUNC Lib "TEST.dll" (ByRef pfData As Single, ByVal nSize As Integer) As Integer 配列宣言 Public sngDat(999,1) as single 呼び出し TESTFUNC(sngDat(0,0),1000) VC側は全く同じコードを利用するとします。 同じように、VC6のTESTFUNC にブレークポイントを設定し、停止するとpfDataの ポインタをインクリメントした場合、sngDat(0,0),sngDat(0,1),sngDat(1,0)と,sngDat(1,1)、と1次元目の添え字を インクリメントせず、2次元目の添え字をインクリメントした状態になるように、メモリに格納されています。 VB6→VC6のDLLの場合の配列渡しと、VB.net→VC6のDLLの場合の配列渡しで違いがあるのは 何故でしょうか?また、VB.net側の配列渡しの仕様を、VB6側の仕様に合わせる方法は無いのでしょうか? よろしくお願いいたします。

  • perlでの三次元配列の作り方

    perlで三次元配列をテキスト入力から作りたいのですが、例えば二次元配列の場合 foreach $line (@input) push @data, [split /[:]/, $line]; で@dataが二次元配列になるのですが、三次元配列の場合このあとに push @output, \@data; とするとリファレンスが同じであるためループをまわしてもうまく三次元になりませんよね。 解決法はありますでしょうか?

  • VBA でのシート内容を配列に保存した時の列の追加

    EXCEL VBAで開発をしています。 2つのEXCELファイルの各表(シート)にオーダーNo(ユニークキー)が あり、処理を書いています。 各表を二次元配列に保存して、行追加時に動的に配列を増やそうとしています。 その時、Redim Preserve を利用すると配列(行,列)と宣言しているので、第一引数にを追加しようとすると、”インデックスが有効範囲にありません”(-9)となりエラーとなります。 二次元配列で、第一引数は増やせない仕様ということは知っているのですが、このエラーを回避するためにどのようにするのが最善なのでしょうか? Excelシートのフィルタ昨日を利用したりして、他の方法で色々試したのですが、処理があまりにも重くて使えませんでした。 ですので、配列などのように内部処理にてデータ処理をして、 一気にシートへ出力という風に考えております。 何卒宜しくお願い致します。

  • VBでの配列をEXCELに出力する方法

    仕事で、VBでDBから取得した値を配列に設定し、それをEXCELで帳票として出力したいのですが、やり方が分かりません。ご存知の方がいましたら、教えていただけますか。 VBからCopyFromRecordsetでRecordsetをEXCELのrangeで出力する方法は見つけたのですが、Recordsetそのままではなく、動的配列に並び替えてEXCELに出力したいと思っています。 EXCELのシートをテーブルと見立てて、SQL文でEXCELにInsertする方法もある聞いたのですが、実際にどのように命令を書けばいいのか分かりません。 ご存知の方、教えていただけますか。

  • 配列入れ替えの効率的方法は

    VBでグローバル変数配列を3000とっています。 格納されているデータを、1つづつずらして代入し直しをやりたいのですが 配列添え字1のデータを配列添え字0にというふうに3000回のループで 処理する以外に良い方法はないでしょうか 例 for i=0 to 2999 a[i]=a[i+1]

  • VBから、エクセルでファイルを開くには・・・

    またまた、お世話になりますm(__)m 環境はVB.net2003、エクセルは2002でプログラミングの勉強、データ処理をしているのですが、VBでエクセルを開き、そのエクセル上でファイル(csvやテキストなど)を開きたいのですが、VB上でどのように書けばよいのでしょうか? VBでエクセルをたちあげたり、bookやsheetを開くことはできました。 どなたかご教授お願いしますm(__)m

専門家に質問してみよう