比较两个文本文件的最快方法是啥,而不是将移动的行计算为不同的
Posted
技术标签:
【中文标题】比较两个文本文件的最快方法是啥,而不是将移动的行计算为不同的【英文标题】:What is the fastest way to compare two text files, not counting moved lines as different比较两个文本文件的最快方法是什么,而不是将移动的行计算为不同的 【发布时间】:2015-12-10 11:39:00 【问题描述】:我有两个非常大的文件,每个文件 50000 行。我需要比较这两个文件并确定更改。但是,要注意的是,如果一条线出现在不同的位置,则不应显示为不同。
例如,考虑一下这个 文件 A.txt
xxxxx
yyyyy
zzzzz
文件 B.txt
zzzzz
xxxx
yyyyy
所以如果这是文件的内容。我的代码应该输出为 xxxx(或 xxxx 和 xxxxx)。
当然,最简单的方法是将文件的每一行存储在一个
List< String>
并与其他人比较
List< String>.
但这似乎需要很多时间。我也尝试过在 java 中使用 DiffUtils。但它不能将不同行号中的行识别为相同的行。那么还有其他算法可以帮助我吗?
【问题讨论】:
你在 Linux 中部署代码吗?? 也许你可以使用简单的数组(string[])。这会快得多。或者如果你想使用一个完成的实现,你可以使用 FileUtils.contentEquals(file1, file2);来自 org.apache.commons.io.FileUtils。 如果你正在寻找最快的方法然后从java调用linux的diff api就完成了 同一行可以在文件中出现多次吗?如果是这样,并且同一行在一个文件中出现一次,在另一个文件中出现两次,那么这些文件是否相同? @prashantthakre Ya 将部署在 linux 中。 【参考方案1】:一般来说 HashSet 是最好的解决方案,但是当我们处理字符串时,有两种可能的解决方案:
将一个文件保存为 HashSet 并尝试在其中查找其他文件的行。
将一个文件保存为 Trie 并尝试在其中查找其他文件的行
在这篇文章中,您可以找到 HashSets 和 Tries 之间的比较How Do I Choose Between a Hash Table and a Trie (Prefix Tree)?
【讨论】:
表的键和值是什么?为什么使用 Hashtable 而不是 HashSet? 哦,是的,JB Nizet 你是对的,HashSet 应该足够了。 也不考虑其他文件中存在额外谎言的可能性,或者考虑重复行。 @DJClayworth 为什么需要考虑重复行? 查看 cmets 的问题。【参考方案2】:可能使用Set
是最简单的方法:
Set<String> set1 = new HashSet<String>(FileUtils.readLines(file1));
Set<String> set2 = new HashSet<String>(FileUtils.readLines(file2));
Set<String> similars = new HashSet<String>(set1);
similars.retainAll(set2);
set1.removeAll(similars); //now set1 contains distinct lines in file1
set2.removeAll(similars); //now set2 contains distinct lines in file2
System.out.println(set1); //prints distinct lines in file1;
System.out.println(set2); //prints distinct lines in file2
【讨论】:
@DJClayworth 我们正在比较两个文件并试图在它们之间找到不同的行,我不明白为什么重复会是一个问题。 @downvoter,您能说明下否决票的原因吗? 这会比比较两个列表更快吗? 也在这一行 set2.removeAll(similars); //现在 set2 在 file2 中包含不同的行。 set2 如何包含不同的行?无论如何,similars 包含 set2 中的所有条目。 @saru10 50000 对于Set
伴侣来说不算什么,我这样做是为了数百万的文件大小。 50000 的过程应该在几秒钟内完成【参考方案3】:
您需要跟踪同一记录可能在文件中多次出现的情况。例如,如果一条记录在文件 A 中出现两次,在文件 B 中出现一次,则需要将其记录为额外记录。
由于我们必须跟踪出现的次数,因此您需要以下之一:
-
一个Multiset
从记录到整数的映射,例如地图
使用 Multiset,您可以添加和删除记录,它会跟踪记录的添加次数(Set 不会这样做 - 它拒绝添加已经存在的记录)。使用 Map 方法,您必须做一些工作,以便整数跟踪出现的次数。让我们考虑一下这种方法(MultiSet 更简单)。
对于地图,当我们谈论“添加”一条记录时,您会查看地图中是否有该字符串的条目。如果有,则将该键的值替换为 value+1。如果没有,则创建一个值为 1 的条目。当我们谈论“删除条目”时,请查找该键的条目。如果找到它,请将值替换为 value-1。如果这会将值减少到 0,则删除该条目。
-
为每个文件创建一个地图。
读取其中一个文件的记录
检查该记录是否存在于其他地图中。
如果它存在于其他地图中,请删除该条目(参见上文了解其含义)
如果不存在,请将其添加到此文件的地图中(见上文)
重复直到结束,交替文件。
这两个地图的内容将为您提供该文件中出现的记录,而不是另一个。
随着我们的进展这样做,而不是预先构建地图,可以降低内存使用量,但可能不会对性能产生很大影响。
【讨论】:
【参考方案4】:我认为这会很有用,
BufferedReader reader1 = new BufferedReader(new FileReader("C:\\file1.txt"));
BufferedReader reader2 = new BufferedReader(new FileReader("C:\\file2.txt"));
String line1 = reader1.readLine();
String line2 = reader2.readLine();
boolean areEqual = true;
int lineNum = 1;
while (line1 != null || line2 != null)
if(line1 == null || line2 == null)
areEqual = false;
break;
else if(! line1.equalsIgnoreCase(line2))
areEqual = false;
break;
line1 = reader1.readLine();
line2 = reader2.readLine();
lineNum++;
if(areEqual)
System.out.println("Two files have same content.");
else
System.out.println("Two files have different content. They differ at line "+lineNum);
System.out.println("File1 has "+line1+" and File2 has "+line2+" at line "+lineNum);
reader1.close();
reader2.close();
【讨论】:
【参考方案5】:您可以尝试先解析您的第一个文件,将所有行存储在 HashMap 中,然后检查第二个文件的每一行是否存在映射。
不过,这仍然是 O(n)。
【讨论】:
不检查第一个文件中是否有不在第二个文件中的行。不检查同一行在两个文件中出现的次数是否相同。 可以创建一个实现来检查那些使用 HashMap(或者,正如其他人指出的那样,一个 HashSet,这可能已经足够了)。这需要更多的努力,但它可能仍然比创建两个列表并比较每个条目要好。【参考方案6】:只需使用 BufferedReader 进行字节比较。这将是比较两个文件的最快方法。从一个文件中读取一个字节块并将其与另一个文件的字节块进行比较。首先检查文件长度是否相同。
或者只使用org.apache.commons.io.FileUtils
中的FileUtils.contentEquals(file1, file2);
。
【讨论】:
不允许同一行在不同的位置。【参考方案7】:你可以使用 FileUtils.contentEquals(file1, file2)
它将比较两个文件的内容。
查找更多信息here
【讨论】:
不允许以不同的顺序排列相同的行。以上是关于比较两个文本文件的最快方法是啥,而不是将移动的行计算为不同的的主要内容,如果未能解决你的问题,请参考以下文章