How to close a scanner without closing the underlying System.in? [duplicate] - java

This question already has answers here:
Close Scanner without closing System.in
(3 answers)
Closed last month.
If I close one scanner object and make a new one and try to read some more input, I'm getting the NoSuchElementException exception.
My code works fine but it gives me a warning if I don't close the scanner. However, if I close it to get rid of the warning, I also close System.in... How do I avoid this?
Also, are there any consequences of not closing the scanner?
EDIT: Here's my code:
This is the NameAddressExists() method:
public void NameAddressExists() {
System.out.println("Enter name");
Scanner sc = new Scanner(System.in);
String n = sc.next();
System.out.println("Enter address");
String a = sc.next();
int flag = 0;
for(int i = 0; i < count; i++) {
if(array[i].name .equals(n) && array[i].address .equals(a)) {
System.out.println("True");
flag = 1;
}
}
if(flag != 1) {
new Agency(n, a);
}
sc.close();
}
This is the PanNumberExists() method:
public boolean PanNumberExists() {
Scanner s = new Scanner(System.in);
String n = "";
System.out.println("Enter the 5 digits");
try {
n = s.nextLine();
}catch(Exception e) {
System.out.println(e);
}finally {
s.close();
}
if(n .equals(this.PAN.subSequence(4,9))) {
return true;
}
else {
return false;
}
}
These methods are called from the following main() method:
public static void main(String args[]) {
Agency obj1 = new Agency("XYZ", "ABCD");
Agency obj2 = new Agency("XYZ", "ABCDEFG", "+91083226852521", "ab 1234567", "abcd12345ab");
// Agency obj3 = new Agency("XYZ", "TSRK", "36", "ab 1234567", "abcd12345ab");
obj1.NameAddressExists();
System.out.println(obj2.PanNumberExists());
}
As you can see, I first call the NameAddressExists() method, in which I open, use and close a Scanner named 'sc'. This works fine and gives me the correct output. Next, I call the PanNumberExists() method, in which I open another Scanner named 's' and try to use it to get some input from the user. This is where I receive the NoSuchElementException exception. If I leave the Scanner 'sc' open in my NameAddressExists() method, then I don't get this error.

You can use the Decorator pattern and create custom InputStream that can't be closed, then pass it to the Scanner
import java.io.IOException;
import java.io.InputStream;
public class PreventClosingInputStream extends InputStream {
private InputStream inputStream;
public PreventClosingInputStream(InputStream inputStream) {
this.inputStream = inputStream;
}
#Override
public int read() throws IOException {
return inputStream.read();
}
#Override
public void close() throws IOException {
// Don't call super.close();
}
}
Then, in your code:
PreventClosingInputStream in = new PreventClosingInputStream(System.in);
Scanner s = new Scanner(in);
// ...
s.close(); // This will never close System.in as there is underlying PreventClosingInputStream with empty close() method
Using try-with-resources:
try (PreventClosingInputStream in = new PreventClosingInputStream(System.in);
Scanner s = new Scanner(in);) {
// ...
// resources will be automatically closed except of System.in
}

Related

Scanner asks for input twice and im slowly loosing my mind

