10

2 つの CSV ファイルを比較する必要があります。ファイル1にいくつかの行があり、2番目のファイルに同じ数またはそれ以上の行があるとします。ほとんどの行は両方のファイルで同じままである可​​能性があります。これら 2 つのファイルの差分を作成し、最初のファイルから 2 番目のファイルに違いがある行のみを読み取るための最良の方法を探しています。ファイルを処理するアプリケーションは Java です。

これに最適なアプローチは何ですか?

注 : 2 番目のファイルで行が更新、挿入、または削除されたことがわかれば、それは素晴らしいことです。

要件:-

  1. 重複するレコードはありません
  2. ファイル 1 とファイル 2 は、ファイル 2 の更新された値を持ついくつかの行を持つ同じ数のレコードを持つことができます (更新されたレコード)
  3. ファイル 2 では数行が削除される可能性があります (これはレコードの削除として扱われます)。
  4. ファイル 2 にはいくつかの新しい行が追加される可能性があります (これはレコードの挿入として扱われます)。
  5. 列の 1 つをレコードの主キーとして扱うことができますが、これは両方のファイルで変更されません。
4

7 に答える 7

10

これを行う 1 つの方法は、Java のSetインターフェースを使用することです。各行を文字列として読み取り、それをセットに追加してからremoveAll()、最初のセットで 2 番目のセットを使用して、異なる行を保持します。もちろん、これはファイル内に重複する行がないことを前提としています。

// using FileUtils to read in the files.
HashSet<String> f1 = new HashSet<String>(FileUtils.readLines("file1.csv"));
HashSet<String> f2 = new HashSet<String>(FileUtils.readLines("file2.csv"));
f1.removeAll(f2); // f1 now contains only the lines which are not in f2

アップデート

さて、これで PK フィールドができました。文字列からそれを取得する方法を知っていると仮定します。openCSV または regex など、必要なものを使用してください。HashMap上記のように a の代わりに実際のHashSetを作成し、PK をキーとして使用し、行を値として使用します。

