2012-12-27 4 views
8

Ich verarbeite einen binären Datenstrom und muss effizient über eine Reihe von Daten, die mich nicht interessieren, auf einige Daten überspringen, die verarbeitet werden.Robustes Überspringen von Daten in einem java.io.InputStream und seinen Subtypen

InputStream.skip(long) nicht viel machen in der Art von Garantien:

Skips über und verwirft n Bytes von Daten aus dem Stream. Das Auslassen-Verfahren kann aus einer Vielzahl von Gründen dazu führen, dass eine kleinere Anzahl von Bytes, möglicherweise 0, übersprungen wird. Dies kann aus einer beliebigen Anzahl von Bedingungen resultieren; Das Erreichen des Dateiendes, bevor n Bytes übersprungen wurden, ist nur eine Möglichkeit. Die tatsächliche Anzahl der übersprungenen Bytes wird zurückgegeben.

Ich muss wissen, dass eines von zwei Dingen passiert:

  1. Der Strom beendet
  2. Die Bytes
  3. übersprungen wurden

Einfach genug. Die in dieser Beschreibung gewährte Nachsicht bedeutet jedoch, dass beispielsweise BufferedInputStream einige Bytes überspringen und zurückgeben kann. Sicher, es sagt mir, dass es nur diese wenigen ausgelassen hat, aber es ist nicht klar, warum.

Also meine Frage ist: können Sie InputStream.skip(long) so verwenden, dass Sie wissen, wenn entweder der Strom endet oder der Übersprung erfolgreich abgeschlossen?

Antwort

8

Ich glaube nicht, dass wir eine wirklich robuste Implementierung bekommen können, weil der skip() Methodenvertrag ziemlich bizarr ist. Zum einen ist das Verhalten bei EOF nicht gut definiert. Wenn ich 8 Bytes überspringen möchte und is.skip(8) 0 zurückgibt, ist es nicht trivial zu entscheiden, ob ich es noch einmal versuchen sollte. Es besteht die Gefahr einer Endlosschleife, wenn eine Implementierung 0 bei EOF zurückgibt. available() ist auch nicht vertrauenswürdig.

Daher schlage ich folgendes:

/** 
* Skips n bytes. 
*/ 
public static void myskip(InputStream is, long n) throws IOException { 
    while(n > 0) { 
     long n1 = is.skip(n); 
     if(n1 > 0) { 
      n -= n1; 
     } else if(n1 == 0) { // should we retry? lets read one byte 
      if(is.read() == -1) // EOF 
       break; 
      else 
       n--; 
     } else // negative? this should never happen but... 
     throw new IOException("skip() returned a negative value - this should never happen"); 
    } 
} 

Sollten wir keinen Wert zurück, die Anzahl der Bytes „wirklich übersprungen“ zu informieren? Oder ein Boolescher Wert, um zu informieren, dass EOF erreicht wurde? Das können wir nicht auf eine robuste Art und Weise tun. Zum Beispiel, wenn wir skip(8) für ein Objekt, it will return 8 aufrufen, selbst wenn wir bei EOF sind, oder wenn die Datei nur 2 Bytes hat. Aber die Methode ist robust in dem Sinne, dass sie das tut, was wir wollen: Überspringe n Bytes (wenn möglich) und lasse mich weiter verarbeiten (wenn mein nächster Lesewert -1 zurückkommt, weiß ich, dass EOF erreicht wurde).

+0

Ihre Antwort konkretisiert, worum es mir ging. Der Code, den ich gepostet habe _seems_, um in der Praxis zu funktionieren, aber ich bin nicht zuversichtlich, dass es für alle Implementierungen von 'InputStream' funktionieren würde. Ihre Erweiterung sieht interessant aus und ich werde sie in Kürze in der Klasse, in der ich sie brauche, ausprobieren. (Https://code.google.com/p/metadata-extractor/source/browse/Source/com/drew/lang/ StreamReader.java). Momentan versucht meine API zu melden, ob der Übersprung erfolgreich war, daher muss ich möglicherweise den Clientcode ändern, wenn keine Garantie möglich ist. Vielen Dank. –

+0

Sie können das 'FileInputStream.skip()' Problem beheben: Verwenden Sie Ihre 'while' Schleife für' n-1' Bytes; Rufen Sie nach der Schleife einmal 'in.read()' auf. Wenn Sie "-1" zurückgeben, wird mit dem Überspringen EOF ausgelöst, andernfalls ist Ihr Überspringen erfolgreich. Vergessen Sie auch nicht, eine 'n == 0 Prüfung oben zu machen. –

+0

@KannanGoundan Interessanter Vorschlag. Ein Nachteil ist natürlich, dass es mindestens zwei Lesungen von dem Strom erfordern würde (ein "Überspringen" plus ein "Lesen"), was in einigen Szenarien die Leistung beeinträchtigen könnte. – leonbloy

2

Dies scheint für das Überspringen n Bytes zu funktionieren:

long skippedTotal = 0; 
while (skippedTotal != n) { 
    long skipped = _stream.skip(n - skippedTotal); 
    assert(skipped >= 0); 
    skippedTotal += skipped; 
    if (skipped == 0) 
     break; 
} 
boolean skippedEnough = skippedTotal == n; 

aber es ist nicht klar, dass es für alle Implementierungen von InputStream zu arbeiten, die meine Bibliothek übergeben werden kann. Ich frage mich, ob die Implementierung meiner eigenen gepufferten Sprungmethode der richtige Weg ist.

+0

Ich sehe nicht, wie eine 'InputStream'-Implementierung vom Vertrag abweichen kann, der besagt, dass sie zurückgeben, wie viele Bytes wirklich übersprungen wurden. – EJP

+0

@EJP, stimme ich zu. Es geht mir darum zu wissen, ob aufgrund einer Art von IO-Artefakt (Pufferung oder so) oder weil der Stream beendet wurde, weniger Bytes übersprungen wurden. Wenn der Stream nicht beendet wurde, kann "Überspringen" immer noch Null zurückgeben.An welchem ​​Punkt weißt du, dass das Überspringen nicht funktioniert, weil es keine Bytes mehr gibt, vielleicht weil es auf Bytes über ein Netzwerk wartet? –

+2

Das Problem, das ich damit sehe, ist, dass wir nicht sicher sein können, dass wir nicht erneut versuchen sollten, wenn 'skipped == 0'. Außerdem ist das Boolean 'skippedEnough' nicht vertrauenswürdig. Siehe meine Antwort. – leonbloy

Verwandte Themen