How can I update contents of a text file - java

My assignment requires me to make a simple mathGame that generates random math problems. The program has to record the amount correct and the amount incorrect in a text file. It also has to update the statistics of an existing file instead of overwrite them.
This is how I am creating each file:
try {
writer = new FileWriter(userName + " Stats.txt", true);
outputfile = new PrintWriter (writer);
} catch (IOException e) {
}
Here is what is being written to the file:
public static void saveStats () {
outputfile.println();
outputfile.println("Correct Answers:"+ correct);
outputfile.println("Incorrect Answers:" + incorrect);
if (money > 0) {
outputfile.printf("Earnings: $%.2f", money);
outputfile.println();
}
else {
float moneyNegative = Math.abs(money);
outputfile.printf("Earnings: -$%.2f", moneyNegative);
outputfile.println();
}
outputfile.flush();
}
Here is a sample output of the text file after quitting the program:
Correct Answers:0
Incorrect Answers:1
Earnings: -$0.03
correct, incorrect, and money are all global variables and are initialized to 0. If I restart the program, my file will still exist but the values of Correct Answers, Incorrect Answers, and Earnings will be overwritten or a new entry to the file will be added. I just want to update it.
Here is all of my code: https://pastebin.com/1Cmg5Rt8

Have you tried getting the original text first, then writing it before you start writing what you need to?
Basically, you take the input from the file you have at the beginning
Scanner s = new Scanner(new File("FileName.txt"));
Then, you can loop through it and write to the file.
while(s.hasNextLine())
{
outputfile.println(s.nextLine());
}
After you have all of your previous file rewritten into the text, you can run the rest of your code and not have the information overwritten.
Also, instead of try-catch you can just throw IOException

Related

Loading data from an RPG game

So I'm working on a simple RPG game. I want to add an option to load progress through the game, but I guess I'm not that familiar with Java to do it without issues. The idea is to save the game on checkpoints, and load a saved data when the game continues. I'm getting an error with a loadGame method, and I guess it's related to the fact that the data I'm trying to load are all different data types. health and level are integers, while equippedItem is a string.
Here are just two methods, saveGame, and loadGame, because the code is a bit too long to simply paste it in the whole. I'll put additional code if needed of course.
public static void saveGame() {
try {
BufferedWriter bw = new BufferedWriter(new FileWriter("savedGame.txt"));
bw.write("Your health is " + player.getHealth());
bw.newLine();
bw.write("Your level is " + player.getLevel());
bw.newLine();
bw.write("" + player.equippedItem);
bw.close();
}
catch (Exception e) {
System.out.println("There's an error.");
}
}
The saveGame() doing its work well, it stores the data in the file. The issue is with the loadGame() method.
public void loadGame() {
try {
BufferedReader br = new BufferedReader(new FileReader("savedGame.txt"));
player.getHealth();
player.getLevel();
player.equippedItem;
br.close();
} catch (Exception e) {
System.out.println("There's an error.");
}
}
The result of player.getHealth() and player.getLevel() is ignored, and it's probably the data type issue I've mentioned.
What you're doing is creating a text file in saveGame(), 'opening' that save file in loadGame() but not accessing the data inside.
You have correctly created a BufferedReader, but you're using it incorrectly. You will need to use br.readLine() to read the text file which will return a string like "Your health is 123".
To update the state of the player with this data, you could add something like this inside loadGame():
player.setHp(parseHp(br.readLine()));
player.setLevel(parseLevel(br.readLine()));
... // and so on
The parse methods would take a String input like "Your health is 123" and return int 123.

Saving data to a file doesn't behave as expected