HashMap<String, String> f1 = new HashMap<String, String>();
HashMap<String, String> f2 = new HashMap<String, String>();
// read f1, f2; use PK field as the key
List<String> deleted = new ArrayList<String>();
List<String> updated = new ArrayList<String>();
for(Map.Entry<String, String> entry : f1.keySet()) {
    if(!f2.containsKey(entry.getKey()) {
        deleted.add(entry.getValue());
    } else {
        if(!f2.get(entry.getKey().equals(f1.getValue())) {
            updated.add(f1.getValue());
        }
    }
}
for(String key : f1.keySet()) {
    f2.remove(key);
}
// f2 now contains only "new" rows
于 2012-06-02T18:00:53.990 に答える
5

最初のファイル全体を読み取り、List. 次に、2 番目のファイルを 1 行ずつ読み取り、各行を最初のファイルのすべての行と比較して、重複しているかどうかを確認します。重複していない場合は、新しい情報です。読み取りに問題がある場合は、http://opencsv.sourceforge.net/を参照してください。これは、Java で CSV ファイルを読み取るためのかなり優れたライブラリです。

于 2012-06-02T18:01:40.643 に答える
3

java-diff-utilsライブラリを使用してみてください

Java ライブラリの簡単なデモには groovy を使用します。

2 つのサンプル ファイル間で次の相違点が報告されています。

$ groovy diff
[ChangeDelta, position: 0, lines: [1,11,21,31,41,51] to [1,11,99,31,41,51]]
[DeleteDelta, position: 2, lines: [3,13,23,33,43,53]]
[InsertDelta, position: 5, lines: [6,16,26,36,46,56]]

files1.csv

1,11,21,31,41,51
2,12,22,32,42,52
3,13,23,33,43,53
4,14,24,34,44,54
5,15,25,35,45,55

file2.csv

1,11,99,31,41,51
2,12,22,32,42,52
4,14,24,34,44,54
5,15,25,35,45,55
6,16,26,36,46,56

diff.groovy

//
// Dependencies
// ============
import difflib.*

@Grapes([
    @Grab(group='com.googlecode.java-diff-utils', module='diffutils', version='1.2.1'),
])

//
// Main program
// ============
def original = new File("file1.csv").readLines()
def revised  = new File("file2.csv").readLines()

Patch patch = DiffUtils.diff(original, revised)

patch.getDeltas().each {
    println it
}

アップデート

dbunit FAQによると、このソリューションのパフォーマンスは、ResultSetTableFactory インターフェイスのストリーミングされたリビジョンを使用することにより、非常に大きなデータセットに対して改善できます。これは、次のように ANT タスク内で有効にされます。

ant.dbunit(driver:driver, url:url, userid:user, password:pass) {
    compare(src:"dbunit.xml", format:"flat")
    dbconfig {
        property(name:"datatypeFactory", value:"org.dbunit.ext.h2.H2DataTypeFactory")
        property(name:"resultSetTableFactory", value:"org.dbunit.database.ForwardOnlyResultSetTableFactory")
    }
}
于 2012-06-02T23:21:46.763 に答える
2

2 つの CSV ファイルを比較/減算するプログラムがあります。ArrayList を使用します

import java.io.*;
import java.util.ArrayList;

/* file1 - file2 = file3*/
public class CompareCSV {
public static void main(String args[]) throws FileNotFoundException, IOException
{
    String path="D:\\csv\\";
    String file1="file1.csv";
    String file2="file2.csv";
    String file3="p3lang.csv";
    ArrayList al1=new ArrayList();
    ArrayList al2=new ArrayList();
    //ArrayList al3=new ArrayList();

    BufferedReader CSVFile1 = new BufferedReader(new FileReader(path+file1));
    String dataRow1 = CSVFile1.readLine();
    while (dataRow1 != null)
    {
        String[] dataArray1 = dataRow1.split(",");
        for (String item1:dataArray1)
        { 
           al1.add(item1);
        }

        dataRow1 = CSVFile1.readLine(); // Read next line of data.
    }

     CSVFile1.close();

    BufferedReader CSVFile2 = new BufferedReader(new FileReader(path+file2));
    String dataRow2 = CSVFile2.readLine();
    while (dataRow2 != null)
    {
        String[] dataArray2 = dataRow2.split(",");
        for (String item2:dataArray2)
        { 
           al2.add(item2);

        }
        dataRow2 = CSVFile2.readLine(); // Read next line of data.
    }
     CSVFile2.close();

     for(String bs:al2)
     {
         al1.remove(bs);
     }

     int size=al1.size();
     System.out.println(size);

     try
        {
            FileWriter writer=new FileWriter(path+file3);
            while(size!=0)
            {
                size--;
                writer.append(""+al1.get(size));
                writer.append('\n');
            }
            writer.flush();
            writer.close();
        }
        catch(IOException e)
        {
            e.printStackTrace();
        }
}}

http://p3lang.com/subtract-one-csv-from-another-in-Java/

于 2013-04-24T06:37:35.767 に答える
0

「更新された」行の検出について言及しました。これは、行が更新後も存続する何らかの形で ID を持っていることを意味していると思います。おそらく、単一の列または複合列が ID を提供します。これは、個人的に整理して実装する必要がある実装の詳細であり、ソリューションにコードを追加するだけです。

とにかく...データベースは、設定されたデータの操作と csv ファイルからのデータの読み込みを適切にサポートする傾向があります。すべての有名なリレーショナル データベースは、csv ファイル内のデータをテーブルにロードするための簡単な構文を備えた優れたサポートを備えています。その時点で、2 つのテーブル間で新しい行または変更された行を見つけることは、非常に単純な SQL クエリです。

明らかに純粋なJavaソリューションではありませんが、言及する価値があると思います。

于 2012-06-02T18:22:27.087 に答える
-1

私が提案すること:

ファイルを読み取って で区切られたトークンを作成し、余分なスペースが処理されるように両側から各トークンをトリミングしてから、それらを順序付けられたデータ構造に格納できます (リンクされたハッシュ セット、リンクされたハッシュ マップなどと同様 (ファイルに重複がある場合に備えてファイルに重複を渡したい場合)、他のファイルに対してそれを繰り返します。

Java には、これらのデータ構造を比較するためのユーティリティ メソッドが多数用意されています。:)

于 2012-06-02T18:14:12.983 に答える