处理大型文本文件的有效方法是什么?

时间:2010-12-08 22:10:51

标签: java file-io text-files

我有两个文件:
1- 1400,000行或记录--- 14 MB
2-与16000000 - 170 MB

我想查找文件1中的每个记录或行是否也在文件2中

我开发了一个执行以下操作的java应用程序:逐行读取文件并将每行传递给在文件2中循环的方法

这是我的代码:

public boolean hasIDin(String bioid) throws Exception {

    BufferedReader br = new BufferedReader(new FileReader("C://AllIDs.txt"));
    long bid = Long.parseLong(bioid);
    String thisLine;
    while((thisLine = br.readLine( )) != null)
    {
         if (Long.parseLong(thisLine) == bid)
            return true;

    }
        return false;
    }

public void getMBD() throws Exception{

     BufferedReader br = new BufferedReader(new FileReader("C://DIDs.txt"));
     OutputStream os = new FileOutputStream("C://MBD.txt");
     PrintWriter pr = new PrintWriter(os);
     String thisLine;
     int count=1;
     while ((thisLine = br.readLine( )) != null){
         String bioid = thisLine;
         System.out.println(count);
         if(! hasIDin(bioid))
                pr.println(bioid);
     count++;
     }
    pr.close();
}  

当我运行它似乎需要更多1944.44444444444小时才能完成,因为每行处理需要5秒。那是大约三个月!

是否有任何想法可以在更短的时间内完成。

提前致谢。

4 个答案:

答案 0 :(得分:5)

你为什么不;

  • 将file2中的所有行读入一个集合。设置很好,但TLongHashSet会更有效率。
  • 对于第二个文件中的每一行,查看它是否在集合中。

这是一个调整的实现,它打印以下内容并使用< 64 MB。

Generating 1400000 ids to /tmp/DID.txt
Generating 16000000 ids to /tmp/AllIDs.txt
Reading ids in /tmp/DID.txt
Reading ids in /tmp/AllIDs.txt
Took 8794 ms to find 294330 valid ids

代码

public static void main(String... args) throws IOException {
    generateFile("/tmp/DID.txt", 1400000);
    generateFile("/tmp/AllIDs.txt", 16000000);

    long start = System.currentTimeMillis();
    TLongHashSet did = readLongs("/tmp/DID.txt");
    TLongHashSet validIDS = readLongsUnion("/tmp/AllIDs.txt",did);

    long time = System.currentTimeMillis() - start;
    System.out.println("Took "+ time+" ms to find "+ validIDS.size()+" valid ids");
}

private static TLongHashSet readLongs(String filename) throws IOException {
    System.out.println("Reading ids in "+filename);
    BufferedReader br = new BufferedReader(new FileReader(filename), 128*1024);
    TLongHashSet ids = new TLongHashSet();
    for(String line; (line = br.readLine())!=null;)
        ids.add(Long.parseLong(line));
    br.close();
    return ids;
}

private static TLongHashSet readLongsUnion(String filename, TLongHashSet validSet) throws IOException {
    System.out.println("Reading ids in "+filename);
    BufferedReader br = new BufferedReader(new FileReader(filename), 128*1024);
    TLongHashSet ids = new TLongHashSet();
    for(String line; (line = br.readLine())!=null;) {
        long val = Long.parseLong(line);
        if (validSet.contains(val))
            ids.add(val);
    }
    br.close();
    return ids;
}

private static void generateFile(String filename, int number) throws IOException {
    System.out.println("Generating "+number+" ids to "+filename);
    PrintWriter pw = new PrintWriter(new BufferedWriter(new FileWriter(filename), 128*1024));
    Random rand = new Random();
    for(int i=0;i<number;i++)
        pw.println(rand.nextInt(1<<26));
    pw.close();
}

答案 1 :(得分:4)

170Mb + 14Mb并不是那么庞大的文件。 我的建议是将最小的一个文件加载到java.util.Map,解析最大的一个逐行(逐个记录)文件,并检查此Map中是否存在当前行。

P.S。这个问题在RDBMS方面看起来像是微不足道的 - 也许它值得使用?

答案 2 :(得分:2)

当每次迭代太长时,你不能做O(N ^ 2),这是完全不可接受的。

如果你有足够的内存,你只需解析文件1,创建所有数字的地图,然后解析文件2并检查你的地图。

如果没有足够的RAM,请解析文件1,创建地图并将其存储到文件中,然后解析文件2并读取地图。关键是要使地图尽可能容易解析 - 使其成为二进制格式,可能使用二叉树或您可以快速跳过并搜索的内容。 (编辑:我必须添加Michael Borgwardt的Grace Hash Join链接,它显示了更好的方式:http://en.wikipedia.org/wiki/Hash_join#Grace_hash_join

如果你的文件大小有限,选项1更容易实现 - 除非你正在处理huuuuuuuge文件(我说的是很多GB),你绝对想要这样做。

答案 3 :(得分:1)

通常,内存映射是读取大文件的最有效方法。你需要使用java.nio.MappedByteBuffer和java.io.RandomAccessFile。

但是你的搜索算法才是真正的问题。您需要构建某种索引或哈希表。

相关问题