Get out the value of the scanner without advancing it - Java
I want to get the value of the input in the scanner without advancing it. Currently, I am using my scanners input as System.in.
final var sc = new Scanner(System.in);
I know of the hasNext methods on scanner, and they are currently my best/only way to check its input without advancing it.
Here is how I ensure a positive integral input from sc for example.
public static int getPositiveIntegerInput(Scanner sc) {
System.out.println("Please input a positive integer");
sc.useDelimiter("\n");
while (!sc.hasNextInt() || sc.hasNext(".*[^\\d].*")) {
System.out.println("Invalid input, please try again");
sc.nextLine();
}
return sc.nextInt();
}
I want to extend this notion of checking sc's input without advancing it to actually getting sc's input without advancing it.
What I have tried to to this point
I have gone through the implementation details of hasNext() on Scanner.
Implementation of hasNext:
public final class Scanner {
public boolean hasNext(Pattern pattern) {
ensureOpen();
if (pattern == null)
throw new NullPointerException();
hasNextPattern = null;
saveState();
modCount++;
while (true) {
if (getCompleteTokenInBuffer(pattern) != null) {
matchValid = true;
cacheResult();
return revertState(true);
}
if (needInput)
readInput();
else
return revertState(false);
}
}
}
It seemed to me at least, that one can get scanner's input from the method getCompleteTokenInBuffer, but truly I don't really understand how it works. I don't know if that method alone gets the value of scanner without advancing it, or if it advances it then something else reverts it back to the state it was in before the input as if it has not advanced at all, or if it gets it in combination with something else, or really how at all.
I have been playing around with invoking the private methods Scanner through Java's reflection API, to try to actually return the token holding sc's input value without actually advancing methods (but to be honest, I'm just playing around with it and don't know how to actually accomplish what I want to do).
public static void main(String[] args) {
final var sc = new Scanner(System.in);
sc.useDelimiter("\n");
var str = "";
try {
Method method = Scanner.class.getDeclaredMethod("getCompleteTokenInBuffer", Pattern.class);
method.setAccessible(true);
str = (String) method.invoke(sc, Pattern.compile(".*"));
} catch (Exception e) {
System.out.println("Well, that didn't work!");
System.out.println("Exception: " + e);
}
System.out.println("getCompleteTokenInBuffer: " + str);
// Prints: "getCompleteTokenInBuffer: null"
}
Note: The method above does not wait for an input before get the value of sc's input and hence returns a value of null.
Goal:
Just to reiterate, I would like to find away to capture and return a Scanner object's input value with actually advancing it.
What you're looking for might otherwise be referred to as a peek function.
This answer on another thread indicates that you might be served by creating a wrapper class around Scanner that implements this functionality, since the Scanner class itself does not implement it.
Related
I have Java code that asks for user input and then stores this data in a string variable. The below function is part of a class called 'number' and is called in the main function.
public static void setVal(int i){
Scanner readIn = new Scanner(System.in);
//while (readIn.hasNextLine()){
str = readIn.nextLine();
numCheck = false;
if (i == 1){
while (!numCheck){
if (str.contains(" ")){
System.out.println("Please input a single item.");
str = readIn.nextLine();
}
else if (!isNumeric(str)){
System.out.println("Please input a valid number.");
str = readIn.nextLine();
}
else {
numCheck = true;
value = Double.parseDouble(str);
readIn.close();
}
}
readIn.close();
}
else if (i == 2){
while (!numCheck){
if (str.contains(" ")){
System.out.println("Please input a single item.");
str = readIn.nextLine();
}
else if (!isNumeric(str)){
System.out.println("Please input a valid number.");
str = readIn.nextLine();
}
else {
numCheck = true;
secondV = Double.parseDouble(str);
readIn.close();
}
}
readIn.close();
}
else {
System.out.println("An error has occurred.");
}
// }
readIn.close();
}
Part of the main function looks like this:
number input = new number();
for (int i = 1; i <= 2; i++){
input.setVal(i);
System.out.println("Now please input a second value for computing with the first.");
input.setVal(i);
}
I use the same function twice but handing it a different argument to distinguish assignment of the input to a different variable but when it runs a second time it throws a no line found error.
Applying some other advice you can see commented out I have added a 'hasNextLine()' check to check if the line exists before executing the code but this ends up at a 'Scanner closed' error even though I invoke a new instance of Scanner every time the function runs. I have also closed the scanner appropriately to ensure minimisation of errors.
I have no idea what's going wrong as I can create a Scanner in the main function and call '.nextLine()' as many times as requried without an error but when called again through a class method, I receive these errors.
Any help is appreciated, thanks in advance.
Scanner.close() documentation states that
If this scanner has not yet been closed then if its underlying
readable also implements the Closeable interface then the readable's
close method will be invoked. If this scanner is already closed then
invoking this method will have no effect.
On closing scanner, you are also closing System.in input stream, so when you reopen the scanner it will not find any open input stream.
Refer : java.util.NoSuchElementException - Scanner reading user input
Better pass scanner object from outside method as argument and then close it in calling method only when you are done with it.
Just to point out, is your String object str Static?
If not then you can't use it in your static method. Better you remove the static from method declaration.
You have to close the scanner when everything is done.
You have closed the scanner inout stream readIn.close(); twice.
You are closing the stream before picking line by line from the file. So you have to close it once after all the instances that use readIn is finished.
The javadoc for Scanner.hasNextLine() states:
Returns true if there is another line in the input of this scanner.
This method may block while waiting for input. The scanner does
not advance past any input.
Under what conditions will the method block?
It depends on the source that the Scanner gets the input from.
For example, if it's a file, the entire input is available, so hasNextLine() wouldn't block (since it can know with certainty when the end of the file is reached and there's no more input.
On the other hand, if the source is standard input, there can always be more input - the user can always type more input - so hasNextLine() would block until the user types in a new line of input.
How to decide if it will block?
To decide if hasNextLine will block or not is unfortunately not a supported use case.
This is because the underlying sources doesn't always provide an API for peeking in the stream. Put differently, the implementation of hasNextLine calls methods that themselves may block so the problem is sort of inherent.
So, what to do?
If this is indeed a required use case, I would recommend one of the following approaches:
Make sure the conditions are right for the hasNextLine. Only provide the scanner with sources that have a definite end (such as a file or string) and never an "open ended" input such as System.in.
If this is part of an API you could wrap the scanner in your own class that only exposes "safe" constructors.
Roll your own class from scratch that does have a willHasNextLineBlock type of method. This could probably be implemented somewhat robustly using InputStream.available.
Under the category of super ugly workarounds we find:
Making an attempt at calling hasNextLine in a separate thread and see if it returns within reasonable time, as follows:
boolean wouldBlock = false;
Thread t = new Thread(() -> s.hasNextLine());
t.start();
try {
t.join(100);
} catch (InterruptedException e) {
wouldBlock = true;
}
Use a custom input stream (something like a peekable stream that one could tap into before calling hasNextLine. Something like the this
CustomStream wrapped = new CustomStream(originalSource)
Scanner s = new Scanner(wrapped);
...
if (wrapped.hasNextLine())
// s.hasNextLine would not block
else
// s.hasNextLine would block
(Note however that this is somewhat unsafe, since the scanner may have buffered some data from the CustomStream.)
Assuming by "decide if it will block" you mean that you want to know when it will bock.
Have a look at where the input is assigned in the hasNextLine method
String result = findWithinHorizon(linePattern(), 0);
Now, have a look at the findWithinHorizon method
public String findWithinHorizon(Pattern pattern, int horizon) {
ensureOpen();
if (pattern == null)
throw new NullPointerException();
if (horizon < 0)
throw new IllegalArgumentException("horizon < 0");
clearCaches();
// Search for the pattern
while (true) { //it may block here if it never break
String token = findPatternInBuffer(pattern, horizon);
if (token != null) {
matchValid = true;
return token;
}
if (needInput)
readInput();
else
break; // up to end of input
}
return null;
}
As you can see, it will loop infinitely until the end is reached, or until it succeed to read.
findPatternInBuffer is a private method of the Scanner class that try to read the input.
private String findPatternInBuffer(Pattern pattern, int horizon) {
matchValid = false;
matcher.usePattern(pattern);
int bufferLimit = buf.limit();
int horizonLimit = -1;
int searchLimit = bufferLimit;
if (horizon > 0) {
horizonLimit = position + horizon;
if (horizonLimit < bufferLimit)
searchLimit = horizonLimit;
}
matcher.region(position, searchLimit);
if (matcher.find()) {
if (matcher.hitEnd() && (!sourceClosed)) {
// The match may be longer if didn't hit horizon or real end
if (searchLimit != horizonLimit) {
// Hit an artificial end; try to extend the match
needInput = true;
return null;
}
// The match could go away depending on what is next
if ((searchLimit == horizonLimit) && matcher.requireEnd()) {
// Rare case: we hit the end of input and it happens
// that it is at the horizon and the end of input is
// required for the match.
needInput = true;
return null;
}
}
// Did not hit end, or hit real end, or hit horizon
position = matcher.end();
return matcher.group();
}
if (sourceClosed)
return null;
// If there is no specified horizon, or if we have not searched
// to the specified horizon yet, get more input
if ((horizon == 0) || (searchLimit != horizonLimit))
needInput = true;
return null;
}
I posted the whole method to give you a better idea of what I meant by "succeed to read".
This program goes in an infinite loop in while cycle. Please, can someone tell me why?
import java.util.Scanner;
public class program {
public static void main(String[] pars) {
System.out.println("Insert something.");
Scanner read = new Scanner(System.in);
String s = "";
while(read.hasNext()) {
System.out.println(read.next());
}
System.out.println("End of program");
}
}
Read the Javadoc of Scanner#hasNext():
Returns true if this scanner has another token in its input. This method may block while waiting for input to scan. The scanner does not advance past any input.
Hence the while loop will always be executed in your case, each time waiting for input from the user. Since the Scanner is linked to System.in, the input stream will always block until the user inputs a string and hasNext() will always return true, unless the user signals the end of file (e.g. through the Ctrl+z combination on Windows). Scanner#hasNext() is more convenient when reading from files where the input size is known and the end of the file marks the end of the stream.
One way to end the loop here is to add a condition on the input:
while (read.hasNext()) {
s = read.next();
if(s.equals("quit")) {
break;
}
System.out.println(s);
}
P.S.: It is more conventional to name classes starting with an uppercase letter.
The problem is this line:
while(read.hasNext()) {
If you use System.in as a stream provided by the user, it will - if no such input is available - as #manouti says, block and wait for input. But even if you provide input, it will keep waiting. The system has no means to detect whether the user wants to provide additional input in the future.
It will only stop, if the Stream ends. This can be under two conditions:
The end of the file (in case of I/O redirection like java -jar program.jar < input.dat; or
The user marks the end of a stream, in most shells with Ctrl+D. This marks the end-of-stream.
An alternative is to provide some kind of stop directive. Something like "END". Thus:
while(read.hasNext()) {
String nx = read.next();
if(nx.equals("END")) {
break;
}
System.out.println(nx);
}
Just remove while loop
public static void main(String[] pars) {
System.out.println("Insert something.");
Scanner read = new Scanner(System.in);
String s = "";
System.out.println(read.next());
System.out.println("End of program");
}
Or if u want display certain no.of string then mention condition properly.
public static void main(String[] pars) {
System.out.println("Insert something.");
Scanner read = new Scanner(System.in);
String s = "";
int i=0;
while(i<5) {
System.out.println(read.next());
i++;
}
System.out.println("End of program");
}
I just completed an application which prompts the user for a text File input IO but I have something to clarify as the final part, While loop I actually managed to refer it to a tutorial on google. In this loop, there is a if-else statement and for the else part I don't understand why is it necessary.
Here's my code:
import java.io.*;
import java.util.*;
class FileReadingExercise2 {
public static void main(String[] args) {
Scanner userInput = new Scanner(System.in);
Scanner fileInput = null;
do {
try {
System.out.println("Please enter the name of a file or type QUIT to finish");
String a = userInput.nextLine();
if (a.equals("QUIT")) { // if user inputs QUIT then stop application
System.exit(0);
}
fileInput = new Scanner(new File(a)); // the file contains text and integers
} catch (FileNotFoundException e) {
System.out.println("Error - File not found");
}
} while (fileInput == null);
int sum = 0;
while (fileInput.hasNext()) // continues loop as long as there is a next token
{
if (fileInput.hasNextInt()) // if there is an int on the next token
{
sum += fileInput.nextInt(); // then adds the int to the sum
} else {
fileInput.next(); // else go to the next token
}
}
System.out.println(sum);
fileInput.close();
}
}
As you can see, as long as the fileInput Scanner has a next token to look up to then operate the if else statement. If fileInput has a next Int then adds it up to the sum variable. So from what I think is that this will be sufficient. Once fileInput has no more token to read, it shall get out of the while loop isn't it? Why does it has still go onto the next token? I'm confused. Please advise thanks! ;)
Why does it has still go onto the next token?
That is because when nextInt() is executed it will consume the int number within the file but within it, it has a newLine character that needs to be consume and that is when next is executed to consume that newLine after the int number.
sample file content:
1
what actually in there is 1 character and newline \n character
In this loop, there is a if-else statement and for the else part I don't understand
why is it necessary.
fileInput.hasNexInt() method returns true if int value found and than it performs adding operation. if next value is not int type than else part will perform where fileInput.next() will return next value(pointer will points after that value), performs nothing means escaping next value(which can be any type except int-type). Again if condition will check for int.
I am accessing a Scanner that helps retrieve RFID Tags from an RFID Reader. I tried using a loop around my Scanner.next(); but that wouldn't do anything. I think that once my Scanner accesses the RFID Reader's method, that it just permanently stays in there. Is there any way to exit the method for the RFID and then perform the rest of my loop?
//The beginning of the loop
while (!doneYet) {
//Just a set boolean variable that I try to use later
Inter = false;
//A question class that I created, obtains and displays a random question
Test = Work.randomQuestion();
Test.display();
//This is where my problems seem to start, this is the RFID Readers Method.
rfid.addTagGainListener(new TagGainListener() {
public void tagGained(TagGainEvent oe) {
Temp = oe.getValue();
if (Test.getRFIDTag().equals(Temp)) {
System.out.println("You are Correct");
count++;
System.out.println(count);
Inter = true;
System.out.println("out");
/*
* try { System.in.close(); } catch (IOException e) {
* TODO Auto-generated catch block e.printStackTrace();
* }
*/
} else if (!Test.getRFIDTag().equals(Temp)) {
System.out.println("Sorry, you are wrong");
}
return;
}
});
// Before the RFID Opens though, this code is initiated
rfid.openAny();
rfid.waitForAttachment(1000);
sc = new Scanner(System.in);
//This is where I attempted to control the RFID Reader, but this doesn't work
if(!Inter){
sc.next();
}
//How I attempt to end the initial while loop
if(count>10){
doneYet = true;
}
}
Thanks in advance for all of the help.
Your scanner doesn't actually do anything. sc.next() just skips initial whitespace, and returns the first string of non-whitespace characters; but it just discards that string, never (for example) passing it to rfid. As far as I can see, nothing ever triggers rfid's TagGainListener. (And why do you add a new TagGainListener for each pass through the loop, anyway? Surely you only need one listener?)