Java - 基于偏移

时间:2016-05-27 10:40:45

标签: java file io readline randomaccessfile

我有一个非常大的(11GB).json文件(是的,谁认为 一个好主意?)我需要采样(阅读k随机行)。

我在Java文件IO中不是很精明,但我当然发现了这篇文章: How to get a random line of a text file in Java?

我正在删除已接受的答案,因为显然方式太慢而无法读取11GB文件的每一行只是为了从大约100k中选择一个(或更确切地说是k)线。

幸运的是,我认为可能对我有更好用的第二个建议:

  

使用RandomAccessFile寻找文件中的随机字节位置。

     

向左和向右寻找下一行终止符。让他们之间的界线。

     

以概率(MIN_LINE_LENGTH / L.length)返回L.否则,从步骤1开始。

到目前为止一直很好,但我想知道“让L成为他们之间的界限”。

我会做这样的事情(未经测试):

RandomAccessFile raf = ...
long pos = ...
String line = getLine(raf,pos);
...

,其中

private String getLine(RandomAccessFile raf, long start) throws IOException{
    long pos = (start % 2 == 0) ? start : start -1;

    if(pos == 0) return raf.readLine();

    do{
        pos -= 2;
        raf.seek(pos);
    }while(pos > 0 && raf.readChar() != '\n');

    pos = (pos <= 0) ? 0 : pos + 2;
    raf.seek(pos);
    return raf.readLine();
}

然后使用line.length()进行操作,这放弃了明确寻找线路右端的需要。

那么为什么“向下一行终结者寻找左”? 有没有更方便的方法从这两个偏移中获取线路?

1 个答案:

答案 0 :(得分:2)

看起来这会大致相同 - raf.readLine() 寻求下一行终结符的权利;它只是为你做的。

需要注意的一点是,RandomAccessFile.readLine()不支持从文件中读取unicode字符串:

  

通过取字符的低八位的字节值并将字符的高八位设置为零,将每个字节转换为字符。因此,此方法不支持完整的Unicode字符集。

演示不正确的阅读:

import java.io.*;
import java.nio.charset.StandardCharsets;

class Demo {
  public static void main(String[] args) throws IOException {
    try (FileOutputStream fos = new FileOutputStream("output.txt");
         OutputStreamWriter osw = new OutputStreamWriter(fos, StandardCharsets.UTF_8);
         BufferedWriter writer = new BufferedWriter(osw)) {
      writer.write("ⵉⵎⴰⵣⵉⵖⵏ");
    }

    try (RandomAccessFile raf = new RandomAccessFile("output.txt", "r")) {
      System.out.println(raf.readLine());
    }
  }
}

输出:

âµâµâ´°âµ£âµâµâµ

output.txt确实包含正确的数据:

$ cat output.txt
ⵉⵎⴰⵣⵉⵖⵏ

因此,您可能希望自己进行搜索,或者明确地将raf.readLine()的结果转换为正确的字符集:

String line = new String(
    raf.readLine().getBytes(StandardCharsets.ISO_8859_1),      
    StandardCharsets.UTF_8);