![](/img/trans.png)
[英]Performance comparing and doing querys with two really big CSV files in JAVA
[英]Comparing two csv files in Java [closed]
我们需要比较两个 CSV 文件。 假设第一个文件有几行,第二个文件可以有相同的行数或更多。 两个文件上的大多数行都可以保持不变。寻找在这两个文件之间进行差异的最佳方法,并仅读取第二个文件中与第一个文件不同的那些行。 处理文件的应用程序是在 Java 中的。
什么是最好的方法?
注意:如果我们能知道第二个文件中的一行被更新、插入或删除,那就太好了。
要求:-
这样做的一种方法是使用 java 的Set
接口; 将每一行作为字符串读取,将其添加到集合中,然后对第一组中的第二组执行removeAll()
,从而保留不同的行。 当然,这假设文件中没有重复的行。
// 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
,使用 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
读取整个第一个文件,并将其放入List
。 然后一次读取第二个文件,并将每一行与第一个文件的所有行进行比较,看它是否重复。 如果它不是重复的,那么它就是新信息。 如果您在阅读时遇到问题,请查看http://opencsv.sourceforge.net/ ,这是一个非常好的用 Java 读取 CSV 文件的库。
尝试使用java-diff-utils库
我使用 groovy 来快速演示 Java 库:
两个示例文件之间报告了以下差异:
$ 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]]
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
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
//
// 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 常见问题解答,可以通过使用 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")
}
}
有一个程序可以比较/减去两个 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();
}
}}
你提到检测“更新”的行。 我想这意味着一行在更新后以某种方式具有身份。 也许单列或复合列提供标识。 这是您个人需要整理和实施的实施细节,它只会为您的解决方案添加更多代码。
无论如何......数据库往往对使用集合数据和从 csv 文件加载数据有很好的支持。 所有大牌关系数据库都支持简单的语法,将 csv 文件中的数据加载到表中。 此时,在两个表之间查找新行或修改行是非常简单的 sql 查询。
它显然不是纯 Java 解决方案,但我认为值得一提。
如果您想比较存储在字符串变量中的两个 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);
}
}
我的建议:
您可以读取文件以创建由 分隔的令牌,并从两侧修剪每个令牌,以便处理额外的空间,然后将它们存储在有序的数据结构中(类似于链接散列集、链接散列映射等(如果您想通过文件中的重复项以防万一),然后对另一个文件重复此操作。
Java 提供了许多实用方法来比较这些数据结构。 :)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.