i am trying to get two inputs from the scanner (multiple times if needed).
The code in Question is in the else section of the main function but i decided to share everything since something may collide, i don't know. the problem is that the first scanner(the timed one) works fine but the other two scanners (scanner1) require me to first press enter once then input the data and press enter again. Also if it has something to do with having two scanners then i would like for scanner1 not to be timed. Its not tragic but a flaw and i would like to fix it. Can somebody help me because i am running out of ideas.
Sorry if i made spelling mistakes or if my gramar sucks.
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Scanner;
public class TimedScanner implements Runnable
{
public static void main(String[] args) throws IOException {
TimedScanner scanner = new TimedScanner();
System.out.print("Enter the number of searchagents you want to add in 15 second: ");
String input = scanner.nextLine(15000);
if (input == null)
{
System.out.println("\nNothing was entered. Continuing...");
}
else
{
Scanner scanner1 = new Scanner(System.in);
int number = Integer.parseInt(input);
File myFile = new File("searchagent_list.txt");
if (!(myFile.exists())) {
myFile.createNewFile();
}
for (int i = 0; i < number; i++) {
System.out.println("Press Enter!\nEnter the name of searchagent #"+(i+1));
String name = scanner1.nextLine();
System.out.println("Press Enter!\nEnter the adress of searchagent #"+(i+1));
String adress = scanner1.nextLine();
FileWriter fWrite = new FileWriter(myFile, true);
BufferedWriter bWrite = new BufferedWriter(fWrite);
bWrite.write(name+"##"+adress+"##\n");
bWrite.close();
}
System.out.println("Done");
}
}
private Scanner scanner;
private StringBuilder buffer;
private boolean reading;
private Thread t;
public TimedScanner()
{
scanner = new Scanner(System.in);
buffer = new StringBuilder();
reading = false;
t = new Thread(this);
t.setDaemon(true);
t.start();
}
public String nextLine(long time)
{
reading = true;
String result = null;
long startTime = System.currentTimeMillis();
while (System.currentTimeMillis() - startTime < time && result == null)
{
try
{
Thread.sleep(30);
}
catch (InterruptedException e)
{
}
synchronized (buffer)
{
if (buffer.length() > 0)
{
Scanner temp = new Scanner(buffer.toString());
result = temp.nextLine();
}
}
}
reading = false;
return result;
}
#Override
public void run()
{
while (scanner.hasNextLine())
{
String line = scanner.nextLine();
synchronized (buffer)
{
if (reading)
{
buffer.append(line);
buffer.append("\n");
}
else
{
// flush the buffer
if (buffer.length() != 0)
{
buffer.delete(0, buffer.length());
}
}
}
}
}
}```
The return after reading will solve your issue:
#Override
public void run() {
while (scanner.hasNextLine()) {
String line = scanner.nextLine();
synchronized (buffer) {
if (reading) {
buffer.append(line);
buffer.append("\n");
return;
} else {
// flush the buffer
if (buffer.length() != 0) {
buffer.delete(0, buffer.length());
}
}
}
}
}
but there is no benefit to create more than one Scanner reference. It's simply reading input from System.in stream.

Scanner for input file and storing data objects from input file in array

Basically, I had to create a scanner for a given file and read through the file (the name is input through the terminal by the user) once counting the number of lines in the file. Then after, I had to create an array of objects from the file, of the correct size (where the num of lines comes in). Then I had to create another scanner for the file and read through it again, storing it in the array I created. And lastly, had to return the array in the method.
My problem is I cannot seem to get the second scanner to actually store the file objects in the array.
I've tried using .nextLine inside a for loop that also calls the array, but it doesn't seem to be working.
public static Data[] getData(String filename) {
Scanner input = new Scanner(new File(filename));
int count = 0;
while (input.hasNextLine()) {
input.nextLine();
count++;
}
System.out.println(count);
Data[] data = new Data[count];
Scanner input1 = new Scanner(new File(filename));
while (input1.hasNextLine()) {
for (int i = 0; i < count; i++) {
System.out.println(data[i].nextLine);
}
}
return data;
}
I expect the output to successfully read the input file so that it can be accessed by other methods that I have created (not shown).
You should definitely use an IDE if you don't have one, try intellij... There you have autocompletion and syntax checking and much more.
It is not clear what you want to do in your for loop, because there are several mistakes, for example the readline() function works only with the scanner objekt, so you can do input.nextline() or input1.nextline()`...
so I just show you, how you can get the Data from a file with Scanner:
import java.io.File;
import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.Scanner;
public class Readfile {
public static void getData(String filename) throws FileNotFoundException {
ArrayList<String> test = new ArrayList<>(); //arraylist to store the data
Scanner inputSc = new Scanner(new File(filename)); //scanner of the file
while (inputSc.hasNextLine()) {
String str = inputSc.nextLine();
System.out.println(str); //print the line which was read from the file
test.add(str); //adds the line to the arraylist
//for you it would be something like data[i] = str; and i is a counter
}
inputSc.close();
}
public static void main(String[] args) {
try {
getData("/home/user/documents/bla.txt"); //path to file
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
}
You don't need to read thru the file twice - just use an ArrayList to hold the data that's coming in from the file, like this, and then return Data[] at the end:
public static Data[] getData(String filename) {
List<Data> result = new ArrayList<>();
try (Scanner input = new Scanner(new File(filename))){
while (input.hasNextLine()) {
Data data = new Data(input.nextLine());
result.add(data);
}
}
return result.toArray(new Data[0]);
}
Not clear what Data.class do you mean, if you switch it to String, the problem obviously would be in this line
System.out.println(data[i].nextLine);
if you want to assign and print simultaneously write this
for (int i = 0; i < count; i++) {
data[i] = input1.next();
System.out.println(data[i]);
}
and dont forget to close your Scanners, better use try-with-resources.
If your Data is your custom class you'd better learn about Serialization-Deserialization
Or use some ObjectMapper-s(Jackson, for example) to store your class instances and restore them.
Your way of opening the file just to count the lines and then again looping through its lines to store them in the array is not that efficient, but it could be just a school assignment.
Try this:
public static Data[] getData(String filename) {
Scanner input = null;
try {
input = new Scanner(new File(filename));
} catch (FileNotFoundException e) {
e.printStackTrace();
return null;
}
int count = 0;
while (input.hasNextLine()) {
input.nextLine();
count++;
}
input.close();
System.out.println(count);
Data[] data = new Data[count];
try {
input = new Scanner(new File(filename));
} catch (FileNotFoundException e) {
e.printStackTrace();
return null;
}
for (int i = 0; i < count; i++) {
Data d = new Data(input.nextLine(), 0, 0);
data[i] = d;
System.out.println(data[i].name);
}
input.close();
return data;
}
After the 1st loop you must close the Scanner and reopen it so to start all over from the first line of the file.

Program looping infinitly

I wrote program that take command from user and perform particular functionality. However, there is something wrong with the functionality read and write input to file which cause the loop to run indefinitely.
import java.util.HashMap;
import java.util.Scanner;
public class cShell{
static String Currentpath="C:\\";
public String Current = Currentpath;
static HashMap<String, ICommand> myhashData=new HashMap<String, ICommand>();
public static void main(String[] args)
{
myhashData.put("ltf", new cITF());
myhashData.put("nbc", new cNBC());
myhashData.put("gdb", new cGDB());
myhashData.put("Tedit", new cTedit());
do
{
System.out.print(Currentpath+"> ");
String Input = null;
Scanner scan = new Scanner(System.in);
if(scan.hasNext()){
Input = scan.nextLine().trim();
}
//if(Input.equals("exit")){
// System.exit(0);
//}
if(myhashData.containsKey(Input))
{
ICommand myCommand=myhashData.get(Input);
myCommand.Execute();
}
else
{
System.out.println("Invalid Command");
}
}while(!"Input".equals("exit"));
}
}
And here is the class which provide the functionality for read and write.
import java.util.*;
import java.io.*;
//import java.lang.System.*;
public class cTedit implements ICommand{
#Override
public void Execute() {
// TODO Auto-generated method stub
System.out.println("Enter the file name to be edited");
Scanner scan = new Scanner(System.in);
String filename = scan.nextLine();
InputStreamReader cin = null;
FileWriter out = null;
try{
cin = new InputStreamReader(System.in);
out = new FileWriter(cShell.Currentpath+"\\"+filename);
System.out.println("Enter character, 'q' to quit");
char c;
do{
c = (char) cin.read();
out.write(c);
}while(c!= 'q');
}
catch(Exception e){
System.out.println("Error");
}
finally{
try{
cin.close();
out.close();
}
catch(IOException e)
{
System.out.println("File did not close");
}
}
}
}
The problem is that after the reading and writing, the program output the message "Invalid Command" which is defined inside the class cShell. Can anyone point to me where this the cause..??
The do-while loop will run forever becauses its termination condition is:
!"Input".equals("exit")
The string "Input" will never be equal to the string "exit". You may want to use the variable Input instead:
!input.equals("exit")
Note:
Try to follow Java naming conventions. Use 'mixedCase' for methods/variables and use 'CamelCase' for classes/interfaces. In other words, the variable name should be input, not Input.
Change
while(!"Input".equals("exit"));
to
while(!Input.equals("exit"));
As "Input" can never be equal to "exit" condition is always true and hence it loops infinitely.
For NPE you can add null checks
finally{
try{
if(cin != null)
cin.close();
if(out != null)
out.close();
}
catch(IOException e)
{
System.out.println("File did not close");
}
}

Error: java.util.NoSuchElementException - Scanner not behaving as desired

Scanner returning NoSuch Element Exception error. Could you explain why is this happening.
The Scanner now passes and runs fine but it didn't take the nextLine input from the second Scanner call. This may be a little tweak but could someone point out what the mistake is.
public class JavaHW1_1 {
private static Scanner userInput = new Scanner(System.in);
public static void main(String[] args) throws IOException {
String pattern ;
String fileName = null;
// Method to manage user inputs
fileName = userInputFileName(userInput);
pattern = userInputPattern(userInput);
// To find the pattern in the file
// findPattern();
}
private static String userInputPattern(Scanner userInput) {
String pattern = "JollyGood";
System.out.println(". Please enter a pattern to find in the file");
while(userInput.hasNextLine()){
pattern = userInput.nextLine();
System.out.println("The pattern to be searched: "+ pattern);
}
userInput.close();
return pattern;
}
private static String userInputFileName(Scanner userInput) throws IOException {
String path = "./src";
String files, fileName;
File folder = new File(path);
File[] listOfFiles = folder.listFiles();
System.out.println("Please input the desired file name:\n");
System.out.println("Some suggestions:\n");
for (int i = 0; i < listOfFiles.length; i++)
{
if (listOfFiles[i].isFile() && listOfFiles[i].getName().toLowerCase().endsWith(".txt"))
{
files = listOfFiles[i].getName();
System.out.println(files);
}
}
int userAttempt = 0;
do{
fileName = userInput.nextLine();
if(fileName.toLowerCase().endsWith(".txt")){
System.out.println("The file name entered is in correct format");
File file = new File("./src",fileName);
try {
file.createNewFile();
System.out.println("File is created. Please enter text to be written in the file. End the content with \"eof\"");
InputOutput(file.getName());
} catch (IOException e) {
e.printStackTrace();
}
userAttempt = 10;
}
else
{System.out.println("Please enter correct format file with .txt extension");
userAttempt++;}
}while (userAttempt <10);
return fileName;
}
private static void InputOutput(String fName) throws IOException {
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
BufferedWriter out = null;
try {
out = new BufferedWriter(new FileWriter("./src/" + fName));
String inputLine = null;
do {
inputLine=in.readLine();
out.write(inputLine);
out.newLine();
} while (!inputLine.equalsIgnoreCase("aaa"));
System.out.print("Write Successful");
} catch(IOException e1) {
System.out.println("Error during reading/writing");
} finally {
out.close();
in.close();
}
}
private static void findPattern() {
// TODO Auto-generated method stub
}
}
Based in this SO, you might be closing the Scanner and creating a new one to read from the System.in and it makes sense by looking at your code.
So my suggestion for you code is to receive the Scanner by parameter, something like this:
public static void main(String[] args){
Scanner scan = new Scanner (System.in);
String pattern = userInputPattern(scan);
String test = readSomethingElse(scan);
}
private static String readSomethingElse(Scanner scan) {
System.out.println(". Read something else");
return scan.nextLine();
}
private static String userInputPattern(Scanner scan) {
String pattern = "JollyGood";
System.out.println(". Please enter a pattern to find in the file");
pattern = scan.nextLine();
System.out.println("The pattern to be searched: "+ pattern);
return pattern;
}
It could happen if you pass EOF straight into the standard input. For example (in windows):
java com.myprog.MainClass
^Z
Exception in thread "main" java.util.NoSuchElementException: No line found
at java.util.Scanner.nextLine(Unknown Source)
....
The ^Z above represents a Ctrl-Z on windows command prompt which is an EOF signal
You need to consider your requirement and process / display error if user is provided a EOF without any prior data

How to pass a variable in java?

Consider this code:
private static void colourRead(String s) throws IOException {
FileReader readhandle = new FileReader("C:\\****\\****");
BufferedReader br = new BufferedReader(readhandle);
String line = null;
while ((line = br.readLine()) != null) {
ColourInput(); //there's an error here
}
br.close();
readhandle.close();
}
private static void ColourInput(String s) {
char letter;
String fullWord;
Scanner kb = new Scanner(System.in);
System.out.print("Enter whatever: ");
fullWord = kb.nextLine();
System.out.println(fullWord);
for (int i = 0; i < fullWord.length(); i++) {
letter = fullWord.charAt(i);
switch (Character.toUpperCase(letter)) {
case 'A': {
Blue();
}
break;
}
}
}
Is it possible for me to carry the
line
variable from the colourRead method, and somehow assign it to the
fullWord
variable in the ColourInput() method?
I'm trying to read a text file, and output certain colours associated to each letter. I don't want to create a new switch statement in the colourRead method because apparently, this is a bad programming practice.
Any help please?
If you're still unsure of what I'm asking I'll re-edit
EDIT: The problem is that after calling the ColourInput(line) method, the Scanner method comes in to work (original code). I don't want to remove my Scanner method, I want it to 'skip' the scanner method, and continue into the for loop and switch statements.
You're not passing the string to your call of ColourInput
Try
ColourInput(line);
It is also worth mentioning that your code that reads the file is not safe, you should try to read the file, catch the IOException and close the file in a finally clause, if your code crashes somewhere in the while loop your file might remain open
If I understand correctly you want to be able to repeat the functionality of the ColourInput method with the results of the the ColourRead method.
private static void colourRead() throws IOException
{
FileReader readhandle = new FileReader("C:\\****\\****");
BufferedReader br = new BufferedReader(readhandle);
String line = null;
while((line = br.readLine()) != null)
{
ColourText(line); //there's an error here
}
br.close();
readhandle.close();
}
private static void ColourInput()
{
String fullWord;
Scanner kb = new Scanner(System.in);
System.out.print("Enter whatever: ");
fullWord = kb.nextLine();
System.out.println(fullWord);
ColourText(fullWord);
}
private static void ColourText(String text)
{
char letter;
for (int i = 0; i < text.length(); i++)
{
letter = text.charAt(i);
switch(Character.toUpperCase(letter))
{
case 'A':
{
Blue();
}
break;
}
}
This would let you color the text whether it is read from the file or input from the keyboard(using the ColourText method to change the color). But as other people have mentioned you should add to the file reading code as well.
Edit: You could also remove the String s variables from the first two methods since they are not being used in the methods anywhere.

Categories

Resources