1

Why am I not able to read the second line on using the second instance of Scanner? I get a "java.util.NoSuchElementException: No line found".

I understand I should use the hasNextLine() and avoided the exception, but my question is why would the second line not be available at all? Also, the reason there are multiple scanner instances is because in reality, the scanner is being instantiated in a method that is called multiple times, and I am not looking for a solution or a fix there.

Please note that I am not closing either the scanners or the streams. I am reading only 1 line each using the scanners, while I have 3 lines in the stream.

Here is my program simplified:

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());
}

Output is:

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)
Jay
  • 11
  • 2
  • 2
    Never ever ever use two scanner on the same stream – freedev Apr 07 '17 at 13:50
  • 1
    And why are you using a ByteArrayInputStream when you want to read string lines?! – GhostCat Apr 07 '17 at 13:51
  • Just use the same scanner – Hackerman Apr 07 '17 at 13:51
  • @GhostCat I wanted an inputstream to pass to the Scanner's constructor, and the simple way to do this was to create a string and convert it to the ByteArrayInputStream. – Jay Apr 07 '17 at 13:58
  • The duplicate gives a recommendation, not an explanation of what is happening. – Jay Apr 07 '17 at 14:03
  • @Berger The duplicate gives a recommendation, not an explanation of what is happening. – Jay Apr 07 '17 at 14:19
  • 1
    My theory: Scanner often is reading data from files or other resources which require gaining permission to read from OS. Getting such permission usually takes some time and can be very expensive. So we don't want to ask for this permission for each specific character. Instead we want to gain it once, and while we have it read more of them, not to small, but also not to big since reading also takes some time which may be needed in other places. In this case it looks like buffer/cache size exceed size of bytes from string, so all of them ware read leaving none to next Scanner. – Pshemo Apr 07 '17 at 15:09
  • Try to read ByteArrayInputStream containing more bytes, maybe 10_000 and see how many of them will be still `available()` after reading from first scanner. – Pshemo Apr 07 '17 at 15:11
  • @Pshemo That's what I've verified debugging the source code. Each `nextLine()` method call move ahead the stream position of 1024 bytes. That's why the second scanner raise the exception. Do you think worth reopen this question after my answer or should I move it into the [Java Multiple Scanners](http://stackoverflow.com/questions/19766566/java-multiple-scanners)? – freedev Apr 07 '17 at 15:14
  • @Pshemo True, Scanner would be using buffering internally. But if using one instance of Scanner to read a single input makes the stream unreadable again, then isn't this a problem? – Jay Apr 07 '17 at 15:18
  • @Jay I don't see any problem here. Data is moved from stream to Scanner so it is still available to us via Scanner which is our original goal. We simply should use only one Scanner per resource and there will be no problems. What you are trying to do here is against Scanner's (or any Reader) original idea which is why you are fighting with problems which shouldn't normally exist (not to mention that if you close first Scanner it will also close resource it is reading, which means we won't be able to read it using another Scanner). – Pshemo Apr 07 '17 at 15:22

1 Answers1

0

Use multiple scanners (on same stream) is a very bad practice, because scanners consume the stream they share.

This the cause of the exception java.util.NoSuchElementException: No line found you had.

I've tested your code and the exception is raised by second nextLine() invocation.

Into each Scanner class is saved a reference to the same input stream.

When scanner1.nextLine() method is invoked, is read a bunch of bytes on the stream and the position is moved ahead.

Just to be clear, I've double checked debugging the source code of Scanner class.

When the nextLine() method is called, behind the scenes the stream is moved ahead of 1024 position copying the result into a buffer

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

Try to debug the Java source code yourself and look at the method readInput().

freedev
  • 17,230
  • 4
  • 83
  • 98