2016-05-27 18 views
3

Ich habe eine sehr große (11GB) .json Datei (ja, wer dachte , dass eine gute Idee?), Die ich probieren muss (lesen Sie k zufällige Zeilen).Java - Zeile von Random Access-Datei basierend auf Offsets erhalten

Ich bin nicht sehr versiert in Java-Datei IO, aber ich habe, natürlich, fand diesen Beitrag: How to get a random line of a text file in Java?

ich die akzeptierte Antwort bin Fallenlassen, weil es klar Weg ist zu langsam jeden einzelnes lesen Zeile einer 11GB-Datei, nur um eine (oder eher k) aus den etwa 100k Zeilen auszuwählen.

Glücklicherweise gibt es einen zweiten Vorschlag dort gepostet, dass ich denke, könnte mir besser zu nutzen sein:

Verwenden Random zu einer zufälligen Byte-Position in der Datei zu suchen.

Suchen Sie nach links und rechts zum nächsten Leitungsabschluss. Lass L die Linie zwischen ihnen.

Mit Wahrscheinlichkeit (MIN_LINE_LENGTH/L.length) zurückzukehren L. Ansonsten vorn beginnen bei Schritt 1.

So weit so gut, aber ich habe mich gefragt, über dieses „sei L sein die Linie zwischen ihnen“ .

Ich habe so etwas wie diese (nicht getestet) getan hätte:

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

wo

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

und dann betrieben mit line.length(), die die Notwendigkeit verzichtet ausdrücklich vor Ende der Leitung zu suchen.

Warum also "links und rechts zum nächsten Leitungsabschluss suchen"? Gibt es einen bequemeren Weg, die Linie von diesen beiden Offsets zu bekommen?

Antwort

2

Es sieht so aus, als würde das ungefähr das gleiche tun - raf.readLine()ist Suche nach dem nächsten Leitungsabschluss; es macht es nur für dich.


Eine Sache zu beachten ist, dass RandomAccessFile.readLine() nicht Unicode-Strings aus der Datei zu lesen unterstützt:

Jedes Byte in ein Zeichen umgewandelt wird durch den Wert des Bytes für die unteren acht Bits der Einnahme Zeichen und setzt die hohen acht Bits des Zeichens auf Null. Diese Methode unterstützt daher nicht den vollständigen Unicode-Zeichensatz.

Demo von der falschen Lesung:

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

Ausgang:

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

Aber output.txt enthält die richtigen Daten:

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

Als solche möchten Sie vielleicht Suche selbst oder explizit c onvert das Ergebnis raf.readLine() auf die richtige charset:

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

Vielen Dank. Aber wie unterscheidet sich "das Suchen selbst" von "raf.readLine()" und dann konvertieren? Kann ich irgendwie einen InputStreamReader definieren, der am Anfang der Zeile beginnt? – User1291

+1

Logischerweise würde es keinen Unterschied geben; Es könnte nur bedeuten, weniger Objekte zuzuweisen, wenn Sie es selbst tun. Ich würde mit dem Readline/Convert-Ansatz beginnen und ihn später erneut aufsuchen, wenn sich dies als ein Leistungsengpass erweist. –