10

我们需要比较两个 CSV 文件。假设文件一有几行,第二个文件可以有相同的行数或更多。大多数行在两个文件上都可以保持相同。寻找在这两个文件之间进行差异的最佳方法,并仅读取第二个文件与第一个文件有差异的那些行。处理该文件的应用程序使用 Java。

什么是最好的方法?

注意:如果我们能知道在第二个文件中更新、插入或删除了一行,那就太好了。

要求:-

  1. 不会有任何重复记录
  2. 文件 1 和文件 2 的记录数可能相同,文件 2 中有几行更新值(记录已更新)
  3. 文件 2 可能删除了几行(这被视为已删除记录)
  4. 文件 2 可能添加了一些新行(这被视为插入记录)
  5. 列中的一个可以被视为记录的主键,这在两个文件中都不会改变。
4

7 回答 7

10

一种方法是使用 java 的Set接口;将每一行读取为一个字符串,将其添加到集合中,然后removeAll()在第一组上对第二组执行 a ,从而保留不同的行。当然,这假设文件中没有重复的行。

// 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 或正则表达式或任何你想要的。做一个实际的HashMap而不是HashSet上面的 a,使用 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. 然后一次读取第二个文件一行,并将每一行与第一个文件的所有行进行比较,看看它是否重复。如果它不是重复的,那么它就是新信息。如果您在阅读时遇到问题,请查看http://opencsv.sourceforge.net/,它是一个非常好的用于在 Java 中读取 CSV 文件的库。

于 2012-06-02T18:01:40.643 回答
3

Try using the java-diff-utils library

Example

I use groovy for quick demos of java libraries:

The following differences are reported between two sample files:

$ 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
}

Update

According to the dbunit FAQ performance of this solution can be improved for very large datasets by using a streamed revision of the ResultSetTableFactory interface. This is enabled within the ANT task as follows:

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

有一个程序可以比较/减去两个 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

你提到检测“更新”的行。我想这意味着一行在更新后以某种方式具有身份。也许单列或复合列提供标识。那是您个人需要整理和实施的实施细节,它只会为您的解决方案添加更多代码。

无论如何...数据库往往对处理集合数据和从 csv 文件加载数据有很好的支持。所有大名鼎鼎的关系数据库都以简单的语法将 csv 文件中的数据加载到表中提供了强大的支持。此时,在两个表之间查找新行或修改的行是非常简单的 sql 查询。

它显然不是一个纯 Java 解决方案,但我认为值得一提。

于 2012-06-02T18:22:27.087 回答
0

如果您想比较存储在字符串变量中的两个 csv 响应(如果您通过 REST 调用获取它们),我的简单解决方案。就我而言,我想在 10 个不同行的阈值后退出检查。

        BufferedReader baseline = new BufferedReader(new StringReader(responseBaseline));
        BufferedReader tested = new BufferedReader(new StringReader(responseTested));
        String lineBaseline = null;
        String lineTested = null;
        boolean linesExist = true;
        boolean foundDiff = false;
        int lineNumber = 0;
        int errorNumber = 0;
        int errorThreshold = 10;
        String message = "";
        while (linesExist) {
            try {
                lineBaseline = baseline.readLine();
                lineTested = tested.readLine();
                lineNumber++;
                if ((lineBaseline != null) && (lineTested != null)) {
                    if (!lineTested.equals(lineBaseline)) {
                        foundDiff = true;
                        errorNumber++;
                        if (errorNumber > errorThreshold) {
                            message = message + "\r\n" + "Found more than " + errorThreshold + " lines that were different. Will exit check.";
                            break;
                        }
                        message = message + "\r\n" + "\r\n#Found differences for line number " + lineNumber + "\r\nLine baseline: " + lineBaseline + "\r\nLine tested: " + lineTested;
                    }
                } else {
                    linesExist = false;
                }
            } catch (IOException e) {
                throw new Error("Problems with reading csv files");
            }
        }
        if (foundDiff) {
            throw new Error("Found differences between csv files. " + message);
        }
    }
于 2018-12-06T08:04:25.577 回答
-1

我的建议:

您可以读取文件以创建由 分隔的标记,并从两侧修剪每个标记,以便处理额外的空间,然后将它们存储在有序的数据结构中(类似于链接哈希集、链接哈希映射等(如果您想通过文件中的重复项(如果有的话),然后对另一个文件重复它。

Java 提供了许多实用方法来比较这些数据结构。:)

于 2012-06-02T18:14:12.983 回答