Read Input until control+d - java

I want to prompt the user to begin entering characters and I want them to be able to enter characters as long as they want until they hit control+d to exit.
For example, they can type a string of numbers like:
1234567
and as soon as they decide to hit control+d the line that they entered will be displayed (so without having to hit return)
I am thinking I'll need a buffered reader or something. Any suggestions?

What rlibby said is spot on: the CTL-D will cause the terminal to flush buffered input to the JVM. However, the keypress event itself is captured and acted on by the terminal and not passed through.
Fortunately, though, it's easy to detect. If the user hits CTL-D on a line of its own, there is no input to flush...which to the JVM is indistinguishable from EOF. Accordingly, System.in.read() will return -1 per the contract of InputStream. If you've wrapped System.in with a BufferedReader, readLine() will return null.
This is my main loop for an interactive command line tool I just wrote:
BufferedReader systemIn = new BufferedReader(new InputStreamReader(System.in, "UTF-8"));
String line;
while((line = systemIn.readLine()) != null) {
// my program loop.
}
One thing worth pointing out is that if the user hits CTL-D after inputting characters (but before hitting return), you'll get those characters. I don't believe there's a way to detect CTL-D when it's not on a line of its own.
DISCLAIMER: I have no idea how this applies to Windows.

http://download.oracle.com/javase/6/docs/api/java/io/BufferedInputStream.html#read%28%29
public class InputTest {
public static void main(String[] args) throws IOException {
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
StringBuilder out = new StringBuilder();
while (true) {
try {
int c = in.read();
if (c != 4) // ASCII 4 04 EOT (end of transmission) ctrl D, I may be wrong here
out.append (c);
else
break;
} catch (IOException e) {
System.err.println ("Error reading input");
}
}
System.out.println(out.toString());
}
}

You are confusing the roles of your Java program and of the terminal. The terminal buffers input and occasionally supplies it to the Java program, especially after line feeds. Ctrl+D also causes the terminal to make buffered input available, or, if nothing is buffered, to terminate the input. Asking for a program to do something on Ctrl+D is essentially asking it to do something when it reads all available input (and more may become available later). Buffering input on the Java side is going to make things more complicated, rather than less.

Just adding another option for people like me who turn up 9 or so years after the question was asked. If you are using java.util.Scanner, using a String input = in.next(); will drop a NoSuchElementException when Ctrl+d is used. In this case, you can use a try-catch block to catch that particular exception and end execution of your program (either by breaking or changing your iteration value to stop the loop).
import java.util.Scanner;
while (some condition) {
try {
(take your input using the in.next() function here)
...
} catch (java.util.NoSuchElementException e) {
(some condition) = false;
//OR break;
}
}

give this code a try. it actually worked for me
// press ctrl+Z(windows) and ctrl+D(mac, linux) for input termination
StringBuilder out = new StringBuilder();
String text = null;
Scanner scanner = new Scanner( System.in );
while( scanner.hasNextLine() )
{
text = new String( scanner.nextLine() );
out.append( text );
}
scanner.close();
System.out.println( out );
System.out.println( "program terminated" );

In a GUI, you can use a KeyListener.

Related

Scanner Freezes Program When No Input Is Given

I'm making an application that is mainly used with pipes. At the start of the program, here is how I get all my input:
Scanner scanner = new Scanner(System.in);
List<String> input = new ArrayList<>();
while (scanner.hasNextLine()) {
input.add(scanner.nextLine());
}
scanner.close();
When used with pipes, this works well. For example, running ls | java -jar ... it prints out all the lines given from ls. However, when I just run java -jar ... with no input/pipe, it freezes and nothing happens. Even when I press enter and mash my keyboard it's still stuck. I can only stop the program by using ctrl-c. I think this is because there is nothing to read so Scanner waits until there is something to read. How do I read all lines from System.in without freezing, or is there a better way to do this? Thanks.
Edit:
Whoever marked this as a duplicate clearly did not read my question. My question was to check if the System.in was empty to not freeze the program, not how to get past the input stage. It is freezing because there is nothing in System.in so Scanner is taking forever, but I want to check if there is nothing in System.in.
Answer:
Thank you #QiuZhou for the answer. I modified his second example to get this:
List<String> input = new ArrayList<>();
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
while (reader.ready()) {
String nextLine = reader.readLine();
input.add(nextLine);
}
and it works like a charm.
It's freezing because, as you said, there is nothing to read so Scanner waits until there is something to read, but you want to read all lines from System.in without freezing, it sounds kind of conflicting to me, it's not like reading from a file, we don't know how many lines whoever using your program is going to type in, unless the person tell us. I suggest you define a special character as end of input, for example, done, when the person decides he has finished, just enter done to end waiting:
Scanner scanner = new Scanner(System.in);
List<String> input = new ArrayList<>();
System.out.println("Please enter something. Enter 'done' when finished.");
while (scanner.hasNextLine()) {
String nextLine = scanner.nextLine();
if(nextLine.equals("done"))break;
input.add(nextLine);
}
scanner.close();
To check if there is something to read in System.in without being stuck there, you can do it by using BufferedReader.ready(), as the document reads, Note that in.ready() will return true if and only if the next read on the stream will not block.
System.out.println("Please enter something. Enter 'done' when finished.");
List<String> input = new ArrayList<>();
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
while(true) {
if (reader.ready()) {
String nextLine = reader.readLine();
if(nextLine.equals("done"))break;
input.add(nextLine);
}
//since ready() will not block, you can add additional logic here, like if it
//takes too long to read next line from input, you can just end here
// break;
}

