Java 从文件中删除重复的行
我有以下数据:Java 从文件中删除重复的行,java,python,c++,linux,multithreading,Java,Python,C++,Linux,Multithreading,我有以下数据: number1 I am writing line1 . number2 First line . number3 I am writing line2. number4 Second line . number5 I am writing line3 . number6 Third line. number7 I am writing line2 . number8 Fourth line . number9 I am writing lin
number1
I am writing line1 .
number2
First line .
number3
I am writing line2.
number4
Second line .
number5
I am writing line3 .
number6
Third line.
number7
I am writing line2 .
number8
Fourth line .
number9
I am writing line5 .
number10
Fifth line .
现在我想从这个文本文件中删除重复的行——同时我想删除重复行的前一行和后两行。这样,在删除数据后,我的数据如下所示:
number1
I am writing line1 .
number2
First line .
number3
I am writing line2.
number4
Second line .
number5
I am writing line3 .
number6
Third line.
number9
I am writing line5 .
number10
Fifth line .
我的文件大小为60 GB,我使用的服务器具有64 GB RAM。我使用以下代码删除重复项:
fOutput = open('myfile','w')
table_size = 2**16
seen = [False]*table_size
infile = open('test.ttl', 'r')
while True:
inFileLine1=infile.readline()
if not inFileLine1:
break #EOF
inFileLine2=infile.readline()
inFileLine3=infile.readline()
inFileLine4=infile.readline()
h = hash(inFileLine2) % table_size
if seen[h]:
dup = False
with open('test.ttl','r') as f:
for line1 in f:
if inFileLine2 == line1:
dup = True
break
if not dup:
fOutput.write(inFileLine1)
fOutput.write(inFileLine2)
fOutput.write(inFileLine3)
fOutput.write(inFileLine4)
else:
seen[h] = True
fOutput.write(inFileLine1)
fOutput.write(inFileLine2)
fOutput.write(inFileLine3)
fOutput.write(inFileLine4)
fOutput.close()
然而,事实证明这段代码非常慢。是否有某种方法可以提高代码的效率,使用并行化,即在我的系统上使用所有可用的24个内核,或使用任何其他技术
虽然上面的代码是用Python编写的,但是我对C++或Python或java的高效解决方案或使用Linux命令
很好。这里test.ttl是我的输入文件,大小为60GB似乎您的代码每行只读取一次,每行(需要写入的)也只写入一次。因此,无法在文件读写部分优化算法 我强烈怀疑您的代码是缓慢的,因为哈希表的使用非常糟糕。哈希表的大小只有2^16,而文件可能包含约2^28行,假设平均每行240字节 由于您有这么大的RAM(足以容纳所有文件),我建议您将哈希表的大小更改为2^30。这应该会有很大帮助 编辑: 在本例中,您可以尝试使用一些非常简单的哈希函数。例如:
long long weight[] = {generate some random numbers};
long long Hash(char * s, int length)
{
long long result = 0;
int i = 0, j = 0;
while (i < length)
{
result += s[i] * weight[j ++];
i += j;
}
return result & ((1 << 30) - 1); // assume that your hash table has size 2^30
}
number1
I am writing line1 .
number2
First line .
number3
I am writing line2.
number4
Second line .
long-long-weight[]={生成一些随机数};
长哈希(字符*s,整数长度)
{
长结果=0;
int i=0,j=0;
while(i 返回结果&((1如果重复行非常常见,那么我认为解决问题的正确方法与您的方法类似,但是您必须使用一个哈希表,该哈希表可以根据需要增长并自动处理冲突。尝试使用Pythonset
数据类型来存储已经到达的行。使用set
您将不需要确认重复的行确实是重复的;如果它们已经在集合中,那么它们肯定是重复的。这将起作用,并且非常有效。但是,Python的内存管理可能不是非常有效,并且集合
数据类型可能会超出可用内存,在这种情况下,需要重新考虑.试试看
编辑:好的,所以设置
太大了
对于一个好的解决方案,您希望避免重复读取输入文件。在原始解决方案中,对于每个可能的重复,都会再次读取输入文件,因此,如果有N行,则读取的总行数可能高达N^2。优化(分析)并行性并不能改善这一点。而且,由于巨大的文件大小,您还有一个内存限制,它排除了一些简单的技巧,例如将迄今为止看到的所有行存储在哈希表中(如集
)
这是我的第二个建议。在这个建议中,内存需求将根据您的可用容量进行扩展。您将需要足够的磁盘空间来存储至少一个输入文件副本。这些步骤形成一个管道-一个步骤的输出就是下一个步骤的输入
步骤1。我认为您对4行一组的工作感兴趣。您希望保留整个4行一组,或者不保留其中任何一行。您的第一步应该是将每组4行合并为一行。例如:
long long weight[] = {generate some random numbers};
long long Hash(char * s, int length)
{
long long result = 0;
int i = 0, j = 0;
while (i < length)
{
result += s[i] * weight[j ++];
i += j;
}
return result & ((1 << 30) - 1); // assume that your hash table has size 2^30
}
number1
I am writing line1 .
number2
First line .
number3
I am writing line2.
number4
Second line .
变成
number1#I am writing line1 .#number2#First line .
number3#I am writing line2 .#number4#Second line .
请注意,我使用“#”来标记换行符的位置。这一点很重要。您可以在此处使用任何字符,前提是输入文件中的任何其他位置都不使用该字符
第2步。在每行前面加上行号
1#number1#I am writing line1 .#number2#First line .
2#number3#I am writing line2 .#number4#Second line .
第3步。使用Unix排序
实用程序(或其Windows端口)。该实用程序已经进行了高度优化。甚至可以选择并行排序以提高速度。使用以下选项进行排序:
sort '-t#' -k3
这些<代码>排序< /代码>选项导致程序只考虑第三个字段——这是每组中的第二行。< /P>
步骤4。现在逐步完成前一阶段的输出,利用重复项彼此相邻的事实,查找重复项。查看第三个字段。如果发现重复行,则丢弃它
步骤5。使用另一种排序重新构建原始文件的顺序:
sort '-t#' -k1 -n
这一次,排序使用行号的数值(第一个字段)
步骤6。从每行的开头删除行号
步骤7。将每个“#”字符变回换行符。工作完成
虽然这看起来像是很多步骤,但除了步骤3和步骤5外,所有步骤都只涉及对输入文件的一次遍历,因此它们将非常快。N行的N个步骤。排序步骤(3和5)也很快,因为sort
程序经过了大量优化,并使用了良好的排序算法(N行最多N个log N个步骤)查看Java。它被设计为在多个线程同时访问映射时运行良好
另外,使用JavaNIO通过Executor固定线程池读取文件
首先,您可以使用此代码
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Main {
private static final ConcurrentHashMap map = new ConcurrentHashMap();
public static class Task implements Runnable {
private final String line;
public Task(String line) {
this.line = line;
}
@Override
public void run() {
// if (!map.containsKey(line)) // not needed
map.put(line, true);
}
}
public static void main(String[] args) throws IOException {
ExecutorService service = Executors.newFixedThreadPool(10);
String dir_path, file_name;
Files.lines(Paths.get(dir_path, file_name)).forEach(l -> service.execute(new Task(l)));
service.shutdown();
map.keySet().forEach(System.out::println);
}
}
我更愿意使用Java来实现这一点。考虑到文件的大小是60 GB,Java为这个名为MappedByteBuffer的文件提供了一个非常适合的API。
使用文件通道加载文件,并使用上述API映射通道,如下所示:
FileChannel FileChannel=newrandomAccessFile(新文件(inputFile),“r”).getChannel();
mappedBuffer=fileChannel.map(fileChannel.MapMode.READ_ONLY,0,fileChannel.size());
这会将整个文件加载到内存中。为了获得最佳效率,请将其映射为块(加载50k字节)
mappedBuffer=fi