1

This question could be better rephrased as: How do you detect EOF in InputStream without blocking

I have a java program that is able to take input from System.in directly (without using a Scanner), I can also pipe input into the java program directly just like any program. However, when I pipe input into the program, the program keeps running. The idea is to stop the program if input was piped into it, but keep it running if we are waiting for user input.

My question is how do I use an InputStream to detect when the pipe is over (and end the program)? If I was using a Scanner I understand that I would be able to use Scanner#hasNext() to detect if I can read more however, I would like to do this just with an InputStream if possible.

My code currently looks something similar to this:

final InputStream in = System.in; // sometimes in won't be System.in
final byte[] buffer = new byte[1024];
while(true){
    int len;
    // the reason I use in.available() is so I only read if in.read() won't block
    while(in.available() > 0 && (len = in.read(buffer)) > -1){
        String s = new String(buffer, 0, len);
        // do something with string
    }
    // sometimes do other stuff not relevant to this question
}

I am open to simpler solutions. The reason I am not using a Scanner object is because I need to read individual chars at a time, not just lines at a time. The purpose of this program isn't only input from the user, most of the time the input isn't even from System.in, I just included System.in in the above example because it will sometimes be System.in

EDIT: For whatever reason, I've dedicated about 2 hours of my life searching the internet for a simple solution for this. I've tried just using an InputStream, and even converted my code to use a ReadableByteChannel (created using Channels.newChannel()) to see if that would work. Every implementation I could find for something that wraps an InputStream is blocking. I guess the only way to fix this is to use another thread. Good luck future viewers.

retodaredevil
  • 1,261
  • 1
  • 13
  • 20

3 Answers3

0

You don't really need in.available(). When the pipe is depleted, your variable len will be -1 and the internal while loop will end. You can just break the external while loop depending on your need.

tanyehzheng
  • 2,201
  • 1
  • 20
  • 33
  • My problem is that I cannot call read whenever I want because that will sometimes block, that's why I only read if in.available() > 0 which doesn't tell me if the pipe is depleted. – retodaredevil Nov 14 '18 at 01:35
  • In that case, you might want to consider using a separate thread to `read` or use NIO2 – tanyehzheng Nov 14 '18 at 01:36
  • ah, another Thread is what I was afraid of. I'll look at nio.2 and see what the best option is. Thanks – retodaredevil Nov 14 '18 at 01:40
0

Sitting in a while loop and calling in.available is not efficient. It will use a lot of CPU.

If you call in.read() and it returns -1 then it means there is no more input to read. So you should do this but of course then it blocks.

If you need to do something else while this is going on then use a different Thread for the other code. Make sure to set that other thread as a daemon thread so it does not keep the program running when this main thread's method finishes.

Sarel Botha
  • 12,419
  • 7
  • 54
  • 59
0

Simply remember whether read() returned -1, i.e. increase the scope of len.

final InputStream in = System.in;
final byte[] buffer = new byte[1024];
for (int len = 0; len != -1; ) {
    while (in.available() > 0 && (len = in.read(buffer)) != -1) {
        String s = new String(buffer, 0, len);
        // do something with string
    }
    // do other stuff
}
Andreas
  • 154,647
  • 11
  • 152
  • 247
  • 1
    Ah I really wished that worked. I found that when calling in.read(), it would give me all the bytes until the end of the file, returning a positive length, and then the next call to in.available() == 0 which would stop me from calling in.read() again when it would return -1 (if I had called it) – retodaredevil Nov 14 '18 at 02:08
  • @retodaredevil Which means that it cannot be done like this, and you have to use threads like other answers have suggested. – Andreas Nov 14 '18 at 02:19