简体   繁体   中英

How to write to a text file from an ArrayList

I'm trying to take data from an ArrayList of a class and write it into a text file. It creates the temporary file, but doesn't do anything with it. It prints what I'm trying to put in the file and doesn't delete the temporary file. What am I doing wrong?

try
{
    File temp = new File("temp.txt");
    File file = new File(prop.getProperty("path"));
    BufferedWriter writer = new BufferedWriter(new FileWriter(temp));
    ArrayList<Contact> contacts = gestor.checkData();
                        
    if(temp.createNewFile())
    {
        for(int i = 0; i < contacts.size(); i++)
        {
            writer.write(contacts.get(i).getName());
            writer.write(contacts.get(i).getLastNames());
                                
            DateFormat df = new SimpleDateFormat("yyyy-MM-dd");
            String date = df.format(contacts.get(i).getBirthday());
                                
            writer.write(date);
            writer.write(contacts.get(i).getEmail());
        }
    writer.close();
    file.delete();
    temp.renameTo(file);
    }
}
catch (IOException e)
{
    e.printStackTrace();
}

The code of checkData() just returns the ArrayList<Contact> contactList .

Many problems with your code:

resource leakage

Something like a new FileWriter is a resource . Resources MUST be closed. As a consequence, you should never make resources (you usually know it when you make a resource: you either call new X where X is clearly representative of a resource, or you call something like socket.getInputStream or Files.newBufferedReader ... unless you do it 'right'. There are only two ways to do it right:

try (FileWriter w = new FileWriter(...)) {
   // use it here
}

or, if you need the resource to be a field of your class, then the only safe way is to turn your class into a resource: make it implements AutoClosable , make a close method, and now the burden of using that try syntax is on whomever uses your class. There is no way to safely do this stuff without try-with-resources *.

Charset messup

files are bytes. not characters. In addition, the filesystem has no clue what the 'encoding' is. How do you turn , or for that matter, é into bytes? The answer depends on charset encoding. The problem with all methods that turn bytes into characters or vice versa that are NOT in the java.nio.file package is that they use 'platform default encoding'. That's a funny way of saying 'the worst idea ever', as that will 'work' on your machine and will pass all tests, and then fail in production, at the worst possible moment. The solution is to never rely on platform default, ever. If you really really intend to use that, make it explicit. Unfortunately, FileWriter, until java11, has no way of specifying charset encoding, which makes it an utterly useless class you cannot actually ever use without writing buggy code. So don't. I suggest you switch to the new file API, which defaults to UTF-8 (quite a sane default), and can do a lot of complicated things in one-liners.

No newlines

You just .write all this data. write does exactly what it says, and writes precisely the string. It does not print anything more. Specifically, it does not print any newlines. Surely you did not intend to just write, say, JoeSteel1990-10-01 to the file, all in one jumbled mess like that? write \n to avoid this, or preconstruct the entire string just as you want it (with newlines), and then write that.

on unexpected condition silently do nothing

Your code will silently do nothing if the 'temp' file already exists. It sounds like the design is that this is a weird situation (as the temp file is 'renamed' right after). As a general rule of thumb, 'silently do nothing' is entirely the wrong instinct to have when you run into scenarios you know are unlikely or seemingly impossible. The right instinct is the exact opposite: Fail as spectacularly as you can. (The best option, of course, is to think about what the weird scenario means and handle it properly, but that's not always possible). So, instead of that, try:

if (!temp.creatNewFile()) {
    throw new RuntimeException("That is weird - tempfile already exists: " + temp);
}

That's the right mindset: If weird things happen, blow up, as fast as you can, with enough detail so you know what's going wrong. (The most likely 'correct' way to handle this is to just delete the temp file. The whole point of that temp file is that if it's still there when you enter this code, that the previous attempt failed halfway through, so just delete the product of the failed operation, it isn't useful).

exception handling.

Whilst common in examples and even IDE templates, you're not doing it right. You should NEVER handle an exception by printing something and carrying on. Think about it: If something went wrong, continuing with the code execution is either going to cause nasty problems (as you surely did not consider that one of the steps in your program failed when you write your code), or is going to cause another error. And if all errors are handled like this, one thing goes wrong and you get 85 error traces in your logs. That's not useful. If a problem occurs and you do not know how to handle it, do NOT continue running . The only sensible 'I do not know how to handle this' exception handling is:

catch (IOException e) {
    throw new RuntimeException("unhandled", e);
}

sometimes a better exception than RuntimeException is possible (such as UncheckedIOException or ServletException, it depends on the situation). Also, sometimes the right answer is to just throws the exception onwards. Remember, public static void main can (and usually should!) be declared as throws Exception .

Putting it all together

try {
    Path temp = Paths.get("temp.txt");
    Path file = Paths.get(prop.getProperty("path"));
    ArrayList<Contact> contacts = gestor.checkData();

    try (BufferedWriter writer = Files.newBufferedWriter(temp)) {
      for (Contact contact : contacts) {                        
          writer.write(contact.getName());
          writer.write("\n");
          writer.write(contact.getLastNames());
          writer.write("\n");
          DateFormat df = new SimpleDateFormat("yyyy-MM-dd");
          String date = df.format(contact.getBirthday());
          writer.write(date);
          writer.write("\n");
          writer.write(contact.getEmail());
          writer.write("\n");
      }
    }
    Files.delete(file);
    Files.move(temp, file);
} catch (IOException e) {
    throw new UncheckedIOException(e);
}

The above will probably fail, but it will fail telling you exactly why it failed. For example, it might tell you that the directory you are trying to write to does not exist, or you have no write access. Whereas your code will then silently just do nothing.

*) For you java pros out there, sure, you can handroll your own try/finally loops. Let me know how many programmers new to java ever managed to dodge every mine in the minefield when you do that. Until you are fairly wintered, this rule of thumb becomes effectively a rule of law: You can't do it safely without twr. Let's keep it simple.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM