Frage

Ich habe noch nie enge Erfahrungen mit Java IO API hat, und ich bin jetzt wirklich frustriert. Ich finde es schwer zu glauben, wie seltsam und komplex es ist, und wie schwer es könnte sein, eine einfache Aufgabe zu tun.

Meine Aufgabe: Ich habe 2 Positionen (ab Byte, endend Byte), pos1 und pos2. Ich brauche Linien zwischen diesen beiden Bytes (einschließlich dem Start ein, ohne die Endung eins) zu lesen und sie als UTF8 String-Objekte.

Zum Beispiel, in den meisten Skriptsprachen wäre es eine sehr einfache 1-2-3-Liner so sein (in Ruby, aber es wird im Wesentlichen das gleiche für Python, Perl, etc.):

f = File.open("file.txt").seek(pos1)
while f.pos < pos2 {
  s = f.readline
  # do something with "s" here
}

Es kommt schnell Hölle mit Java IO APIs;) In der Tat, ich sehe zwei Möglichkeiten, Linien (endend mit \n) von regelmäßigen lokalen Dateien zu lesen:

  • Random hat getFilePointer() und seek(long pos), aber es ist readline () liest nicht-UTF8-Strings (und auch nicht Byte-Arrays), aber sehr seltsam Saiten mit gebrochener Codierung, und es hat keine Pufferung (was wahrscheinlich bedeutet, dass jeder read*() Anruf in einzelne undelying OS read() => ziemlich langsam übersetzt werden würde).
  • BufferedReader hat große readLine() Methode, und es kann sogar etwas suchend mit skip(long n) zu tun, aber es hat keine Möglichkeit, gerade Anzahl von Bytes zu bestimmen, die bereits gelesen wurden, nicht die aktuelle Position in einer Datei zu erwähnen.

Ich habe zu verwenden versucht, so etwas wie:

    FileInputStream fis = new FileInputStream(fileName);
    FileChannel fc = fis.getChannel();
    BufferedReader br = new BufferedReader(
            new InputStreamReader(
                    fis,
                    CHARSET_UTF8
            )
    );

... und dann fc.position() mit aktueller Datei erhalten Leseposition und fc.position(newPosition) zu Satz, aber es scheint nicht zu Arbeit in meinem Fall: Sieht aus wie er die Position eines Puffers zurückgibt Vorbefüllung von BufferedReader getan, oder so etwas wie die. - diese Zähler scheinen in 16K-Schritten aufgerundet wird

Muss ich wirklich alles von mir selbst zu implementieren, das heißt eine Datei readering Schnittstelle, die würde:

  • Lassen Sie mich bekommen / Soll-Position in einer Datei
  • Puffer-Datei Leseoperationen
  • erlaubt das Lesen UTF8-Strings (oder zumindest erlauben Operationen wie „Lese alles bis zum nächsten \n“)

Gibt es einen schnelleren Weg, als alles selbst zu implementieren? Bin ich die Aufsicht über etwas?

War es hilfreich?

Lösung

import org.apache.commons.io.input.BoundedInputStream

FileInputStream file = new FileInputStream(filename);
file.skip(pos1);
BufferedReader br = new BufferedReader(
   new InputStreamReader(new BoundedInputStream(file,pos2-pos1))
);

Wenn Sie nicht über pos2 scherte, dann WOUNDN'T Sie Apache Commons IO benötigen.

Andere Tipps

Ich schrieb diesen Code utf-8 mit randomaccessfiles

lesen
//File: CyclicBuffer.java
public class CyclicBuffer {
private static final int size = 3;
private FileChannel channel;
private ByteBuffer buffer = ByteBuffer.allocate(size);

public CyclicBuffer(FileChannel channel) {
    this.channel = channel;
}

private int read() throws IOException {
    return channel.read(buffer);
}

/**
 * Returns the byte read
 *
 * @return byte read -1 - end of file reached
 * @throws IOException
 */
public byte get() throws IOException {
    if (buffer.hasRemaining()) {
        return buffer.get();
    } else {
        buffer.clear();
        int eof = read();
        if (eof == -1) {
            return (byte) eof;
        }
        buffer.flip();
        return buffer.get();
    }
}
}
//File: UTFRandomFileLineReader.java


public class UTFRandomFileLineReader {
private final Charset charset = Charset.forName("utf-8");
private CyclicBuffer buffer;
private ByteBuffer temp = ByteBuffer.allocate(4096);
private boolean eof = false;

public UTFRandomFileLineReader(FileChannel channel) {
    this.buffer = new CyclicBuffer(channel);
}

public String readLine() throws IOException {
    if (eof) {
        return null;
    }
    byte x = 0;
    temp.clear();

    while ((byte) -1 != (x = (buffer.get())) &amp;&amp; x != '\n') {
        if (temp.position() == temp.capacity()) {
            temp = addCapacity(temp);
        }
        temp.put(x);
    }
    if (x == -1) {
        eof = true;
    }
    temp.flip();
    if (temp.hasRemaining()) {
        return charset.decode(temp).toString();
    } else {
        return null;
    }
}

private ByteBuffer addCapacity(ByteBuffer temp) {
    ByteBuffer t = ByteBuffer.allocate(temp.capacity() + 1024);
    temp.flip();
    t.put(temp);
    return t;
}

public static void main(String[] args) throws IOException {
    RandomAccessFile file = new RandomAccessFile("/Users/sachins/utf8.txt",
            "r");
    UTFRandomFileLineReader reader = new UTFRandomFileLineReader(file
            .getChannel());
    int i = 1;
    while (true) {
        String s = reader.readLine();
        if (s == null)
            break;
        System.out.println("\n line  " + i++);
        s = s + "\n";
        for (byte b : s.getBytes(Charset.forName("utf-8"))) {
            System.out.printf("%x", b);
        }
        System.out.printf("\n");

    }
}
}

Für @Ken Bloom Ein sehr schnell bei einer Version Java 7 gehen. Anmerkung: Ich glaube nicht, das der effizienteste Weg ist, bin ich noch immer meinen Kopf um NIO.2, Oracle hat damit begonnen, ihre Tutorial hier

Beachten Sie, dass dies nicht mit Java 7 der neuen ARM-Syntax (die für dateibasierte Ressourcen Pflege der Ausnahmebehandlung dauert), war es nicht in der neuesten openJDK Build arbeiten, dass ich habe. Aber wenn die Menschen die Syntax sehen wollen, lassen Sie es mich wissen.

/* 
 * Paths uses the default file system, note no exception thrown at this stage if 
 * file is missing
 */
Path file = Paths.get("C:/Projects/timesheet.txt");
ByteBuffer readBuffer = ByteBuffer.allocate(readBufferSize);
FileChannel fc = null;
try
{
    /*
     * newByteChannel is a SeekableByteChannel - this is the fun new construct that 
     * supports asynch file based I/O, e.g. If you declared an AsynchronousFileChannel 
     * you could read and write to that channel simultaneously with multiple threads.
     */
    fc = (FileChannel)file.newByteChannel(StandardOpenOption.READ);
    fc.position(startPosition);
    while (fc.read(readBuffer) != -1)
    {
        readBuffer.rewind();
        System.out.println(Charset.forName(encoding).decode(readBuffer));
        readBuffer.flip();
    }
}

Starten Sie mit einem RandomAccessFile und Verwendung read oder readFully einen Byte-Array zwischen pos1 und pos2 zu bekommen. Lassen Sie uns sagen, dass wir die Daten lesen in einer Variablen namens rawBytes gespeichert haben.

Dann erstellen Sie Ihre BufferedReader mit

new BufferedReader(new InputStreamReader(new ByteArrayInputStream(rawBytes)))

Dann können Sie readLine auf dem BufferedReader nennen.

Caveat. Dies wahrscheinlich mehr Speicher, als wenn Sie die BufferedReader an die richtige Stelle suchen machen könnte selbst, weil sie alles in den Speicher preloads

