2017-09-08 71 views
1

随机行我有非常巨大的文本文件,1800万线4Gbyte,我想挑选从它的一些随机的线条,我写了下面的代码要做到这一点,但它是缓慢选择从巨大的文本文件

import java.io.BufferedWriter; 
import java.io.IOException; 
import java.nio.charset.Charset; 
import java.nio.file.Files; 
import java.nio.file.Paths; 
import java.util.Arrays; 
import java.util.Collections; 
import java.util.List; 
import java.util.Random; 
import java.util.stream.Collectors; 
import java.util.stream.Stream; 
public class Main { 

    public static void main(String[] args) throws IOException { 
     int sampleSize =3000; 
     int fileSize = 18000000; 
     int[] linesNumber = new int[sampleSize]; 
     Random r = new Random(); 
     for (int i = 0; i < linesNumber.length; i++) { 
      linesNumber[i] = r.nextInt(fileSize); 

     } 
     List<Integer> list = Arrays.stream(linesNumber).boxed().collect(Collectors.toList()); 
     Collections.sort(list); 

     BufferedWriter outputWriter = Files.newBufferedWriter(Paths.get("output.txt")); 

     for (int i : list) { 

      try (Stream<String> lines = Files.lines(Paths.get("huge_text_file"))) { 
       String en=enlines.skip(i-1).findFirst().get(); 

       outputWriter.write(en+"\n"); 
       lines.close(); 

      } catch (Exception e) { 
       System.err.println(e); 

      } 

     } 
     outputWriter.close(); 


    } 
} 

有没有更优雅更快的方法来做到这一点? 谢谢。

+0

这可能是一个代码审查类型的问题 - 我真的不知道。 –

+1

如果这段代码工作正常,那么这个问题就是堆栈溢出问题,但可能对我们的姊妹站点[代码评论](https://codereview.stackexchange.com/)很有帮助。 –

回答

2

有几件事情,我觉得麻烦你当前的代码。

  1. 您正在载入整个文件到RAM。我对你的示例文件不太了解,但是我使用的文件会导致我的默认JVM崩溃。
  2. 你正在一遍又一遍跳过相同的线条,对于早期的线条更是如此 - 这是非常低效的,就像O(n^n)之类的东西。如果你能用这种方法处理一个500MB的文件,我会感到惊讶。

这就是我想出了:

public static void main(String[] args) throws IOException { 
    int sampleSize = 3000; 
    int fileSize = 50000; 
    int[] linesNumber = new int[sampleSize]; 
    Random r = new Random(); 
    for (int i = 0; i < linesNumber.length; i++) { 
     linesNumber[i] = r.nextInt(fileSize); 

    } 
    List<Integer> list = Arrays.stream(linesNumber).boxed().collect(Collectors.toList()); 
    Collections.sort(list); 

    BufferedWriter outputWriter = Files.newBufferedWriter(Paths.get("localOutput/output.txt")); 
    long t1 = System.currentTimeMillis(); 
    try(BufferedReader reader = new BufferedReader(new FileReader("extremely large file.txt"))) 
    { 
     int index = 0;//keep track of what item we're on in the list 
     int currentIndex = 0;//keep track of what line we're on in the input file 
     while(index < sampleSize)//while we still haven't finished the list 
     { 
      if(currentIndex == list.get(index))//if we reach a line 
      { 
       outputWriter.write(reader.readLine()); 
       outputWriter.write("\n");//readLine doesn't include the newline characters 
       while(index < sampleSize && list.get(index) <= currentIndex)//have to put this here in case of duplicates in the list 
        index++; 
      } 
      else 
       reader.readLine();//readLine is dang fast. There may be faster ways to skip a line, but this is still plenty fast. 
      currentIndex++; 
     } 
    } catch (Exception e) { 
     System.err.println(e); 
    } 
    outputWriter.close(); 
    System.out.println(String.format("Took %d milliseconds", System.currentTimeMillis() - t1)); 
} 

这大约需要87毫秒,我就为30的样本大小和文件大小的50000运行的4.7GB文件,并把〜91毫秒,当我将样本大小更改为3000.当我将文件大小增加到10,000时,花费了122毫秒。 Tl;博士对于这一段=它的尺度非常好,并且在更大的样本尺寸下可以很好地缩放。

直接回答你的问题“是否有更优雅的快速方法来做到这一点?”就在这里。 更快的方法是自己跳过线条,不要将整个文件加载到内存中,并确保继续使用缓冲读写器。此外,我会避免尝试做你自己的原始数组缓冲区或类似的东西 - 只是不要。

如果您想了解更多关于它的工作原理,请随意浏览我已包含的方法。

0

我首先想到的方法是查看Java cf中的RandomAccess文件。 https://docs.oracle.com/javase/tutorial/essential/io/rafs.html。通常,随机查找比读取整个文件要快很多,但是您需要逐字节地读取以读取下一行的开始(例如),然后逐字节地读取该行到下一个换行,然后寻找另一个随机位置。

我不确定这种方法会更优雅(部分取决于你如何编码我猜),但我希望它会更快。

0

有没有有效的方法来寻求线路。唯一我能想到的就是使用RandomAccessFile,寻找随机位置,然后将下一个200(?)字符读入数组。然后进行换行查找并形成一个字符串。

doc