Why a Scanner cannot be reused? - java

I created a method called readFileAsScanner. It creates a file and a Scanner which attaches to the file. Then returns the Scanner.
Neverthelesss, I use it. The Scanner can be only used once. Why? Can I reset it by the reset() method of Scanner to make it reusable?
import java.io.*;
import java.util.*;
public class Lab10{
public static void main(String[] args)throws FileNotFoundException{
String[] words = readWords();
int i;
for(i=0;i<words.length;i++)
System.out.println(words[i]);
System.out.println(words.length);
}
public static String[] readWords()throws FileNotFoundException{
Scanner data = readFileAsScanner();
String[] words = new String[estimateWords(data)];
int i=0;
while(data.hasNext()){
System.out.println(data.next());
}
return words;
}
public static Scanner readFileAsScanner() throws FileNotFoundException{
Scanner input = new Scanner(System.in);
System.out.println("Input file name:");
//String fileName = input.next();
String fileName = "unsorted.txt";
Scanner data = new Scanner(new File(fileName));
return data;
}
public static int estimateWords(Scanner data){
int estimatedSize = 0;
while(data.hasNext()){
data.next();
estimatedSize++;
}
return estimatedSize;
}
}

You're consuming everything that you want to read in the estimateWords method call. By the time the scanner advances to the end of the file, you've read pretty much everything there is to read in the file, and the scanner's next call to hasNext() will return false.
You can fix this in one of two ways:
Open the file twice with a Scanner instance in each method call and pass in the filename, or
Open the file once, perform both a count and a reading of data in one method only.
I would opt for the latter, since it would be more straightforward and better practice (you typically don't see Scanner instances passed around).

Because the underlying stream is at end, and probably cannot be rewound or reset, and you can't attach a existing Scanner to another stream.

Related

Add method not working when inserting integers from a data file into an integer array

import java.util.*;
import java.io.*;
import java.util.Set;
public class tester
{
public static void main(String args[]) throws Exception
{
BufferedReader reader1 = new BufferedReader(new FileReader("numbers1.in"));
//this will create a buffered reader to read the file, read each line
//and count how many lines there are so I can easily create my array
int lines = 0;
while (reader1.readLine() != null)//reads each line
{
lines++;
}
reader1.close();
Scanner reader2 = new Scanner(new File("numbers1.in"));//new scanner to read the file
int numbers[] = new int[lines];//creates my array with correct array dimensions
while(reader2.hasNextLine())
{
int next = reader2.nextInt();
numbers.add(next);
}
}
}
I am a beginner at this, so excuse the messy code. I am trying to read integers from a data file which includes a list of integers, each separated by a new line. I have to add each of those into an integer array, and for some reason the .add method from java.util.Set is not working, giving me an error message that states the add method cannot be found.
I would appreciate any help, thank you!
In java array length is immutable. It doesn't have an add method.
Use a List
List<int> numbers = new ArrayList<>();
while(reader2.hasNextLine()) {
int next = reader2.nextInt();
numbers.add(next);
}
Or, if you need to use array only
int index = 0;
while(reader2.hasNextLine()) {
int next = reader2.nextInt();
numbers[index++] = next;
}

scanner.hasNextInt() returns unexpected value

I'm very new in Java, and for practicing I'm trying to make a program that generates new, random words using some values (mostly letters). The program is meant to take these values from a text file. The next step is to define inventories (Arrays) that contain each letter of its kind (first by defining a variable (int) for the length of each array, and then filling each array with its proper letters (String)). While checking my progress, I realize that my code isn't updating the inventories lengths (cInv and vInv).
This is the relevant part of the code:
static File language;
static Scanner scanFile;
static Scanner scanInput = new Scanner(System.in);
static int cInv;
static int vInv;
//Getters go here
public static void setLanguage(File language) {Problem.language = language;}
public static void setCInv(int CInv) {Problem.cInv = cInv;}
public static void setVInv(int VInv) {Problem.vInv = vInv;}
//Asks for the file with the language values.
public static void takeFile() throws FileNotFoundException {
String route = scanInput.nextLine();
setLanguage(new File(route));
BufferedReader br;
br = new BufferedReader(new FileReader(language));
}
//Gathers the language values from the file.
public static void readFile() throws FileNotFoundException {
takeFile();
scanFile = new Scanner(language);
//Defines the inventory sizes. It seems the failure is here.
if (scanFile.hasNextInt()) {setCInv(scanFile.nextInt());}
if (scanFile.hasNextInt()) {setVInv(scanFile.nextInt());}
}
public static void main(String[] args) throws FileNotFoundException {
readFile();
//The following line is for checking the progress of my coding.
System.out.println(cInv);
}
This is the relevant part of the text file that it reads (or should be reading):
---Phonemes---
Consonants: 43
Vowels: 9
And it's output is 0
I've tried typing 43 at the very beginning of the file, and I've also tried typing the number in the input, but I keep getting 0 nevertheless. Does someone know what I'm missing or doing wrong?
First, change your assignments as you are reassigning the same static variables.
public static void setCInv(int CInv) {Problem.cInv = CInv;}
public static void setVInv(int VInv) {Problem.vInv = CInv;}
Second, you need to move across all tokens in the file to identify the numbers and update the respective variable.
//Gathers the language values from the file.
public static void readFile() throws FileNotFoundException {
takeFile();
scanFile = new Scanner(language);
int num = 0;
scanFile.nextLine(); //Skip ---Phonemes---
setCInv(getInt(scanFile.nextLine()));
setVInv(getInt(scanFile.nextLine()));
}
public static int getInt(String str){
System.out.println(str);
int num =0;
Scanner line = new Scanner(str);
//Splits the scanned line into tokens (accessed via next()) and search for numbers.
//Similar thing could have been done using String.split(token);
while(line.hasNext()){
try{
num = Integer.parseInt(line.next());
return num;
}catch(NumberFormatException e){}
}
return num;
}

How to proceed with making an in - BlueJ Java file manager

I know that it is a bit of a noob program, but I'm slowly getting confused. The 'down' function is going to act like cd and the 'up' function would act like cd..
I have no clue how to allow the user to create a file or folder. I attempted using arrayLists instead of arrays but couldn't sort out the errors. Any help would be appreciated.
import java.util.Scanner;
class FileManager {
//array of arrays will go here
String Dir[] = {"UserOne"};
String SystemFolders [] = {"Documents","","",};
String SubFiles [] = {"","","","","",""};
String Nav [][] = { Dir, SystemFolders, SubFiles};
int levelCounter = 0;
public void main(String[]args) {
Scanner sc = new Scanner (System.in);
System.out.println("Enter a command");
String command = sc.next();
if (command.compareTo("down") == 0)
down();
//else if is on the way
}
void down () {
//This will execute when the command is 'down'
System.out.println(Nav[++levelCounter]);
}
void up () {
//This will execute when the command is 'up'. It acts like cd..
System.out.println(Nav[--levelCounter]);
}
}
If this is the entry point of your program then you need to declare your main method as static, like so
public static void main(String[] args)
Then to access the methods in your FileManager class in your main method you need to create an instance of your class in your main method. Like this
public static void main(String[]args) {
FileManager fm = new FileManager(); // Creates an instance
Scanner sc = new Scanner (System.in);
System.out.println("Enter a command");
String command = sc.next();
if (command.equals("down")) // equals will suffice in this case
// or equalsIgnoreCase() if you dont want case to be a problem
fm.down(); // Notice now this calls the down method from the instance
//else if is on the way
}
Then look at this to example to create files or this to create folders

Why does this throw a InputMismatchException?

public class Test
{
public static void main(String[] args) throws FileNotFoundException
{
Scanner input = new Scanner("text.txt");
int x = input.nextInt();
}
}
text.txt being:
8
8
6
7
This code throws a InputMismatch Exception. Why?
That is because "text.txt" is not a number. Try:
Scanner input = new Scanner(new File("text.txt"));
The constructor Scanner(String) accepts a String to read from, not a file name.
So, nextInt() is trying to read an int from the String you pass to it, ie. "text.txt".
Instead, use the constructor that accepts a File source, Scanner(File).
The problem is due to overloading. You are calling new Scanner(String) instead of new Scanner(File). If you tried scanner.next() you would see it returns "text.txt"

JUnit test for console input and output

I have only one method main. How to check System.out.println() and replace Scanner to input values automatically using JUnit?
P.S. Please, provide some solutions...
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int[] arr = new int[4];
for (int i = 0; i < arr.length; i++) {
arr[i] = scanner.nextInt();
}
for (int i = 0; i < arr.length; i++) {
int res = 0;
int k = 0;
int num = arr[i];
/*.....*/
System.out.println(num);
}
}
Ideally, extract the awkward dependencies so that you can test without them. Change main to simply:
public static void main(String[] args) {
doWork(new Scanner(System.in), System.out);
}
// TODO: Rename to something meaningful
public static void doWork(Scanner input, PrintStream output) {
// Remainder of code
}
(Consider using a Writer instead of a PrintStream for output.)
Then you don't really need to unit test main - but you can test doWork using a Scanner based on a StringReader, and output based on a StringWriter, providing whatever input you want and checking the output.
I faced a similar issue and this is what I ended up doing.
First off, I'd suggest doing as #Jon-Skeet suggests and instead of using the main(String[]) method of the class, create a separate method.
Then you can have that method take in an InputStream as a parameter and then create a Scanner object within the method that uses the passed InputStream as its source. That way you can pass any InputStream, such as System.in, to the method when it's called (elaboration below).
package my.package;
import ...;
public class MyClass
{
public static void myMethod(InputStream inputStream)
{
Scanner inputScanner = new Scanner(inputStream);
// Do stuff with the Scanner such as...
String input = inputScanner.nextLine();
System.out.println("You inputted " + input);
}
}
Now, in your production source code you can call myMethod and pass it System.in as an argument as such, myMethod(System.in);
And then in your unit tests, you can create mock input values via a ByteArrayInputStream:
package my.package;
import ...;
public class MyClassTest
{
#Test
void testMyMethod()
{
// Simulates a user inputting the string "Mock input" and hitting enter
assertDoesNotThrow(myMethod(new ByteArrayInputStream("Mock input\n".getBytes())));
}
}
And voila, you now have a way to pass your method mock input as well as it being more modular overall.
I just want to point out without getting too much into it, that when working with System.in, one needs to be careful about closing it and in unit tests when working with input streams, one needs to be careful about reusing a reference to the same InputStream as its state can persist across uses.

Categories