Code after while loop never executes

Obviously, my real code is more complex, but here's an example:
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner input = new Scanner(System.in).useDelimiter("\n");
String[] cmdSplit = null;
while (true) {
while (input.hasNext()) {
cmdSplit = input.next().split("\\s+");
System.out.println("stuff");
}
for (int i = 0; i < cmdSplit.length; i++) System.out.println(cmdSplit[i]);
}
}
}
In the above example, the code takes input from System.in, splits it, and should output each piece. However, for some reason, the code after the inner while loop never executes. If I replace while with if, it works. If you test it, you can see it doesn't run infinitely, because it only prints "stuff" once, showing the loop runs once. What is the issue with the while loop?
Reading from System.in is different than reading from a file or other fixed-size source of input. The input doesn't necessarily exist until you create it, and so attempts to read it need to block until the input actually arrives (i.e. you type it). Try typing another line - you'll see the stuff message again; that will allow .hasNext() to return because there is now input.
To have .hasNext() return false the input source needs to be closed. For a command line application you can do this by sending the EOF signal (Ctrl+D on Linux) which tells the process stdin has no more input. That's not generally how you want a program to work, though, so if your intent is to only read one line and then move on, you should in fact be using an if instead of a while as you've tried to do. Later if you need to read more input you'll call .hasNext() again and your program will block there until the user passes more input.
As #user7 mentions your outer while (true) combined with while(input.hasNext()) is redundant. If you want to read only once get rid of the while (true) and use if (input.hasNext()). Otherwise if you want to read forever just combine the two loops:
while (input.hasNext()) {
cmdSplit = input.next().split("\\s+");
System.out.println("stuff");
for (int i = 0; i < cmdSplit.length; i++) System.out.println(cmdSplit[i]);
} // Loop will terminate once stdin is closed, e.g. by the user sending EOF.
Yes , your code won't go to the for loop because the Scanner.hasNext() will always listen to the console for inputs.
You have to break the loop in order to come out and go to the for loop.
Scanner input = new Scanner(System.in).useDelimiter("\n");
String[] cmdSplit = null;
while (true) {
while (input.hasNext()) {
cmdSplit = input.next().split("\\s+");
System.out.println("stuff");
break;
}
for (String element : cmdSplit) {
System.out.println(element);
}
}
The reason it is printing "stuff" only one time is because the hasNext() returned false.
Let me explain what I have observed.
To get "stuff" printed indefinately the assignment has to be removed. meaning once you assigned the input the scanner does not have any more token
The java.util.Scanner.hasNext() method Returns true if this scanner has another token in its input.
This will print indefinitely
while (input.hasNext()) {
// cmdSplit = input.next().split("\\s+");
System.out.println("stuff");
}

Code Behaving Differently On Different IDE