Ich denke, die Verwirrung, die durch die UTF-8-Codierung und die Möglichkeit von Double-Byte-Zeichen verursacht wird.

UTF8 ist nicht festgelegt, wie viele Bytes in einem einzelnen Zeichen ist. Ich gehe davon aus aus Ihrem Post, dass Sie einzelne Byte-Zeichen verwenden. Zum Beispiel würde bedeuten 412 Bytes 411 Zeichen. Aber wenn die Zeichenfolge Double-Byte-Zeichen verwendet wurden, würden Sie die 206 Zeichen bekommen.

Das ursprüngliche java.io Paket befaßt sich nicht gut mit dieser Multi-Byte-Verwirrung. So fügten sie mehr Klassen speziell mit Streichern zu beschäftigen. Das Paket mischt zwei verschiedene Arten von Datei-Handler (und sie kann verwirrend sein, bis die Nomenklatur aussortiert wird). Der Strom Klassen bieten für die direkten Daten-I / O ohne Konvertierung. Die Leser Klassen Konvertieren von Dateien in Strings mit voller Unterstützung für Multi-Byte-Zeichen. Das könnte helfen ein Teil des Problems klären.

Da Sie Sie angeben, werden mit UTF-8-Zeichen, sollten Sie die Leser-Klassen. In diesem Fall schlage ich Filereader. Der Sprung () -Methode in Filereader können Sie von X Zeichen passieren und dann den Text zu lesen beginnen. Alternativ ziehe ich die überladene read () Methode, da sie Ihnen die gesamten Text auf einmal packen können.

Wenn Sie Ihre „Bytes“ sind einzelne Zeichen, versuchen Sie so etwas wie dieses annehmen:

FileReader fr = new FileReader( new File("x.txt") );
char[] buffer = new char[ pos2 - pos ];
fr.read( buffer, pos, buffer.length );
...

Ich bin spät zur Party hier, aber ich lief über dieses Problem in meinem eigenen Projekt.

Nach viel Traversal von Javadocs und Stack-Überlauf, ich glaube, ich eine einfache Lösung gefunden.

Nach Ihrer Random an der entsprechenden Stelle zu suchen, die ich hier raFile nenne, gehen Sie wie folgt:

FileDescriptor fd = raFile.getFD();
FileReader     fr = new FileReader(fd);
BufferedReader br = new BufferedReader(fr);

Dann sollten Sie in der Lage sein zu nennen br.readLine() zum Inhalt Ihres Herzens, die schneller sein wird als viel raFile.readLine() aufrufen.

Das einzige, was ich bin nicht sicher, ob UTF8 Strings korrekt behandelt werden.

Der Java-IO-API ist sehr flexibel. Leider manchmal macht die Flexibilität, die sie ausführlich. Der Grundgedanke dabei ist, dass es viele Bäche, Autoren und Leser, die Wrapper-Rüttler implementieren. Zum Beispiel wickelt BufferedInputStream andere Input. Das gleiche ist zu den Ausgabeströmen.

Der Unterschied zwischen den Strömen und Leser / Schreiber ist, dass Ströme mit Bytes arbeiten, während Leser / Schreiber mit Zeichen arbeiten.

Zum Glück einige Bäche, Schriftsteller und Leser bequem Konstrukteure, die Codierung zu vereinfachen. Wenn Sie Datei lesen möchten, müssen Sie nur sagen,

    InputStream in = new FileInputStream("/usr/home/me/myfile.txt");
    if (in.markSupported()) {
        in.skip(1024);
        in.read();
    }

Es ist nicht so, wie Sie Angst kompliziert.

Kanäle ist etwas anderes. Es ist ein Teil von so „neuer IO“ oder nio genannt. New IO nicht blockiert wird - es sein Hauptvorteil ist. Sie können für jede „nio Java-Tutorial“ in Internet suchen und darüber lesen. Aber es ist komplizierter als normaler IO und ist für die meisten Anwendungen benötigt.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top