Saving data to a file doesn't behave as expected - java

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. :)

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.

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.

Check if file exists from string

This is the code I use when I try to read some specific text in a *.txt file:
public void readFromFile(String filename, JTable table) {
BufferedReader bufferedReader = null;
try {
bufferedReader = new BufferedReader(new FileReader(filename));
String a,b,c,d;
for(int i=0; i<3; i++)
{
a = bufferedReader.readLine();
b = bufferedReader.readLine();
c = bufferedReader.readLine();
d = bufferedReader.readLine();
table.setValueAt(a, i, 0);
table.setValueAt(b, i, 1);
table.setValueAt(c, i, 2);
table.setValueAt(d, i, 3);
}
} catch (FileNotFoundException ex) {
ex.printStackTrace();
} catch (IOException ex) {
ex.printStackTrace();
} finally {
//Close the reader
try {
if (bufferedReader != null) {
bufferedReader.close();
}
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
And it is called in this way:
readFromFile("C:/data/datafile.txt", table1)
The problem is the following: the 1st time I open the program the *.txt file I'm going to read does not exist, so I thought I could use the function exists(). I have no idea about what to do, but I tried this:
if(("C:/data/datafile.txt").exists()) {
readFromFile("C:/data/datafile.txt", table1)
}
It is not working because NetBeans gives me a lot of errors. How could I fix this?
String has no method named exists() (and even if it did it would not do what you require), which will be the cause of the errors reported by the IDE.
Create an instance of File and invoke exists() on the File instance:
if (new File("C:/data/datafile.txt").exists())
{
}
Note: This answer use classes that aren't available on a version less than Java 7.
The method exists() for the object String doesn't exist. See the String documentation for more information. If you want to check if a file exist base on a path you should use Path with Files to verify the existence of the file.
Path file = Paths.get("C:/data/datafile.txt");
if(Files.exists(file)){
//your code here
}
Some tutorial about the Path class : Oracle tutorial
And a blog post about How to manipulate files in Java 7
Suggestion for your code:
I'll point to you the tutorial about try-with-resources as it could be useful to you. I also want to bring your attention on Files#readAllLines as it could help you reduce the code for the reading operation. Based on this method you could use a for-each loop to add all the lines of the file on your JTable.
you can use this code to check if the file exist
Using java.io.File
File f = new File(filePathString);
if(f.exists()) { /* do something */ }
You need to give it an actual File object. You're on the right track, but NetBeans (and java, for that matter) has no idea what '("C:/data/datafile.txt")' is.
What you probably wanted to do there was create a java.io.File object using that string as the argument, like so:
File file = new File ("C:/data/datafile.txt");
if (file.exists()) {
readFromFile("C:/data/datafile.txt", table1);
}
Also, you were missing a semicolon at the end of the readFromFile call. Im not sure if that is just a typo, but you'll want to check on that as well.
If you know you're only ever using this File object just to check existence, you could also do:
if (new File("C:/data/datafile.txt").exists()) {
readFromFile("C:/data/datafile.txt", table1);
}
If you want to ensure that you can read from the file, it might even be appropriate to use:
if(new File("C:/data/datafile.txt").canRead()){
...
}
as a condition, in order to verify that the file exists and you have sufficient permissions to read from the file.
Link to canRead() javadoc

How to save the data into File in java?

I have one problem, that is I have one string of data and I want to save it into a separate file every time. Please give me a suggestion.
Thanks,
vara kumar.pjd
Use a timestamp in the filename so you can be sure it is unique. Below example uses a timestamp in milliseconds which should be enough in most cases.
If you expect you can have multiple files within 1 millisecond then you could do something with a GUID/UUID. Note that GUID/UUID could result in duplicates too, however this chance is extremely rare.
import java.io.*;
class FileWrite
{
public static void main(String args[])
{
try{
// Create file
FileWriter fstream = new FileWriter(System.currentTimeMillis() + "out.txt");
BufferedWriter out = new BufferedWriter(fstream);
out.write("Hello Java");
//Close the output stream
out.close();
}catch (Exception e){//Catch exception if any
System.err.println("Error: " + e.getMessage());
}
}
}
You don't need to compute the filename by yourself, have a look at File.createTempFile.
From the javadoc:
Creates a new empty file in the specified directory, using the given prefix and suffix strings to generate its name. If this method returns successfully then it is guaranteed that:
The file denoted by the returned abstract pathname did not exist before this method was invoked, and
Neither this method nor any of its variants will return the same abstract pathname again in the current invocation of the virtual machine.
This method provides only part of a temporary-file facility. To arrange for a file created by this method to be deleted automatically, use the deleteOnExit() method.
A one liner. Using base 36 for the ids will make the file names shorter.
IOUtils.write(text, new FileWriter(Long.toString(System.currentTimeMillis(), 36)+".txt")));
http://commons.apache.org/io/
One solution can be, use a random number generator to generate a random number. Use this random number with some text as a filename. Maintain a list of already used names and each time you are saving the file, check through this list if the file name is unique.
One of possible ways to get File object with unique name could be:
public static File getUniqueFile(String base, String ext, int index) {
File f = new File(String.format("%s-%03d.%s", base, index, ext));
return f.exists() ? getUniqueFile(base, ext, index + 1) : f;
}
Update: and here goes basic usage/test case:
String s = "foo string\n";
FileWriter writer = null;
for (int i = 0; i < 10; i++) {
File f = getUniqueFile("out", "txt", 0);
try {
writer = new FileWriter(f);
writer.write(s);
writer.close();
writer = null;
} catch (IOException e) {
e.printStackTrace();
break;
}
}
if (writer != null) { try { writer.close(); } catch (Exception e) {} }

how to clear file before putting data there?

I need to save some data into text file. I'm using class Files with its method write().
If such file doesn't exist - everything alright. The problem is if such file already exists it appends new data to the end of the file. And I need to clear it first. The code is:
public static void main(String[] args) {
DepoList test0 = new DepoList();
test0.init();
ArrayList<Depo> list0 = test0.getList();
Collections.sort(list0);
for (Depo depo : list0) {
String str = String.format("sum = %1$8.2f interest = %2$7.2f\n", depo.getSum(), depo.getIncome());
System.out.format(str);
try {
Files.write(Paths.get("depo.txt"), str.getBytes(), StandardOpenOption.CREATE, StandardOpenOption.APPEND);
} catch (IOException e) {
e.printStackTrace();
}
}
System.out.println();
I think I need to add some another StandardOpenOperation. How to clear the file before putting data there?
Remove StandardOpenOption.CREATE,Standardoption.APPEND this just appends your new data to the existing one
Use Files.write((Paths.get("depo.txt"), str.getBytes());

Categories

Resources