I have a file that contains lots of movie titles along with their years, rating out of 5 stars, and length. I'm attempting to read the file and store the titles, year, rating, and length as variables. The problem I've encountered is in my code for retrieving the year. The code compiles, but then upon running throws a NumberFormatException when it gets to a movie with multiple years (for example, it is listed as 2006-2009). Here is my code.
while((line = bufferedReader.readLine()) != null) {
//System.out.println(line);
for(int i = 0; i < line.length(); i++)
{
if(line.charAt(i) == ')' || line.charAt(i) == '-')//get year
{
yr = line.substring(yearStart,i);
year = Integer.parseInt(yr);
}
}
System.out.println(year);
}
Shouldn't line.charAt(i) == '-' in my if statement take care of this issue?
EDIT: The below code is where yearStart comes from.
if(line.charAt(i) == '(')//get title
{
title = line.substring(0,i);
yearStart = i+1;
}
The file is formatted like this:
title (year) | rating, length
Sorry, I should have included that originally.
EDIT #2: Here's a sample of part of the file, if that helps
!Women Art Revolution (2010) | 3 stars, 1hr 22m
#1 Cheerleader Camp (2010) | 3 stars, 1hr 35m
$5 a Day (2008) | 3.4 stars, 1hr 37m
'night, Mother (1986) | 3.7 stars, 1hr 36m
'Til Death (2006-2009) | 3.7 stars, 4 Seasons//This is the one that causes the error
#Suicide Room (2011) | 3.4 stars, 1hr 51m
... And God Spoke (1993) | 2.8 stars, 1hr 22m
What happens once you have found the year? The loop will still run until the end of the line, trying to parse things as numbers every time it finds a ) or a -.
Maybe you should exit the loop once you have found the year.
if(line.charAt(i) == ')' || line.charAt(i) == '-')//get year
{
yr = line.substring(yearStart,i);
year = Integer.parseInt(yr);
break; // year found, no point in looping more
}
Or maybe you should reset the yearStart index so that the second year can be parsed.
if (line.charAt(i) == '-')
{
// parse first year
yr = line.substring(yearStart,i);
year = Integer.parseInt(yr);
yearStart = i + 1; // reset to parse second year
}
else if (line.charAt(i) == ')')
{
yr = line.substring(yearStart,i);
year = Integer.parseInt(yr);
break; // year found
}
Put Intger.parseInt in a try/catch block (similar to the one below) to see where this is failing and how:
try {
year = Integer.parseInt(yr);
} catch (Exception e) {
throw new RuntimeException(
"Failed to parse year '" + yr + "' in line '" + line + "'", e);
}
Btw: line.indexOf(')') returns the position of '(' in line.
After the first year is found, yearStart is still at the index of the first year. So when you try and get a subString, it is still (2006-2009) - Automatic NumberFormatException, can't parse the -.
You should change the yearStart after the first year is found:
for(int i = 0; i < line.length(); i++)
{
if(line.charAt(i) == ')' || line.charAt(i) == '-')//get year
{
yr = line.substring(yearStart,i);
year = Integer.parseInt(yr);
yearStart = i + 1;
}
}
The condition if(line.charAt(i) == ')' || line.charAt(i) == '-') holds true twice in your loop. First, when the hyphen matches, and then, when the closing parenthesis matches. The first time, year is assigned to, in your example, 2009. But then, the loop keeps running, and a few iterations later, you try to parse "2005-2009" which results in the NumberFormatException.
The most straightforward solution is to break; the loop just after assigning year, so that year is only assigned once. That said, supposing that you want to get the starting value of the interval.
A cleaner solution would be to avoid the loop at all, and instead judiciously using String.indexOf. Something like that (disclaimer: untested).
String title;
int year;
while((line = bufferedReader.readLine()) != null) {
title = line.substring(0, line.indexOf('('));
int yearStart = line.indexOf('(') + 1;
int yearEnd;
if (line.indexOf('-', yearStart) > 0 ) {
yearEnd = line.indexOf('-', yearStart);
} else {
yearEnd = line.indexOf('(', yearEnd);
}
int yr = line.substring(yearStart,i);
year = Integer.parseInt(yr);
System.out.println(year);
}
Finally, a more professional solution would use regular expresions, I understand you might not be familiar with that, and thus I have tried to provide an answer that follows a similar structure as yours.
Related
This question already has an answer here:
Pressing Enter Triggers System.in.read() twice?
(1 answer)
Closed 4 years ago.
I get this snippets from a guide but the guide doesn't explain why. For my understanding, the for loop will evaluate whether i equals to 'S', if not equals to S then execute the code inside the loop and i++ and read in a new buffer to compare with 'S' again. But each time the code inside the loop is executed two time but I not sure why, could anyone explain to me?
Code snippets from guide: https://i.imgur.com/b1oOw1S.png
class Example {
public static void main(String args[])
throws java.io.IOException{
int i;
System.out.println("Press S to stop.");
for(i = 0;(char)System.in.read() != 'S';i++)
{
//The line below is executed twice
System.out.println("Pass # " + i);
}
}
}
Problem solved! https://i.imgur.com/iVyJe0n.png
I am not a Java programmer, but I bet your loop block executes twice every time something other than 'S' is entered. If that's the case, it's possible that System.in.read() returns the user's letter first, then immediately a:
13 byte (\n new line, Enter) or
10 byte (\r carriage return) or
0 byte (\0 end of file, end of string).
To debug, use this:
int b; // byte number as int, can be converter to char
for(i = 0; (char)(b = (int)System.in.read()) != 'S'; ++i)
{
System.out.println("DEBUG: byte = " + b.ToString());
System.out.println("Pass # " + i);
}
Sorry if this doesn't compile, I come from C# planet, please correct this code as needed.
Should I be right, you must print only when the character is not \n nor \r nor \0 (or, when cast to int, not 13 nor 10 nor 0):
for(int i = 0 ;;)
{
int byteNo = System.in.read();
if(byteNo == 10 || byteNo == 13)
continue;
char letter = (char)byteNo;
if(letter == 's' || letter == 'S')
break;
System.out.println("Pass # " + ++i);
}
or more concise:
for(int i = 0, b ; (b = System.in.read()) != (int)'S' ;)
{
if(b == 10 || b == 13) continue;
System.out.println("Pass # " + ++i);
}
I am trying to write a program that takes input from a user. The enter a number 1-12 and it returns the month January-December. I have to have all the months in one long string and then use a substring to return the corresponding month.
I am very confused as to how to get an int to correlate to a substring. I would appreciate some general guidelines for doing this. I'm not looking to have the whole program done for me.
Don't use substring(). If you have a csv of month names, use split() to turn the string into an array:
String months = "January,February,etc";
int choice; // 1-12
String monthName = months.split(",")[choice - 1];
Note that java arrays are zero-based, so you must subtract 1 from a 1-12 ranged number when used as an index.
Easier to read would be:
static String[] monthNames = "January,February,etc".split("");
then in your method:
String monthName = monthNames[choice - 1];
When you get the values of the month using your substring, store it in your array of String. And then get the 1 - 12 value by their indexes + 1.
Beside using Split you can use StringTokenizer to parse your string as well.
My Code:
int i = 1;
int month = 0;
while (i == 1) {
System.out.println("Enter your number ");
Scanner input = new Scanner(System.in);
month = input.nextInt();
if (month > 13 || month < 0) {
System.out.println("your number has to be between 1 and 12");
} else {
i = 2;
}
}
List<String> monthList = new ArrayList<>();
StringTokenizer st = new StringTokenizer("January February March April"
+ " May June July August September October November December");
while (st.hasMoreTokens()) {
monthList.add(st.nextToken(" "));
}
System.out.println("the month is " + monthList.get(month - 1));
My Output:
Enter your number
333
your number has to be between 1 and 12
Enter your number
3
the month is March
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 9 years ago.
Improve this question
I'm making a thingy that takes 3 inputs like "1500, 1 and 1" or "1500, January and 1" and returns "January 1st, 1500" or "1/1/1500", i had some problems on the day part but someone already told me how to fix it, and now I'm having problems with the month part, i made this kinda fast and i haven't figured out why it isn't working, it SHOULD see if the input is a valid month, if it is then it outputs the month(this part is just for testing), and if it isn't then it should say "please use a valid month or a number between 1 and 12", but when i write anything that isn't a month it just stops, and doesn't output anything, even if i put a month after it just doesn't do anything, i tried to see if there was any errors but i didn't found any, this is the code that i used:
Scanner scan = new Scanner(System.in);
String mx;
System.out.println("Insert Month");
String[] mm = {"january","february","march","april","may","june","july","august","september","october","november","december"};
int mz = 0;
while (0 < 1){
mx = scan.nextLine();
mx = mx.toLowerCase();
for(int i = 0; i < 11; i++){
if (mx.equals(mm[i])){
mz = i + 1;
break;
}
else {
if(i == 11){
System.out.println("please use a valid month or a number between 1 and 12");
}
else{
}
}
}
//}
if(mz > 0){
break;
}
else {}
}
System.out.println(mx);
The reason your program just "stops" is that you only print the statement "please enter a valid month..." if i == 11 and you have your for loop break if i >= 11. Thus, this condition will never be met. The while loop keeps running, even though this statement isnt printed. You could have entered a non-month string on the first try, and then a month string on the second and your while loop would have been broken.
Here is how I have improved your code to work for taking in the month. Pay attention to subtle changes of highlighted. These are important for writing better, more readable code:
Scanner scan = new Scanner(System.in);
//initialize to empty string
String mx = "";
System.out.println("Insert Month");
//use good naming conventions for easier code readability
String[] validMonths = {"january","february","march","april","may","june","july","august","september","october","november","december"};
//using a boolean to break makes much more sense than the way you have written it with an infinite loop and a manual break statement
boolean noMonth = true;
while (noMonth){
mx = scan.nextLine();
for(int i = 0; i < 12; i++){
//rather than convert to lowercase, use this handy String method
//also compares for valid number entries
if (mx.equalsIgnoreCase(validMonths[i]) || mx.equals(Integer.toString(i+1))){
noMonth = false;
break;
}
}
if(noMonth){
System.out.println("please use a valid month or a number between 1 and 12");
}
}
System.out.println(mx);
Create a new while loop to take in the day and a new one to take in the year after these, checking for valid input. Also, every if does not require an else in Java.
You're not using meaningful variable names, making your code a little bit hard to read and maintain. So, I had to create you the following code from scratch:
public static void main(String[] args)
{
Scanner keyboard = new Scanner(System.in);
String month = getMonthName(getInt("Enter Month: ", keyboard) - 1);
int day = getInt("Enter Day: ", keyboard);
int year = getInt("Enter Year: ", keyboard);
System.out.printf("%s %d, %d\n", month, day, year);
}
public static String getMonthName(final int monthNo)
{
String[] months = {"january","february","march","april","may","june","july","august","september","october","november","december"};
return months[monthNo];
}
public static int getInt(final String msg, Scanner keyboard)
{
System.out.print(msg);
return keyboard.nextInt();
}
The code above does NOT perform and input validation, as you may have noticed already. If you want to validate month input for example, your if condition may look something like this:
if (month < 0 || month < 12)
{
System.out.println("Invalid month number entered");
System.exit(0);
}
I'm having a little trouble grasping the difference between ! || and && when they are tested in a while condition. In the example below I want the program to ask a question "do you see a four on the screen?" then if the person answers no the program continues and keeps asking. If the user enters the answer "yes" the program exits but mine does not.
In my while loop condition am I telling the while loop to continue only if both i is less than 5 and the answer to the question is not yes? How is the correct way of thinking about ! || and && when used inside the context of a while loop?
import acm.program.*;
public class WhileConditionTestProgram extends ConsoleProgram{
public void run(){
String question = ("do you see a four on the screen? ");
int i = 1;
while(i <= 20 && !(question.equals("yes"))){
String question = readLine("do you see a 4 on the screen?: ");
i++;
}
}
}
Apart from the obvious issue of variable re-declaration, you should also consider using a do-while loop, since you are reading the user input at least once.
So, you can better change your loop to:
int i = 0;
String answer = "";
do {
answer = readLine("do you see a 4 on the screen?: ");
i++;
} while (i <= 20 && !answer.equalsIgnoreCase("yes"));
Note: I have used equalsIgnoreCase just for safer side, since you are reading input from user. You never know what combination of letters it passes.
In your while condition you are testing answer not question try that:
while(i <= 20 && !(answer.equals("yes"))){
answer = readLine("do you see a 4 on the screen?: ");
i++;
}
The problem with this code:
String question = ("do you see a four on the screen? ");
int i = 1;
while(i <= 20 && !(question.equals("yes"))){
String question = readLine("do you see a 4 on the screen?: ");
i++;
}
Is that you're redefining the question variable inside the while function. As an example, this will print "1", and not "2":
String question = "1";
int i = 1;
while (i <= 20) {
String question = "2";
i++;
}
System.out.println("Question is: " + question); // This will print "1"!
When you say String question = "2" you are declaring a brand new variable called question and setting it to "2". When you get to the end of the while loop, that variable goes out of scope and the program throws its data away. The original question is untouched. Here is a corrected version of that code snippet:
String question = ("do you see a four on the screen?");
int i = 1;
while(i <= 20 && !(question.equals("yes"))){
question = readLine("do you see a 4 on the screen?: ");
i++;
}
These operators work in a while loop the same way as they work everywhere else.
The && and || operators perform Conditional-AND and Conditional-OR operations on two boolean expressions.
Try this:
String answer = "";
int i = 1;
while(i <= 20 && !(answer.equalsIgnoreCase("yes"))){
answer = readLine("do you see a 4 on the screen?: ");
i++;
}
I have a java programming assignment where you have to input a date on a single line and it gives you a numerology (horoscope-like) report based on the date. It is assumed that the user will enter a formatted date, separated with spaces.
I can retrieve the month, day, and year of the input by using in.nextInt(). However, I also have to check that the user used a correct separating character for each part of the date, which means I just have to check whether the user used forward slashes.
When looking at my code below, I currently use charAt() to find the separating characters. The problem is that the date won't always be 14 characters long. So a date in the form of 10 / 17 / 2004 is 14 characters long, but a date of 4 / 7 / 1992 is only 12 characters long, meaning that "slash1" won't always be in.charAt(3), in the latter situation it would be in.charAt(2).
Does java have a method that allows something like in.nextChar()? I know that it doesn't, but how could I just find a next character in the date?
EDIT: I forgot to reflect this originally, but my professor said that we are NOT allowed to use the String.split() method, for some reason. The thing is, I get the month, day, and year perfectly fine. I just need to check that the person used a forward slash to separate the date. If a dash is entered, the date is invalid.
public void getDate()
{
char slash1, slash2;
do
{
System.out.print("Please enter your birth date (mm / dd / yyyy): ");
Scanner in = new Scanner(System.in);
String date = in.nextLine();
month = in.nextInt();
day = in.nextInt();
year = in.nextInt();
slash1 = date.charAt(3);
slash2 = date.charAt(8);
} while (validDate(slash1, slash2) == false);
calcNum();
}
you could consider to split the input date string with " / ", then you get a String array. the next step is converting each string in that array to int.
I would use Scanner just to get a line. Then split() the line on whitespace and check the fields:
import java.util.Scanner;
import java.util.regex.Pattern;
public class GetDate {
int month, day, year;
public static void main(String[] args)
{
GetDate theApp = new GetDate();
theApp.getDate();
}
public void getDate()
{
String date;
do
{
System.out.print("Please enter your birth date (mm / dd / yyyy): ");
Scanner in = new Scanner(System.in);
date = in.nextLine();
} while (validDate(date) == false);
calcNum();
}
boolean validDate(String date)
{
// split the string based on white space
String [] fields = date.split("\\s");
// must have five fields
if ( fields.length != 5 )
{
return false;
}
// must have '/' separators
if ( ! ( fields[1].equals("/") && fields[3].equals("/") ) )
return false;
// must have integer strings
if ( ! ( Pattern.matches("^\\d*$", fields[0]) &&
Pattern.matches("^\\d*$", fields[2]) &&
Pattern.matches("^\\d*$", fields[4]) ) )
return false;
// data was good, convert strings to integer
// should also check for integer within range at this point
month = Integer.parseInt(fields[0]);
day = Integer.parseInt(fields[2]);
year = Integer.parseInt(fields[4]);
return true;
}
void calcNum() {}
}
Rather than thinking about what characters are used as separators, focus on the content you want, which is digits.
This code splits on non digits, do it doesn't matter how many digits are in each group or what characters are used as separators:
String[] parts = input.split("\\D+");
It's also hardly any code, so there's much less chance for a bug.
Now that you have the numerical parts in the String[], you can get on with your calculations.
Here's some code you could use following the above split:
if (parts.length != 3) {
// bad input
}
// assuming date entered in standard format of dd/mm/yyyy
// and not in retarded American format, but it's up to you
int day = Integer.parseInt(parts[0];
int month = Integer.parseInt(parts[1];
int year = Integer.parseInt(parts[2];
Look ahead in the stream to make sure it contains what you expect.
private static final Pattern SLASH = Pattern.compile("\\s*/\\s*");
static SomeTypeYouMadeToHoldCalendarDate getDate() {
while (true) { /* Might want to give user a way to quit. */
String line =
System.console().readLine("Please enter your birth date (mm / dd / yyyy): ");
Scanner in = new Scanner(line);
if (!in.hasNextInt())
continue;
int month = in.nextInt();
if (!in.hasNext(SLASH)
continue;
in.next(SLASH);
...
if (!validDate(month, day, year))
continue;
return new SomeTypeYouMadeToHoldCalendarDate(month, day, year);
}
}
This uses Scanner methods to parse:
import java.util.Scanner;
import java.util.InputMismatchException;
public class TestScanner {
int month, day, year;
public static void main(String[] args)
{
TestScanner theApp = new TestScanner();
theApp.getDate();
theApp.calcNum();
}
public void getDate()
{
int fields = 0;
String delim1 = "";
String delim2 = "";
Scanner in = new Scanner(System.in);
do
{
fields = 0;
System.out.print("Please enter your birth date (mm / dd / yyyy): ");
while ( fields < 5 && in.hasNext() )
{
try {
fields++;
switch (fields)
{
case 1:
month = in.nextInt();
break;
case 3:
day = in.nextInt();
break;
case 5:
year = in.nextInt();
break;
case 2:
delim1 = in.next();
break;
case 4:
delim2 = in.next();
break;
}
}
catch (InputMismatchException e)
{
System.out.println("ERROR: Field " + fields + " must be an integer");
String temp = in.nextLine();
fields = 6;
break;
}
}
} while ( fields != 5 || validDate(delim1, delim2) == false);
in.close();
System.out.println("Input date: " + month + "/" + day + "/" + year);
}
boolean validDate(String delim1, String delim2)
{
if ( ( ! delim1.equals("/") ) || ( ! delim2.equals("/") ) )
{
System.out.println("ERROR: use '/' as the date delimiter");
return false;
}
if ( month < 1 || month > 12 )
{
System.out.println("Invalid month value: " + month);
return false;
}
if ( day < 1 || day > 31 )
{
System.out.println("Invalid day value: " + day);
return false;
}
if ( year < 1 || year > 3000 )
{
System.out.println("Invalid year: " + year);
return false;
}
return true;
}
void calcNum()
{
}
}