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如果重复行非常常见,那么我认为解决问题的正确方法与您的方法类似,但是您必须使用一个哈希表,该哈希表可以根据需要增长并自动处理冲突。尝试使用Python
set
数据类型来存储已经到达的行。使用
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