I am writing a program that takes information about a track and field competition and then does some computation with it. The issue I'm having now is only in the first user input section.
I first wrote my code in BlueJ and it worked fine. Then, I tried compiling it in JCreator and started getting this error where the program would only receive 3 user inputs before going onto the next piece of code when it should have received 5 (which it did when I compiled in BlueJ).
When I placed a System.out.println statement after the input statement however, the program (in JCreator) DID receive all 5 statements before proceeding. When I commented it out again, it only received 3 statements before continuing. Here is the code below.
String[] events = new String[5];
System.out.println("Please enter the 5 events in this competition.");
for(int i = 0; i < events.length; i++)
{
events[i] = input.nextLine();
System.out.println(i);
}
This is the output with the System.out.println statement.
This is the output with the System.out.println statement commented out.
Change the line
events[i] = input.nextLine();
to
String newLine;
while( (newLine = input.nextLine()).isEmpty() );
events[i] = newLine;
That should consume the extra newline characters and leave you with just the legitimate input in events.
The possible cause of your problem is newline characters, which are interpreted as line. It seems you are having additional newline characters in your input buffer.
You may check your IDE what character is provided when an enter key is pressed.
It seems you are using Scanner class to read input. You may try to wrap your System.in with InputStreamReader, that might help. (not sure, try it out)
Scanner input = new Scanner(new InputStreamReader(System.in));
events[i]=input.nextLine();
Instead you may also try to use BufferedReader to read the input.
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
events[i]=reader.readLine();
Hope it will help!!

Scanner nextLine, stuck in while loop or exiting at odd times

I started doing the CodeAbbey problems last night, they mentioned using stdIn since some the input data is long so copy/paste is much easier than by hand. I had never used the Scanner before so it looked easy enough. I got it working for single line inputs then I got a problem where the input was:
867955 303061
977729 180367
844485 843725
393481 604154
399571 278744
723807 596408
142116 475355
I assumed that nextLine would read each couple, xxxx yyyyy. I put the code in a while loop based on if nextLine is not empty. It runs, but I get weird output, and only after I hit return a few times.
package com.secryption;
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
System.out.println("Input: ");
Scanner scanner = new Scanner(System.in);
String input = "";
while(!(scanner.nextLine().isEmpty())) {
input = input + scanner.nextLine();
}
String[] resultSet = input.split("\\s+");
for(String s : resultSet) {
System.out.println(s);
}
}
}
I thought I might need something after adding scanner.nextLine() to input. I tried a space and that didn't help. I tried a newline and that didn't make it better.
This "should" put all the numbers in a single array, nothing special. What am I missing with scanner?
EDIT: Ok so #Luiggi Mendoza is right. I found this How to terminate Scanner when input is complete? post. So basically it it working, I just expected it to do something.
The problem is here:
while(!(scanner.nextLine().isEmpty())) {
input = input + scanner.nextLine();
}
Scanner#nextLine reads the line and will continue reading. You're reading two lines and not storing the result of the first line read, just reading and storing the results of the second.
Just change the code above to:
StringBuilder sb = new StringBuilder();
while(scanner.hasNextLine()) {
sb.append(scanner.nextLine()).append(" ");
}
hasNext() is an end of file indicator that terminates by combining keys control d on Mac ox and control z on windows pressing enter won't send the right message
to JVM

Java redirecting System.in - InputStream.read called but nothing "returned"

So I'm trying to redirect a textbox to System.in. I've seen this thread Redirect System.in to swing component , and seem to have everything working:
private class SystemIn extends InputStream {
#Override
public int read() {
int x = GUIConsole.this.read();
System.out.println("Read called: returning " + x);
return x;
}
}
The reads seem to be executing properly: I have a helper
public static String readLine(String msg) {
String input = "";
InputStreamReader converter = new InputStreamReader(System.in);
BufferedReader in = new BufferedReader(converter);
while (input.length() == 0) {
try {
print(msg);
input = in.readLine().trim();
} catch (Exception ignore) {
//
}
}
return input;
}
And I call
String in3 = SimpleConsole.readLine("Enter text: ");
System.out.println("You said: " + in3);
So the "Read called: returning #" is printed. I get the proper character codes followed by the final -1 when I call something. The read method blocks until input is ready, as the docs specify.
However, I only get the "Read called..." messages, and the next line ("You said...") never executes (it's stuck still reading). I can't for my life figure out what the problem is - If you want to see more code (although I think the "Read called..." messages show it's doing the right thing) just let me know.
Is there something else I should do to be able to call readLine and get the input from a textbox? I also tried overriding the other methods in inputstream without luck (again, the read method is executing properly)
Agh, as usual, I find the answer soon after I post a question (and I say this every time too). Anyways, in case someone else is looking for this:
When reading/implementing the inputstream, make sure the last call returns the newline char
\n' (followed by -1). Because if you're readline a line it waits for that last newline feed.
So for me, I append the text from a textfield plus a newline character. Now it works, the program finishes reading and printing the final message.

Categories

Resources