Java - 根据偏移量从随机访问文件中获取行

Java - get line from Random access file based on offsets

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

我对 Java 文件 IO 不是很了解,但我当然发现了这个 post: How to get a random line of a text file in Java?

我放弃了接受的答案,因为它显然 way 太慢了,无法将 11GB 文件的每一行读取到 select 一行(或者更确切地说 k) 在大约 100k 行中。

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

Use RandomAccessFile to seek to a random byte position in the file.

Seek left and right to the next line terminator. Let L the line between them.

With probability (MIN_LINE_LENGTH / L.length) return L. Otherwise, start over at step 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()进行操作,这样就不用明确地寻找行的右端了。

那么为什么要“向左和向右寻找下一行终止符”呢? 有没有更方便的方法从这两个偏移量得到直线?

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


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

Each byte is converted into a character by taking the byte's value for the lower eight bits of the character and setting the high eight bits of the character to zero. This method does not, therefore, support the full Unicode character set.

错误阅读演示:

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);