比较两个文本文件的最快方法是什么,而不是将移动的行计算为不同
What is the fastest way to compare two text files, not counting moved lines as different
我有两个非常大的文件,每个文件有 50000 行。我需要比较这两个文件并确定更改。但是,要注意的是,如果一条线出现在不同的位置,则不应显示为不同。
例如,考虑这个
文件 A.txt
xxxxx
yyyyy
zzzzz
文件B.txt
zzzzz
xxxx
yyyyy
如果这是文件的内容。我的代码应该将输出作为 xxxx(或 xxxx 和 xxxxx)。
当然,最简单的方法是将文件的每一行存储在一个
List< String>
并与对方比较
List< String>.
但这似乎花费了很多时间。我也尝试过在 java 中使用 DiffUtils。但它不会将不同行号中存在的行识别为相同。那么还有其他算法可以帮助我吗?
一般来说,HashSet 是最好的解决方案,但由于我们处理的是字符串,因此有两种可能的解决方案:
将一个文件保存为 HashSet 并尝试在其中查找其他文件的行。
将一个文件保存为 Trie 并尝试在其中查找其他文件的行
在此 post 中,您可以找到 HashSet 和 Tries 之间的比较 How Do I Choose Between a Hash Table and a Trie (Prefix Tree)?
您可以先尝试解析第一个文件,将所有行存储在 HashMap 中,然后检查第二个文件的每一行是否存在映射。
虽然这仍然是 O(n)。
只需使用 BufferedReader 进行字节比较。这将是比较两个文件的最快方法。从一个文件中读取一个字节块,并将其与另一个文件的字节块进行比较。先检查文件长度是否相同
或者只使用 org.apache.commons.io.FileUtils
中的 FileUtils.contentEquals(file1, file2);
。
可能使用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
您可以使用 FileUtils.contentEquals(file1, file2)
它将比较两个文件的内容。
查找更多信息here
您需要跟踪同一记录可能在文件中出现多次的情况。例如,如果一条记录在文件 A 中出现两次,在文件 B 中出现一次,那么您需要将其记录为额外记录。
由于我们必须跟踪出现的次数,您需要以下之一:
- 一个Multiset
- 从记录到整数的映射,例如地图
使用 Multiset,您可以添加和删除记录,它会跟踪添加记录的次数(Set 不会这样做 - 它拒绝添加已经存在的记录).使用 Map 方法,您必须做一些工作,以便整数跟踪出现的次数。让我们考虑一下这种方法(MultiSet 更简单)。
有了地图,当我们谈论 'adding' 一条记录时,您会查看地图中是否有该字符串的条目。如果有,则用该键的 value+1 替换该值。如果没有,则创建值为 1 的条目。当我们谈论 'removing an entry' 时,查找该键的条目。如果找到它,请将值替换为 value-1。如果这会将值减小为 0,则删除该条目。
- 为每个文件创建一个地图。
- 读取其中一个文件的记录
- 检查该记录是否存在于另一个 Map 中。
- 如果它存在于另一个 Map 中,则删除该条目(请参阅上文了解其含义)
- 如果不存在,请将其添加到此文件的地图中(见上文)
- 重复直到结束,交替文件。
这两个地图的内容将为您提供那个文件中出现的记录,而不是另一个文件中出现的记录。
在我们进行的过程中这样做,而不是预先构建地图,可以降低内存使用率,但可能不会对性能产生太大影响。
我认为这会很有用,
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();
我有两个非常大的文件,每个文件有 50000 行。我需要比较这两个文件并确定更改。但是,要注意的是,如果一条线出现在不同的位置,则不应显示为不同。
例如,考虑这个
文件 A.txt
xxxxx
yyyyy
zzzzz
文件B.txt
zzzzz
xxxx
yyyyy
如果这是文件的内容。我的代码应该将输出作为 xxxx(或 xxxx 和 xxxxx)。
当然,最简单的方法是将文件的每一行存储在一个
List< String>
并与对方比较
List< String>.
但这似乎花费了很多时间。我也尝试过在 java 中使用 DiffUtils。但它不会将不同行号中存在的行识别为相同。那么还有其他算法可以帮助我吗?
一般来说,HashSet 是最好的解决方案,但由于我们处理的是字符串,因此有两种可能的解决方案:
将一个文件保存为 HashSet 并尝试在其中查找其他文件的行。
将一个文件保存为 Trie 并尝试在其中查找其他文件的行
在此 post 中,您可以找到 HashSet 和 Tries 之间的比较 How Do I Choose Between a Hash Table and a Trie (Prefix Tree)?
您可以先尝试解析第一个文件,将所有行存储在 HashMap 中,然后检查第二个文件的每一行是否存在映射。
虽然这仍然是 O(n)。
只需使用 BufferedReader 进行字节比较。这将是比较两个文件的最快方法。从一个文件中读取一个字节块,并将其与另一个文件的字节块进行比较。先检查文件长度是否相同
或者只使用 org.apache.commons.io.FileUtils
中的 FileUtils.contentEquals(file1, file2);
。
可能使用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
您可以使用 FileUtils.contentEquals(file1, file2)
它将比较两个文件的内容。
查找更多信息here
您需要跟踪同一记录可能在文件中出现多次的情况。例如,如果一条记录在文件 A 中出现两次,在文件 B 中出现一次,那么您需要将其记录为额外记录。
由于我们必须跟踪出现的次数,您需要以下之一:
- 一个Multiset
- 从记录到整数的映射,例如地图
使用 Multiset,您可以添加和删除记录,它会跟踪添加记录的次数(Set 不会这样做 - 它拒绝添加已经存在的记录).使用 Map 方法,您必须做一些工作,以便整数跟踪出现的次数。让我们考虑一下这种方法(MultiSet 更简单)。
有了地图,当我们谈论 'adding' 一条记录时,您会查看地图中是否有该字符串的条目。如果有,则用该键的 value+1 替换该值。如果没有,则创建值为 1 的条目。当我们谈论 'removing an entry' 时,查找该键的条目。如果找到它,请将值替换为 value-1。如果这会将值减小为 0,则删除该条目。
- 为每个文件创建一个地图。
- 读取其中一个文件的记录
- 检查该记录是否存在于另一个 Map 中。
- 如果它存在于另一个 Map 中,则删除该条目(请参阅上文了解其含义)
- 如果不存在,请将其添加到此文件的地图中(见上文)
- 重复直到结束,交替文件。
这两个地图的内容将为您提供那个文件中出现的记录,而不是另一个文件中出现的记录。
在我们进行的过程中这样做,而不是预先构建地图,可以降低内存使用率,但可能不会对性能产生太大影响。
我认为这会很有用,
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();