2

I create an I/O class to read and append some text to a file.

I supposed it has been written but for some reasons it's not shown in the file - although the program recognize it.

The file TestResult.csv is in the src/main/resources folder inside the program folder.

I learned how to open and read it from an tutorial on HowToDoInJava: Read a file from the ‘resources’ folder.

My Code

The first couple of code lines are pretty similar to the example in the website.

import java.io.*;
import java.nio.file.Files;
import java.io.FileWriter;

public class ReadResourceFileDemo {

    public static void main(String[] args) throws IOException {
        //open file
        ClassLoader classLoader = IOLab.class.getClassLoader();
        File file = new File(classLoader.getResource("TestResult.csv").getFile());

        //append
        try (FileWriter fw=new FileWriter(file, true)) {
            fw.append("\nJulie,Brown,100,A");
            String content = new String(Files.readAllBytes(file.toPath()));
            System.out.println(content);
        }
    }
}

What I got in TestResult.csv

Alex,Smith,99,A
Jolene,Schmidt,100,A
Mackinzie,Jensen,86,B

The goal is to append the String to the TestResult.csv.

After I ran the first time the result was:

Alex,Smith,99,A
Jolene,Schmidt,100,A
Mackinzie,Jensen,86,B

Then I run again for the second time and got:

"C:\Program Files\Java\jdk-16.0.2\bin\java.exe" "-javaagent:C:\Program 
Files\JetBrains\IntelliJ IDEA 2021.2.1\lib\idea_rt.jar=56365:C:\Program 
Files\JetBrains\IntelliJ IDEA 2021.2.1\bin" -Dfile.encoding=UTF-8 -classpath 
C:\Users\tuant\IdeaProjects\untitled\target\classes edu.sdccd.cisc191.ReadResourceFileDemo
Alex,Smith,99,A
Jolene,Schmidt,100,A
Mackinzie,Jensen,86,B
Julie,Brown,100,A

Process finished with exit code 0

Issue

This means for the first time the program skipped the append or it did but didn't show in the file for some reason.

When I open the file TestResult.csv it only has the first 3 lines.

However, the 4th line that appended manually as Julie,Brown,100,A didn't show up.

Thanks a lot for any help!

hc_dev
  • 8,389
  • 1
  • 26
  • 38
Tobi
  • 65
  • 5
  • The auto-close should flush the writer as well so I'd expect the file content to be updated. However, if the file is treated as a resource this could prevent actual changes. Did you try with an absolute path somewhere on your hard drive? – Thomas Oct 26 '21 at 06:41
  • 1
    It is very strange that you are reading from the file while you still have it open for writing. You should move this "String content = new String(Files.readAllBytes(file.toPath()));" outside of the try statement. – matt Oct 26 '21 at 06:42
  • 2
    It's a very bad idea to write to a file that was found via `ClassLoader.getResource()`. Those should be considered read-only resources and if you run from a jar file, they indeed are. It can easily happen that the automatic background build of your IDE overwrites that file and/or you're not actually opening the path you think you are opening! – Joachim Sauer Oct 26 '21 at 10:31
  • that what the homework told us to do, maybe I misunderstand it. I go double check with my Prof. Thanks for the information! – Tobi Oct 27 '21 at 07:31
  • 1
    @Tobi: it's possible that your prof doesn't care about the potential problems for this case. If that's the case, then ignore the issue for now. Just be aware that it can create some stumbling blocks if you use this specific pattern in "real code". – Joachim Sauer Oct 27 '21 at 09:10
  • 1
    @JoachimSauer, this is a great advice for dealing with code challenges from "homework". It encourages all student and new contributors like Tobi to be honest and __share the context__ (e.g. homework, Prof., course, etc.) to benefit from situational advice and practical experience of professionals and former students ️ – hc_dev Oct 27 '21 at 17:30
  • 1
    @hc_dev: thank you. For me this is just an extension of general openness on SO: Weird constraints and restrictions on technical questions can come from everywhere, even in professional software development we sometimes have surrounding circumstances that make the "best and correct" solution unworkable for some reason. That's just the reality of this job (and many others, I assume). So if those exist (either because it's homework or some stakeholder insists on using XML for something), then it helps if they are clearly communicated. – Joachim Sauer Oct 27 '21 at 17:55

3 Answers3

2

What happened

When you open and write the first time, it is written to a buffer. This buffer is maintained inside the try-with block. But the buffer is not yet flushed. It is flushed either by explicitly calling flush() or by closing the IO-stream/writer which is done implicitly by close():

Closes the stream, flushing it first.

And close is automatically and implicitly called when the try block ends with the closing curly-brace }. This resource-saving attitude of try-with block is the benefit of a concept called auto-closable. The interface AutoCloseable is implemented by most IO-steams like FileWriter.

As your independent read-operation Files.readAllBytes(file.toPath()) is also inside this try-with block, it reads from the file at disk, not from the buffer.

How to fix

Theoretically it would be logical to read from the buffer of fw, but this is not easily possible.

However you can either flush the buffer inside the try-with block using fw.flush() and then read. This flush writes the buffer to disk, so you can read from the disk and expect the flushed content appearing read.

Or you can move the read-operation after the try-with block which assures that buffer was flushed and written before.

Both fixes are suggested in Epicblood's answer - somewhat obsolete but effective.

hc_dev
  • 8,389
  • 1
  • 26
  • 38
1

Try calling the flush method on filewriter.

In the first run, it could be the case that the buffer was not flushed into the file. In general, it is a good practice to call flush after a write to file.

sushant
  • 1,101
  • 2
  • 9
  • 15
  • There must be a reason that a write-operation is not _auto-flushing_ by design. This reason might be performance caused by _flushing_ (disk-IO, etc.). So there are also good reasons and practices to not call flush after a write. However it is good practice to finally close and call flush before close ... thus Java has automated this (see auto-flush on close and auto-closable). – hc_dev Oct 26 '21 at 07:17
  • Thanks @hc_dev for pointing out that nuance. – sushant Oct 26 '21 at 08:51
1

You need to flush your writer:

try (FileWriter fw=new FileWriter(file, true)) {
    fw.append("\nJulie,Brown,100,A");
    fw.flush();
}
String content = new String(Files.readAllBytes(file.toPath()));
System.out.println(content);
Epicblood
  • 1,167
  • 2
  • 10
  • 29
  • 1
    Now, since you're closing the fw, you don't need to flush it. – matt Oct 26 '21 at 06:57
  • Thanks for you suggestion, I"ll try it asap. – Tobi Oct 27 '21 at 05:30
  • It work on the append issue but the csv file in resources folder was not changed. I think some people said it was for read-only. Is there anyway we can deal with this issue? – Tobi Oct 27 '21 at 07:28
  • @Tobi, I suppose "it work on the append issue" means, the `content` read from updated file was printed, i.e. 4th line `Julie,Brown,100,A` shows on console. If so and no error occurred, print the `file.toPath()` __at runtime__ and check that location. It may be in a `target/classes` folder (your _classpath_ at runtime), not in the `src/main/resources`. See [Updating resource files at runtime](https://stackoverflow.com/questions/44889793/updating-resource-files-at-runtime). – hc_dev Oct 27 '21 at 10:04
  • Nice! Thanks alot @hv_dev – Tobi Oct 27 '21 at 17:08