I would like to create a result.csv file if it does not exist, then I call write
several times and append corresponding messages one by a call.
But If the file already exists and contain something before the run of the program, I would like to erase its contents and then begin to append corresponding messages .
I have tried the following:
public final class Writer {
public static void write(String message, String destinationPath) {
try (FileWriter fw = new FileWriter(destinationPath, true);
BufferedWriter bw = new BufferedWriter(fw)
) {
bw.write(message);
bw.newLine();
} catch (IOException e) {
e.printStackTrace();
}
}
}
But, as you may guess, when I run the program over and over it just appends new line on existing ones not erasing what was inside before the program was ran.
the call of write
is done like:
studentNamesAndIds.values().forEach(id -> Writer.write(id+","+ DecideGrade.softLookUp(id, studentIdsAndGrades),writeFilePath));
What should I change to get the result I mentioned at the beginning?
P.S. If I were declaring new FileWriter(destinationPath)
without the second argument true
then every call of write
would erase the content of my file, I would withe the last line inside.
This code should do the job according to your description from the question:
public final class Writer
{
private static final Set<String> m_AppendMarkers = new HashMap<>();
public static void write( final String message, final String destinationPath )
{
final var append = m_AppendMarkers.contains( destinationPath );
try( final var fw = new FileWriter( destinationPath, append );
final var bw = new BufferedWriter( fw ) )
{
bw.write( message );
bw.newLine();
m_AppendMarkers.add( destinationPath );
}
catch( final IOException e )
{
e.printStackTrace();
}
}
}
The set will be empty at each restart of the program, therefore Set::contains
will return false
for the first attempt to write to the given file; for further attempts, the path to the file was added to the set and the contains check will give you true
, so further lines will be appended.
But to be honest, although this works, you should consider a different design! So it is not a good idea to open and close a file for each and every new line you write to it.
Also working with a static method is not good design (and that is the reason why you need the Set
instead of a simple boolean
flag).
To do it right (or at least a bit better) try this:
public class YourProgram
{
private static class Writer implements AutoClosable
{
private final String m_DestinationPath;
private final StringJoiner m_Buffer ) = new StringJoiner( "\n", "", "\n" );
public Writer( final String destinationPath )
{
m_DestinationPath = destinationPath;
}
@Overwrite
public final void close() throws IOException
{
try( final var writer = new BufferedWriter( new FileWriter( m_DestinationPath, false ) ) )
{
writer.write( m_Buffer.toString() );
}
}
public static void write( final String message )
{
m_Buffer.add( message );
}
}
// class Writer
private static Writer m_Writer;
public static final void main( final String... args )
{
try( var w = new Writer( "TheNameOfYourOutputFile" ) )
{
m_Writer = w;
// Your code goes here …
…
}
catch( final IOException e )
{
err.println( "Failed to write the output file" );
e.printStackTrace( err );
}
catch( final Throwable t )
{
err.println( "Unhandled exception" );
t.printStackTrace( err );
}
}
}
// class YourProgram
Error handling was omitted to keep readability.
As long as you do not attempt to write several GigaBytes of data, this should work much more smoothly than the other design. Ok, it is not crash-save, nothing will be written to the output file if the program will be killed externally.