Второй экземпляр java.util.Scanner выдает исключение NoSuchElementException

Почему я не могу прочитать вторую строку при использовании второго экземпляра сканера? Я получаю «java.util.NoSuchElementException: строка не найдена».

Я понимаю, что должен использовать hasNextLine() и избежать исключения, но мой вопрос: почему вторая строка вообще недоступна? Кроме того, причина наличия нескольких экземпляров сканера заключается в том, что на самом деле сканер создается в методе, который вызывается несколько раз, и я не ищу там решения или исправления.

Обратите внимание, что я не закрываю ни сканеры, ни потоки. С помощью сканеров я читаю только по 1 строке, а в потоке у меня 3 строки.

Вот моя упрощенная программа:

private void scanLines() {
    String input = "Line 1." + System.lineSeparator() 
                 + "Line 2." + System.lineSeparator() 
                 + "Line 3." + System.lineSeparator();

    ByteArrayInputStream bais = new ByteArrayInputStream(input.getBytes());

    Scanner scanner1 = new Scanner(bais);
    System.out.println(scanner1.nextLine());

    Scanner scanner2 = new Scanner(bais);
    System.out.println(scanner2.nextLine());
}

Выход:

Line 1.
Exception in thread "main" java.util.NoSuchElementException: No line found
        at java.util.Scanner.nextLine(Unknown Source)
        at ScannerTest.scanLines(ScannerTest.java:23)
        at ScannerTest.main(ScannerTest.java:6)

person Jay    schedule 07.04.2017    source источник
comment
Никогда не используйте два сканера в одном потоке   -  person freedev    schedule 07.04.2017
comment
И почему вы используете ByteArrayInputStream, когда хотите читать строковые строки?!   -  person GhostCat    schedule 07.04.2017
comment
Просто используйте тот же сканер   -  person Hackerman    schedule 07.04.2017
comment
@GhostCat Я хотел, чтобы входной поток передавался конструктору сканера, и простой способ сделать это — создать строку и преобразовать ее в ByteArrayInputStream.   -  person Jay    schedule 07.04.2017
comment
Дубликат дает рекомендацию, а не объяснение происходящего.   -  person Jay    schedule 07.04.2017
comment
@Berger Дубликат дает рекомендацию, а не объяснение происходящего.   -  person Jay    schedule 07.04.2017
comment
Моя теория: Сканер часто считывает данные из файлов или других ресурсов, для которых требуется разрешение на чтение из ОС. Получение такого разрешения обычно занимает некоторое время и может быть очень дорогим. Поэтому мы не хотим запрашивать это разрешение для каждого конкретного персонажа. Вместо этого мы хотим получить его один раз, и пока он у нас есть, читаем их больше, не мало, но и не слишком много, так как чтение также занимает некоторое время, которое может понадобиться в других местах. В этом случае похоже, что размер буфера/кэша превышает размер байтов из строки, поэтому все они считываются, не оставляя ничего следующему сканеру.   -  person Pshemo    schedule 07.04.2017
comment
Попробуйте прочитать ByteArrayInputStream, содержащий больше байтов, может быть, 10_000, и посмотрите, сколько из них останется available() после чтения с первого сканера.   -  person Pshemo    schedule 07.04.2017
comment
@Pshemo Это то, что я проверил при отладке исходного кода. Каждый вызов метода nextLine() продвигает позицию потока на 1024 байта. Вот почему второй сканер вызывает исключение. Как вы думаете, стоит ли повторно открыть этот вопрос после моего ответа или мне следует переместить его в множественные сканеры Java?   -  person freedev    schedule 07.04.2017
comment
@Pshemo Правда, сканер будет использовать внутреннюю буферизацию. Но если использование одного экземпляра Scanner для чтения одного ввода снова делает поток нечитаемым, то разве это не проблема?   -  person Jay    schedule 07.04.2017
comment
@Джей, я не вижу здесь никаких проблем. Данные перемещаются из потока в сканер, поэтому они по-прежнему доступны для нас через сканер, что является нашей первоначальной целью. Мы просто должны использовать только один сканер на ресурс, и проблем не будет. То, что вы пытаетесь сделать здесь, противоречит оригинальной идее Сканера (или любого Читателя), поэтому вы боретесь с проблемами, которые обычно не должны существовать (не говоря уже о том, что если вы закроете первый Сканер, он также закроет ресурс, который он читает, что означает, что мы не сможем прочитать его с помощью другого сканера).   -  person Pshemo    schedule 07.04.2017


Ответы (1)


Использование нескольких сканеров (в одном потоке) — очень плохая практика, потому что сканеры потребляют общий поток.

Это причина исключения java.util.NoSuchElementException: No line found, которое у вас было.

Я проверил ваш код, и исключение возникает при втором вызове nextLine().

В каждом классе Scanner сохраняется ссылка на один и тот же входной поток.

Когда вызывается метод scanner1.nextLine(), считывается группа байтов в потоке, и позиция перемещается вперед.

Просто чтобы внести ясность, я дважды проверил отладку исходного кода класса Scanner.

При вызове метода nextLine() за кулисами поток перемещается вперед на 1024 позицию, копируя результат в буфер

// Internal buffer used to hold input
private CharBuffer buf;

Попробуйте самостоятельно отладить исходный код Java и посмотрите на метод readInput().

person freedev    schedule 07.04.2017
comment
Спасибо! любая документация/javadocs по этому поводу? - person Jay; 07.04.2017
comment
Да, позиция потока смещается вперед, но она должна быть сдвинута вперед на 1 строку, а не потреблять весь поток. - person Jay; 07.04.2017
comment
Вы забыли, что есть базовый буфер... может быть, я ошибаюсь... - person freedev; 07.04.2017
comment
Я дважды проверил, как read() перемещается вперед на позицию 1024 байта, а затем сохраняет результат в буфере. - person freedev; 07.04.2017