Надежное пропущение данных в java.io.InputStream и его подтипах

Я обрабатываю двоичный поток и должен пропускать эффективно за пределы диапазона данных, которые мне не интересны, к некоторым данным, которые будут обработаны.

InputStream.skip(long) не мешает гарантировать:

Пропускает и удаляет n байтов данных из этого входного потока. Метод пропуска может по разным причинам заканчиваться пропусканием некоторого меньшего количества байтов, возможно, 0. Это может быть результатом любого из нескольких условий; конечный конец файла перед пропуском n байтов - это только одна возможность. Возвращается фактическое количество пропущенных байтов.

Мне нужно знать, что произошло одно из двух:

  • Поток закончился
  • Пропущенные байты

Прост достаточно. Однако снисходительность, предоставляемая в этом описании, означает, что, например, BufferedInputStream может просто пропустить несколько байтов и вернуться. Конечно, он говорит мне, что он пропустил только те немногие, но неясно, почему.

Итак, мой вопрос: можете ли вы использовать InputStream.skip(long) так, как вы знаете, когда заканчивается поток или успешно завершается прогон?

Ответ 1

Я не думаю, что мы можем получить действительно надежную реализацию, потому что контракт метода skip() довольно причудливый. С одной стороны, поведение в EOF не очень хорошо определено. Если я хочу пропустить 8 байтов и is.skip(8) возвращает 0, то is.skip(8) решить, следует ли мне повторить попытку, существует опасность бесконечного цикла, если какая-то реализация решит вернуть 0 в EOF. И available() тоже нельзя доверять.

Следовательно, я предлагаю следующее:

/**
 * Skips n bytes. Best effort.
 */
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");
    }
}

Разве мы не должны возвращать значение, чтобы сообщить количество байтов, "действительно пропущенных"? Или логическое значение, чтобы сообщить, что EOF был достигнут? Мы не можем сделать это надежным способом. Например, если мы вызовем skip(8) для объекта FileInputStream, он вернет 8, даже если мы находимся в EOF или если файл имеет только 2 байта. Но метод является надежным в том смысле, что он делает то, что мы хотим: пропустить n байтов (если это возможно) и позволить мне продолжить его обработку (если мое следующее чтение вернет -1 я буду знать, что EOF достигнут).

Ответ 2

Кажется, что это работает для пропуска n bytes:

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;

Однако неясно, что он будет работать для всех реализаций InputStream, которые могут быть переданы в мою библиотеку. Мне интересно, как реализовать мой собственный метод буферизованного пропуска.

Ответ 3

Я на 6 лет опоздал на этот вопрос.

В принципе, нет разницы между skip (int n) и readFully (int n). В случае пропуска вы не интересуетесь байтами.

Для прямой трансляции, т.е. tcp-сокет или файл, к которому добавляется, skip (n) может заблокировать (подождать), как только он "пропустит" 0 байтов, в зависимости от предпочтения пользователя ждать.

Возвращение EOF или -1 указывает на конец потока, и это должно быть возвращено конечному пользователю, так как после этой точки больше ничего не произойдет.

Чтобы эффективно пропустить байты в файле, id исследовать случайные каналы. но эту оптимизацию нельзя сделать общей для любого входного потока.