1

I have a program that does a lot of processing with loops and writes strings to a file at many different points. I'm not sure about the overall design for how best to do this. I won't need to read from the file at any point during running, though will want to view it afterwards.

Firstly, is a BufferedWriter with FileWriter a reasonable way of doing this?

Secondly, presumably I don't want to be opening and closing this every time I want to write something (several times per second).

But if I use try with resources then I'd have to put practically the entire program inside that try, is this normal?

At the moment the skeleton looks like:

try (FileWriter writer = new FileWriter("filename.txt");
     BufferedWriter bw = new BufferedWriter(writer)) {

} catch (IOException e) {
    //catch IO error
}

for (//main loop){
    bw.write(string);
    for (//several sub loops){
        bw.write(//more strings);
    }
    for (//several sub loops){
        bw.write(//more strings);
    }
}

bw.write(//final string);

try {
    bw.close();
} catch (IOException ex) {
    //catch IO error
}

Does this look reasonable or is there a better way? Thanks very much in advance for the help.

Edit - thanks to you all for the help, totally answered my questions.

  • How does this code even compile? `bw` isn't in scope after the try-with-resources block. Do you have another `bw` defined elsewhere? – Andy Turner Apr 17 '19 at 18:47
  • It doesn't compile, it's just a sketch. That's one of my issues, would I need to put the whole of my code in the for loops in a try-with-resources? –  Apr 17 '19 at 19:37
  • @John There are some ways in which you dont have, one way is to produce the `String` to write before the try-with-resources block and then write it as a whole. But as is described in my answer, this is not that memory friendly on big files. But as long you dont write for hours with long pauses, don't hesitate to keep the file open and wrap the block around it. Improvde readability by producing the content in single methods so the try block doesn't get too bloated and bad to read. – Zhedar Apr 17 '19 at 20:00

4 Answers4

1

Firstly, is a BufferedWriter with FileWriter a reasonable way of doing this?

Yes, it should be the most convenient way to do this.

Secondly, presumably I don't want to be opening and closing this every time I want to write something (several times per second).

You really shouldn't. But you would actually overwrite your progress this way everytime you open the file anyway. That's because you didn't tell the FileWriter to append to an existing file (via new FileWriter("filename.txt", true);.

But if I use try with resources then I'd have to put practically the entire program inside that try, is this normal?

I don't see a problem with that. You can (and should) always move your logic into own methods or classes, which may return the Strings to write. This way you get the actual business logic separated from the technical file writing logic and structure your code, making it easier to understand.

You could also just write into a giant big String and then write that String in the try-with-resources block. But that has it's limits with really big files and may not be the best choice sometimes.

Zhedar
  • 3,480
  • 1
  • 21
  • 44
0

But if I use try with resources then I'd have to put practically the entire program inside that try, is this normal?

Thats just how try-catch-with-resources work - it closes resources on exiting try block. If that is bothering you, don't use that construct and you manage writer yourself.

Above skeleton will not work as first try will open and close your writers;

Antoniossss
  • 31,590
  • 6
  • 57
  • 99
0

It is totally OK to put the whole Code into a try-catch routine. Whenever you have issues to write into the file it will just catch it and does not give you an error. However, I would recommend you to try this structure with just one try-catch routine.

try  { (FileWriter writer = new FileWriter("filename.txt");
 BufferedWriter bw = new BufferedWriter(writer)) 

for (/*main loop*/){
   bw.write(string);
   for (/*several sub loops*/){
    bw.write(/*more strings*/);
    }
for (/*several sub loops*/){
    bw.write(/*more strings*/);
    }
   }

bw.write(/*final string*/);


bw.close(); 


} catch (IOException e) {
   System.out.println("error");
}

PS: If you comment something between some code use this:/* comment */ instead of this:// because it will comment out the whole line.

Manuel
  • 44
  • 10
  • You've got the opening syntax of the try-with-resources wrong. Also you don't need to close the `BufferedWriter` anymore as the managed try block does that for you. – Zhedar Apr 17 '19 at 20:06
  • You are in TWR block, no need to close expicitly – Antoniossss Apr 17 '19 at 20:07
  • @Zhedar, how is the syntax wrong? Good point re unnecessary close. –  Apr 17 '19 at 20:26
0

Here is an alternate that does finer exception handling. In many cases, this is preferred. Having a catch block handle too many exceptions gets to be very confusing: Control flow is obscured, and diagnosing errors can be a lot harder.

Having a file open through the entire time a program is running is very usual. This is often the case for log files. If you know your program will be running for a long time, and if you suspect there will be long delays between output to a single file, you could open and close the file for each batch of close in time operations. But you would have to have a clear idea of the pattern of activity to do this, as you will want to match the open time of the file with expected close-in-time batches of writes. You should very much avoid high frequency open and close operations. That has all sorts of unwanted extra overhead.

package my.tests;

import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
import java.util.function.Consumer;

public class WriterTest {
    public static final String TARGET_NAME = "filename.txt";

    public void performMainLoop() {
        performWrites( this::mainLoop, TARGET_NAME );
    }

    public void performWrites( Consumer<Writer> writeActor, String targetName ) {
        FileWriter fileWriter;
        try {
            fileWriter = new FileWriter(targetName);
        } catch ( IOException e ) {
            System.out.println("Open failure: " + e.getMessage());
            e.printStackTrace();
            return;
        }

        BufferedWriter bufferedWriter = null;
        try {
            bufferedWriter = new BufferedWriter(fileWriter);
            writeActor.accept(bufferedWriter);

        } finally {
            if ( bufferedWriter != null ) {
                try {
                    bufferedWriter.close();
                } catch ( IOException e ) {
                    System.out.println("Unexpected close failure: " + e.getMessage());
                    e.printStackTrace();
                }
            } else {
                try {
                    fileWriter.close();
                } catch ( IOException e ) {
                    System.out.println("Unexpected close failure: " + e.getMessage());
                    e.printStackTrace();
                }
            }
        }
    }

    public void mainLoop(Writer writer) {
        for ( int loopNo = 0; loopNo < 10; loopNo++ ) {
            try {
                writer.write("Loop [ " + Integer.toString(loopNo) + " ]\n");

            } catch ( IOException e ) {
                System.out.println("Unexpected write failure: " + e.getMessage());
                e.printStackTrace();
                return;
            }
        }
    }
}
Thomas Bitonti
  • 1,179
  • 7
  • 14
  • Alot of boilerplate code, no try-catch-with-resources, closing both writers while closing buffered writer would do. – Antoniossss Apr 17 '19 at 20:06