I have a simple project where I created a Store with customers, products and employees. Each is represented by a Class of course and I also have a CSV file for each one of them to be able to load data from and save data to it.
I'm facing issues where the file reading/writing is working, but not really. For example, I have the ability to save each file individually so if for instance I want to create a new customer, I'd save it to the list and then to the file. Issue is, once I do it for another Class (i.e if I create a new employee) and then save it again, the customer file object I saw in the CSV earlier is deleted. BUT, once I add a new object again, that same object reappears again. Hope you can somehow understand, but here is a more detailed view:
customer.csv is empty:
Me creating a new customer:
Created and saved to CSV:
Now, if I go to the other menu, and click on "Save all data" that jon snow customer object will be gone. Then if I create a new customer, then it will be added to the CSV file, along with the jon snow I added earlier. So why is it gone in the first place?
So here is the whole file reader/writer code I'm using:
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileWriter;
import java.util.ArrayList;
import java.io.IOException;
import java.util.List;
import java.util.Scanner;
class CSV {
static void CreateFile(String filename) { //Create new file
try {
File fileToCreate = new File(filename);
if (fileToCreate.createNewFile()) {
System.out.println("File created sucessfully: " + fileToCreate.getName());
}
} catch (IOException e) {
System.out.println("Cannot create file!");
}
}
static void ReadFile(String path_and_filename){
try {
File fileToRead = new File(path_and_filename);
Scanner myReader = new Scanner(fileToRead);
System.out.println("Reading file "+path_and_filename+" :");
while (myReader.hasNextLine()) {
String data = myReader.nextLine();
System.out.println(data);
}
myReader.close();
System.out.println();
} catch (FileNotFoundException e) {
System.out.println("There is no such file "+"\"path_and_filename\""+".\n");
}
}
// The StringBuilder in Java represents a mutable sequence of characters.
// Java's built in String class is not mutable.
static void saveArrayListToFile(List<Output> listToSave, String fileName, String sep) throws Exception {
StringBuilder ans = new StringBuilder();
for (Output record : listToSave) {
ans.append(record.createOutput());
ans.append(sep);
}
saveStringToFile(ans.toString(), fileName);
System.out.println("\nData saved to "+ fileName);
}
static void saveArrayListToFile1(ArrayList<String> listToSave, String fileName, String sep){
StringBuilder ans = new StringBuilder();
for (Object record : listToSave) {
ans.append(record.toString());
ans.append(sep);
}
saveStringToFile(ans.toString(), fileName);
System.out.println("\nList was saved to file "+fileName+"\n");
}
static void saveStringToFile(String data, String fileName){
BufferedWriter bufferedWriter=null;
try {
bufferedWriter = new BufferedWriter(
new FileWriter(fileName,false));
bufferedWriter.write(data);
} catch (IOException e) {
System.out.println("Cannot write to file");
} finally {
try {
bufferedWriter.close();
} catch (IOException e) {
System.out.println("Cannot write to file");
}
}
}
}
When I'm creating a new customer, I call it from a menu and it looks like this:
switch (selection) {
case 1:
try {
System.out.println("You're registering as a new customer");
String custID = ObjectIDs.generateID();
System.out.println("Enter first name:");
String firstName = sc.next();
System.out.println("Enter last name:");
String lastName = sc.next();
st.newCustomer(custID, firstName, lastName);
st.saveCustomersList();
} catch (Exception e) {
e.printStackTrace();
}
break;
the saveCustomerList() function is this:
#SuppressWarnings("unchecked")
void saveCustomersList() throws Exception {
CSV.saveArrayListToFile((List<Output>)(List<?>) customers, CUSTOMERS_FILE_PATH,"\n");
}
And then the functions calls saveArrayListToFile() to save it.
The behavior is the same with Product and Employee projects, so I randomly chose to show how it acts when creating a new Product.
I hope I added enough information. If needed, I can paste more code in but I already feel it's very cluttered. Hopefully it's ok.
Thank you very much :)
At the moment it's hard to say, as one can only hypothesise as to what happens when you click on "Save all data". There are some weird things (what is saveArrayListToFile and saveArrayListToFile11? Why does one declare an exception? When are these called?).
Having said that, look at the actual file writing method saveStringToFile, it says:
bufferedWriter = new BufferedWriter(new FileWriter(fileName,false));
This false there means 'do not append to file, rewrite it from scratch'. So each time you call it, file contents are discarded and replaced from what you provide to the method call. So my somewhat educated guess would be:
You save customer one to file (gets cleared, customer 1 written) and
append the customer to a list of customers (that's my guess)
You
save customer two to file (file gets cleared, so only customer 2 is
saved), you add to list to customers (do you?)
Then you choose 'save all' which gets list of customers, and save them in one go, a single call to the method. The file is cleared, all customers are saved.
But it's all guessing. Try creating a minimal, reproducible example
In addition to pafau k. I would like to add some things at least I would do differently...
First of all:
Things that can cause errors or unexpected behaviour:
Everything below is in saveStringToFile
Like already pointed out the Initialisation of the BufferedWriter: It should be initialized like this:
bufferedWriter = new BufferedWriter(new FileWriter(filename, true));
This puts the File into appending mode (if you want to append to a file you can also get rid of the boolean (second argument) entirely because appending is standard: new FileWriter(filename))
If for some case the Creation of the BufferedWriter failed you will still have a null-pointing object as bufferedWriter. This however means that you will be surprised with a NullPointerException in your finally block. To prevent this first of all do a check in your finally block:
if (bufferedWriter != null) {
// Close your bufferedWriter in here
}
Also, if you run into an error you will likely be presented with the same error message twice.
Now cosmetics:
Things that I would write differently for aesthetic reasons:
Java methods (and static "methods") are always starting with a small letter :)
This means it should be public static void createFile() for example or static void readFile()
variables and parameters of methods do not contain seperators like _ but instead if you want to make it more readable you start with a small letter and for each seperation you use a capital letter for that: e.g. String thisIsAVeryLongVariableWithALotOfSeperations = "Foo";
The generic types in saveArrayListToFile1() work like a placeholder. So you declare ArrayList<String> listToSave so you don't need a cast in the following for-loop: You can simply write:
for (String record : listToSave) {
ans.append(record);
ans.append(sep);
}
I hope this fixes all errors or complications. :)

Append to a certain part of a text file in Java

I have an assignment where I have created a program to sell and order electronic devices and update two text files whenever a new sale/order has been made.
I found a way to update the text file instead of overwriting it so any old orders/sales are not lost and the new ones are added to the end of the file, but my assignment requires me to have the text file in the following form:
SALES
{
SALE
{
(Sale info here)
}
SALE
{
(Another sale info here)
}
}
The SALES { } needs to appear once in the whole file, and I need to update the file with each SALE { }. Can I make it so that
the writer writes only after SALES } (therefore in the 3rd line) and before } (so in the second to last line), even after restarting the application?
This is part of the code of my writer:
File file1= null;
BufferedWriter writer=null;
try {
file1=new File(path);
}
catch (NullPointerException e) {
System.err.println ("Not Found.");
}
try {
writer=new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file1, true)));
}
catch (FileNotFoundException e) {
System.err.println("Error opening file for writing.");
}
try
{
writer.write("SALES " + "\n" + "{");
//Writer writes sale info here
writer.write("\n" + "}");
}
catch (IOException e) {
System.err.println("Write error!");
}
Basically as of now, it creates SALES{ } every time I run the program, which is something I don't want.
Another way I thought of doing this is basically start the file with the following:
SALES
{
}
and just overwrite the last line with every new order, and at the end of each execution I will add another } in the end which will close the upper SALES {. But I also do not know how to do that.
Sorry if this sounds very amateurish. Thank you for any answers beforehand.
One way you can give a try is by checking whether "SALES
{" string is present in your file. If present you may directly write sales info else write the entire file.
You can include following snippet in your code to scan the file line by line as follows:
Scanner scanner = new Scanner(file1);
while(scanner.hasNextLine()){
if("SALES{".equals(scanner.nextLine().trim())){
//Writer writes sale info here
break;
}else{
writer.write("SALES " + "\n" + "{");
//Writer writes sale info here
writer.write("\n" + "}");
}
}
First of all, use this as a line separator:
String lineSeparator = System.getProperty("line.separator");
Why? diferent systems use diferent ways to separate the lines ( \n < linux, \r\n < windows, \r < mac).
In your code you will change de +"\n"+ to + lineSeparator + .
The best way to write this is to use a collection (array) of Sale Objects and then you will interate through this collection, like:
for(Sale sale : sales){
sale.getters // Infos
//write +\t+ (tab) and save infos
}
and then finish with "+}+"
For me its better to always create a new file in this case.

How to solve my try-catch exception handling?

I'm having problems with my try-catch exception here. Actually what it does is to prompt the user for the name of a text file say, Robot.txt but if say the file does not exist, I have to make sure that the application reprompts the user for the file name. Hope you guys can understand I'm still a newbie here so please feel free to provide suggestions or advices on my coding etc. Cheers!
Main method class:
import java.io.*;
import java.util.Scanner;
import java.util.Vector;
class TestVector3 {
public static void main(String [] args)
{
System.out.println("Please enter the name of the text file to read: ");
Scanner userInput = new Scanner(System.in);
Vector <KillerRobot> robotDetails = new Vector <KillerRobot>();
KillerRobot robot;
Scanner fileInput = null;
try
{
File textFile = new File(userInput.nextLine());
fileInput = new Scanner(textFile);
}
catch (FileNotFoundException e)
{
System.out.println("Error - file not found!");
System.out.println("Re-enter file name :"); //Reprompt user for name of the text file
fileInput = new Scanner(userInput.nextLine());
}
while(fileInput.hasNext())
{
robot = new KillerRobot();
String first = fileInput.next();
robot.setName(first);
String second = fileInput.next();
robot.setMainWeapon(second);
int third = fileInput.nextInt();
robot.setNumberOfKills(third);
robotDetails.add(robot);
}
for(KillerRobot i : robotDetails)
{
System.out.println(i);
}
fileInput.close();
}
}
KillerRobot class file:
class KillerRobot {
private String name;
private String mainWeapon;
private int numberOfKills;
KillerRobot()
{
}
public String getName()
{
return name;
}
public String getMainWeapon()
{
return mainWeapon;
}
public int getNumberOfKills()
{
return numberOfKills;
}
public String toString()
{
return name + " used a " + mainWeapon + " to destroy " + numberOfKills + " enemies ";
}
public void setName(String a)
{
name = a;
}
public void setMainWeapon(String b)
{
mainWeapon = b;
}
public void setNumberOfKills(int c)
{
numberOfKills = c;
}
}
As you state that you are a beginner, let us first look at the relevant part of your code, to make sure that we talk about the same thing:
Scanner fileInput = null;
try {
File textFile = new File(userInput.nextLine());
fileInput = new Scanner(textFile);
}
catch (FileNotFoundException e) {
System.out.println("Error - file not found!");
System.out.println("Re-enter file name :");
fileInput = new Scanner(userInput.nextLine());
}
You have an input and you want to check this input for a condition and require a new input until this condition is fulfilled. This problem can be solved using a loop like the following:
Scanner fileInput = null;
do {
System.out.println("Enter file name :");
try {
fileInput = new Scanner(new File(userInput.nextLine()));
} catch (FileNotFoundException e) {
System.out.println("Error - file not found!");
}
} while(fileInput == null);
So finally, why does this work? The fileInput variable is set to null and will remain null until the given file is successfully read from standard input because an exception is thrown otherwise what prevents the fileInput variable to be set. This procedure can be repeated endlessly.
On a side note, for performance reasons, it is normally not a good idea to implement control flow that is based on exceptions. It would be better to check for a condition if a file exists via File::exists. However, if you read the file after checking for its existence, it might have been deleted in the meantime which introduces a racing condition.
Answer to your comment: In Java (or almost any programming language), you can inline expressions. This means that instead of calling two methods in two different statements as in
Foo foo = method1();
Bar bar = method2(foo);
you can simply call
Bar bar = method2(method1());
This way, you save yourself some space (what becomes more and more important if your code gets longer) as you do not need the value that you saved in foo at any other place in your code. Similarly, you can inline (which is how this pattern is called) from
File file = new File(userInput.nextLine())
fileInput = new Scanner(file);
into
fileInput = new Scanner(new File(userInput.nextLine()));
as the file variable is only read when creating the Scanner.
Try putting the try-catch in a loop like below:
Scanner fileInput = null;
while (fileInput==null)
{
try
{
System.out.println("Please enter the file name.");
File textFile = new File(userInput.nextLine());
fileInput = new Scanner(textFile);
}
catch (FileNotFoundException e)
{
System.out.println("Error - file not found!");
}
}
Next you could think of moving the File creation part into separate method, so that the code was cleaner.
Do not fall for try-catch instead add this as your functionality. Exceptions are naturally for run time error handling not for logic building.
Check if file exists at given location.
File textFile = new File(userInput.nextLine());
// Check if file is present and is not a directory
if(!textFile.exists() || textFile.isDirectory()) {
System.out.println("Error - file not found!");
//Reprompt user for name of the text file
System.out.println("Re-enter file name :");
fileInput = new Scanner(userInput.nextLine());
}
You can put while loop instead of if loop if you want to continuously prompt user until correct path is entered.
You can call back your main(), like following
try
{
File textFile = new File(userInput.nextLine());
fileInput = new Scanner(textFile);
}
catch (FileNotFoundException e)
{
System.out.println("Error - file not found!");
main(args); // recursively call main() method
}
Now if user first attempt wrong then your code will asked to re enter file name.
How to check isFile exist?
File file = new File(filePathString);
if(file.exists() && !file.isDirectory()){
System.out.println("file exist");
}
This really is an XY problem because you assumed the only way to check for a file existence is by catching a FileNotFoundException (hence asking about try-catch exception handling) whereas other means exist to help you avoid a try-catch idiom in an elegant manner.
To check if a file exists at the given path or not you can simply use the File.exists method. Please also see the File.isFile method and/or the File.isDirectory method to verify the nature of the targeted File object.
EDIT : As stated by raphw, this solution is best used in simple scenario since it can incur a race condition in the case of concurrent file deletion happening during the file existence check. See his answer for handling more complex scenario.

What is the best way to handle an exception for an invalid file?

I have a java class where a user provides a file path and if the path doesn't exist I ask them to try again. My professor says we should use an exception to handle this.
Here is a snippet of how I'm currently doing it:
public class SalesUtil {
public static void processSales() {
Scanner keyboard = new Scanner(System.in);
System.out.println("Enter sales file name: ");
String salesFile = keyboard.nextLine();
try {
Scanner scanFile = new Scanner(new File(salesFile));
//do stuff
}
} catch(FileNotFoundException fnfe) {
System.out.println("Invalid file name supplied, please try again.");
processSales();
}
}
}
Well in the do stuff section, I'm calculating values and printing data to the console. If I enter the correct file name correctly on the first try all the data is correct. If it is incorrect one or more times the data is not correct.
I imagine this is because of adding function calls on top of my initial stack and never 'getting out' of the initial stack while supplying subsequent stack calls until the correct file is supplied?
I'm still new to java and would appreciate some tips in understanding how to solve this using an exception.
The FileNotFoundException is the correct one to catch, however I gather that you're worried about the stacks building up? I tested reading back the file after multiple failed attempts and it was fine. The recursive call is at the end of the method so it is the last line of code and therefore the stacks shouldn't have any effect.
However, if you want, you could use a while loop instead of recursion to avoid stack buildup:
public static void processSales() {
Scanner scanFile = null;
Scanner keyboard = new Scanner(System.in);
while (scanFile == null) {
System.out.println("Enter sales file name: ");
String salesFile = keyboard.nextLine();
try {
scanFile = new Scanner(new File(salesFile));
while (scanFile.hasNextLine()) {
System.out.println(scanFile.nextLine());
}
} catch(FileNotFoundException fnfe) {
System.out.println("Invalid file name supplied, please try again.");
}
}
}
use the file.exist() method to check, if that what you want to do is to make sure it exist then this is the codes:
File sfile = new File(salesFile);
if (sfile.exists()) {
// ok, file exist do something.
...
}
On the other hand, when you say "invalid file" could be anything, if it is bad filename, then it is another animal (well, different exeception)...
To use try/catch for a readonly file then:
try {
FileInputStream sfile = new FileInputStream(salesFile);
...
} catch (FileNotFoundException e) {
System.out.println(e.getMessage());
}

Categories

Resources