2012-12-27 5 views
8

Przetwarzam strumień binarny i muszę przejść skutecznie poza zakres danych, które nie są mi potrzebne, do niektórych danych, które będą przetwarzane.Trwałe pomijanie danych w java.io.InputStream i jego podtypach

InputStream.skip(long) nie robi dużo w drodze gwarancji:

pomija i odrzutów n bajtów danych z tego strumienia wejściowego. Metoda przeskakiwania może z wielu powodów przeskoczyć o mniejszą liczbę bajtów, być może 0. Może to wynikać z dowolnego z szeregu warunków; docieranie do końca pliku przed pomijaniem n bajtów jest tylko jedną z możliwości. Zwracana jest rzeczywista liczba pominiętych bajtów.

muszę wiedzieć, że jedna z dwóch rzeczy się wydarzyło:

  1. Strumień zakończony
  2. Bajty zostały pominięte

dość proste. Jednakże, złagodzenie przyznane w tym opisie oznacza, że ​​na przykład BufferedInputStream może po prostu pominąć kilka bajtów i powrócić. Jasne, mówi mi, że pomijano tylko tych kilka, ale nie jest jasne, dlaczego.

Moje pytanie brzmi: czy można użyć InputStream.skip(long) w taki sposób, aby wiedzieć, kiedy kończy się strumień lub czy pomijanie kończy się pomyślnie?

Odpowiedz

8

Nie sądzę, możemy uzyskać naprawdę solidną implementację, ponieważ umowa na metodę skip() jest dość dziwaczna. Po pierwsze, zachowanie w EOF nie jest dobrze zdefiniowane. Jeśli chcę pominąć 8 bajtów i is.skip(8) zwraca 0, nie jest trywialne, aby zdecydować, czy powinienem spróbować ponownie, istnieje niebezpieczeństwo nieskończonej pętli, jeśli jakaś implementacja zdecyduje się zwrócić 0, gdy na EOF. available() też nie można ufać.

Stąd proponuję następujące:

/** 
* 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"); 
    } 
} 

nie powinniśmy zwracać wartość poinformować liczbę bajtów „naprawdę pomijane”? Lub boolean, aby poinformować, że EOF został osiągnięty? Nie możemy tego zrobić w solidny sposób. Na przykład, jeśli wywołujemy skip(8) dla obiektu FileInputStream, it will return 8, nawet jeśli jesteśmy na EOF lub jeśli plik ma tylko 2 bajty. Ale metoda jest solidna w tym sensie, że robi to, co chcemy: pomiń n bajtów (jeśli to możliwe) i pozwól mi kontynuować przetwarzanie (jeśli mój następny odczyt zwróci -1 będę wiedział, że EOF został osiągnięty).

+0

Twoja odpowiedź konkretnie wyjaśnia, co mnie martwi. Kod, który opublikowałem _seems_ do pracy w praktyce, ale nie mam pewności, że zadziałałby dla wszystkich implementacji 'InputStream'. Twoje rozszerzenie wygląda interesująco, a wypróbuję je wkrótce w [klasie, w której jest to potrzebne] (https://code.google.com/p/metadata-extractor/source/browse/Source/com/drew/lang/ StreamReader.java). Obecnie mój interfejs API próbuje zgłosić, czy pominięcie powiodło się, więc może być konieczna modyfikacja kodu klienta, jeśli żadna gwarancja nie jest możliwa. Dziękuję bardzo. –

+0

Możesz naprawić problem 'FileInputStream.skip()': używaj pętli 'while' dla' n-1' bajtów; następnie, po pętli, wywołaj 'in.read()' raz. Jeśli zwróci "-1", Twój skok będzie trafiony EOF, w przeciwnym razie pomyłka zakończyła się powodzeniem. Nie zapomnij również o sprawdzeniu "n == 0" u góry. –

+0

@KannanGoundan Interesująca sugestia. Wadą jest oczywiście to, że wymagałoby co najmniej dwóch odczytów ze strumienia (jeden "przeskok" i jeden "odczyt"), co w niektórych sytuacjach może wpłynąć na wydajność. – leonbloy

2

To wydaje się działać na pomijanie n bajtów:

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; 

Jednak nie jest jasne, że to będzie działać dla wszystkich implementacjach InputStream, które mogą być przekazywane do mojej biblioteki. Zastanawiam się, czy wdrożenie mojej własnej buforowanej metody pomijania jest drogą do zrobienia.

+0

Nie widzę jak jakakolwiek implementacja 'InputStream' może odejść od umowy, która mówi, że zwracają liczbę bajtów, które zostały pominięte. – EJP

+0

@ EJP, zgadzam się. Obawiam się wiedzieć, czy mniej bajtów zostało pominiętych z powodu jakiegoś artefaktu IO (buforowanie lub tak) lub ponieważ strumień się zakończył. Jeśli strumień się nie zakończył, 'skip' może nadal zwracać zero.W którym momencie wiesz, że pomijanie nie działa, ponieważ nie ma więcej bajtów, a może oczekuje na bajty w sieci? –

+2

Problemem, który widzę z tym, jest to, że nie możemy być pewni, że nie powinniśmy próbować ponownie, gdy 'pomijamy == 0'. Ponadto nie można zaufać wartości boolean 'skippedEnough'. Zobacz moją odpowiedź. – leonbloy