7

I am a rookie programmer-wanna-be and came across this problem I couldn't find the answer for.
I use Eclipse, and for the program I use slick and lwjgl-2.9.3
The following code is in a state, inside the public void update(...)

I have the problem with this part of the code:
(the file.txt exists and have no capitals in its name, giveToFile is a String) (no exceptions thrown)

try{
    BufferedWriter bw = new BufferedWriter(new FileWriter("src/file.txt"));
    bw.write(giveToFile);
    bw.close();
}catch(IOException e){
    e.printStackTrace();
}

( EDIT:

try{
    bw = new BufferedWriter(new FileWriter("src/file.txt"));
    bw.write(giveToFile);
    bw.flush();
}catch(IOException e){
    e.printStackTrace();
}finally {
    if (bw != null){
        try {
           bw.close();
       }catch (Throwable t){
           t.printStackTrace();
       }
   }
}  

produced the same bug)

I placed a System.out.print at the end of the try block, and it run normally, and run only once. I also used a g.drawString and the giveToFile always gives the intended String. I executed the following two experiments. (The program is a game-ish thing, you get a score at the end based on your performance and it places it into the high-scores then rewrites the TXT file.) (I suggest to read TLDR before.)

Experiment 1 (file.txt : "0 0 0 0 0") (successful):

  1. I run the program and earn 15 points.
    - string loaded from the txt: "0 0 0 0 0"
    - giveToFile (string): "15 0 0 0 0"
  2. I doubleclick the TXT file inside Eclipse at the left side (package explorer), it opens in a new tab and I see inside the txt: "15 0 0 0 0", I close the tab
  3. I run the program again and earn 30 points.
    - string leaded from text: "15 0 0 0 0"
    - giveToFile (String): "30 15 0 0 0"
  4. I doubleclick the TXT file inside Eclipse at the left side (package explorer), it opens in a new tab and I see inside the txt: "30 15 0 0 0", I close the tab
  5. I run the program one last time and earn 0 points.
    - string loaded from txt: "30 15 0 0 0"
    - giveToFile (string): "30 15 0 0 0"

Experiment 2 (file.txt : "0 0 0 0 0") (failure):

  1. I run the program and earn 15 points.
    - string loaded from the txt: "0 0 0 0 0"
    - giveToFile (string): "15 0 0 0 0"
  2. I doubleclick the TXT file inside Eclipse at the left side (package explorer), it opens in a new tab and I see inside the txt: "15 0 0 0 0", I close the tab
  3. I run the program again and earn 30 ponts.
    - string leaded from text: "15 0 0 0 0"
    - giveToFile (String): "30 15 0 0 0"
  4. I dont doubleclick the TXT file, I dont open it in a new tab, and I dont check it.
  5. I run the program one last time and earn 0 points.
    - string loeaded from txt: "15 0 0 0 0"
    - giveToFile (string): "15 0 0 0 0"

TLRD: the program doesn't write into the TXT file unless I check it manually

there is a bug, and there isn't, depends on if I check the txt file, or not

sorry for the long question and sorry if it is something super simple, but I am a beginner and couldn't find any solution on the internet, thanks for the help in advance

EDIT:

I use this to close the program: (xpos and ypos are the mouse coordinates) (basically a primitive exit button)

if((xpos>= 200 && xpos <= 400) && (ypos>=100 && ypos <=200)){
    if(Mouse.isButtonDown(0)){
        System.exit(0);
    }
}  

I got this: (no exceptions)

Thu Apr 30 16:44:14 CEST 2015 INFO:Slick Build #237
Thu Apr 30 16:44:14 CEST 2015 INFO:LWJGL Version: 2.9.3
Thu Apr 30 16:44:14 CEST 2015 INFO:OriginalDisplayMode: 1366 x 768 x 32 @60Hz
Thu Apr 30 16:44:14 CEST 2015 INFO:TargetDisplayMode: 600 x 600 x 0 @0Hz
Thu Apr 30 16:44:15 CEST 2015 INFO:Starting display 600x600
Thu Apr 30 16:44:15 CEST 2015 INFO:Use Java PNG Loader = true
Thu Apr 30 16:44:15 CEST 2015 INFO:Controllers not available

This part reads the file, no other part does anything with the file, and the reader works fine:

try{
    InputStream is = getClass().getResourceAsStream("/file.txt");
    Scanner fileIn = new Scanner(is);
    for(int i=0; i<SCOREMAX; i++){
        scoreInt[i] = fileIn.nextInt();
    }
    fileIn.close();
}catch (Exception e) {
    e.printStackTrace();
}  

it is inside the public void init and SCOREMAX's type is public static final int

LW001
  • 2,452
  • 6
  • 27
  • 36
  • 1
    Have you tired `bw.flush();` just before `bw.close();` – Brett Walker Apr 30 '15 at 14:20
  • @BrettWalker `close` invokes `flush`. – Reut Sharabani Apr 30 '15 at 14:21
  • 2
    My best guess based on the data you provided would be that the (old) copy you have open on eclipse is configured to automagically be synced to the file system after the program executes. This reverses the file copy on the disk to an old version (the one you have open). To verify you may want to not open the file at all and run the program multiple times. If the works you can run the program and check each iteration on a copy of the file (simple copy it manually). If both work I'd lean towards my observation. – Reut Sharabani Apr 30 '15 at 14:35
  • @ Reut Sharabani the close method of BufferdWriter flushes it's internal buffer but it does not call flush on the underlaying Writer. – Robert Apr 30 '15 at 14:54
  • @Reut Sharabani I only opened Eclipse, and I only opened the file.txt from Eclipse (which showed it inside Eclipse in a new tab), the file.txt never poped up in a seperate window. Without ever opening it inside Eclipse, just running the program several times, none of the writing works. – stack_overflow_interrogator Apr 30 '15 at 15:38
  • In order to rule out a few things: Try using a file in an *absolute* path that is not in your Eclipse workspace, e.g. `new FileWriter("C:/file.txt")`. And as another, independent test: Try what happens when you select your project root after writing the file, and press `F5` (refresh) (*instead* of opening the file!), and load it afterwards. – Marco13 Apr 30 '15 at 16:06
  • @Marco13 the f5 works fine, like if I'd open it from Eclipse, I am testing the absolute path aswell – stack_overflow_interrogator Apr 30 '15 at 16:14
  • Then it's most likely related to what @ReutSharabani said: Some automagic eclipse caching thing. I never heard about that, but websearches like "eclipse file caching changes" indicate that this might be the case, and I would not even be surprised, considering that you're using the `src` directory and Eclipse has a "local history". I think that the best solution is to **not** write into the `src` directory, anyhow... – Marco13 Apr 30 '15 at 16:21

3 Answers3

4

You need to close the BufferedWriter in your finally block.

Optionally, you can flush your BufferedWriter as well in the try block after you are finished writing, although the close operation will flush it first.

Here's a revisited example, Java 6-styled:

BufferedWriter bw = null;

try {
    bw = new BufferedWriter(new FileWriter("src/file.txt"));
    bw.write(giveToFile);
    // bw.flush(); // if needed
}
catch(IOException e){
    e.printStackTrace();
}
finally {
    if (bw != null) {
        try {
            bw.close();
        }
        catch (Throwable t) {
            t.printStackTrace();
        }
    }
}

... and Java 7-styled ("try with" and AutoClosables):

try (BufferedWriter bw = new BufferedWriter(new FileWriter("src/file.txt"))) {
    bw.write(giveToFile);
    bw.flush();
}
catch(IOException e){
    e.printStackTrace();
}
Mena
  • 47,782
  • 11
  • 87
  • 106
  • 2
    `close` should call `flush`. Also, if no exception was thrown, why would it matter if it's in `finally` or not? – Reut Sharabani Apr 30 '15 at 14:23
  • @ReutSharabani as mentioned, the `close` invocation will `flush`. However, OP might want to `flush` eagerly for some reason, hence the option. – Mena Apr 30 '15 at 14:25
  • @ReutSharabani concerning the `finally` block, it will perform regardless of `Exception`s thrown, hence the common practice to `close` streams there. Java 7's `AutoClosable`s grant a more fluent syntax. – Mena Apr 30 '15 at 14:29
  • 1
    Why does this fix the problem? Why would calling `flush` twice in a row (when no new data was written) matter? Can you link me to "eager flushing" problems? I am familiar with `finally` and the OP clearly stated no exceptions were thrown - which means `close` was called. This is answer is unclear to me. – Reut Sharabani Apr 30 '15 at 14:30
  • @ReutSharabani there are no "eager flushing" problems I know of. But you might need to programmatically ensure the data **is** on disk, which is done by flushing, either lazily (let the `BufferedWriter` do it or wait for `close`), or eagerly (DIY). – Mena Apr 30 '15 at 14:33
  • No, it still does the same bug. – stack_overflow_interrogator Apr 30 '15 at 14:41
  • @stack_overflow_interrogator mmh are you sure your program finished executing when checking the file in the end? Also, are you sure there is **no** `Exception` thrown, here or somewhere else, impacting on your execution flow? – Mena Apr 30 '15 at 14:42
  • @stack_overflow_interrogator also is there any multi-threaded context that may impact on the writing? – Mena Apr 30 '15 at 14:44
  • no exceptions thrown, and there is no more code that would do anything with the file – stack_overflow_interrogator Apr 30 '15 at 15:13
1

YAY!!! After trying out everything you all said, get deeper into the topic and spend almost 2 days researching the problem I got found a solution!!!!

The file is still in the same src so its path is src/file.txt but there is a file automatically created in the bin folder with the same name, deleting one results boths file to be deleted. In short, these two files somehow the same

For the code I used this to read the file:

try{    
    InputStream is = state1.class.getResourceAsStream("/file.txt");

    Scanner fileIn = new Scanner(is);
    for(int i=0; i<SCOREMAX; i++){
        scoreInt[i] = fileIn.nextInt();
    }
    fileIn.close();
}catch (Exception e) {
     e.printStackTrace();
}  

Notice that I didnt use src/file.txt just /file.txt, it reads from the file in the bin folder.

For the write I used:

FileOutputStream fs = null;
try{
    fs = new FileOutputStream("bin/file.txt");
    OutputStreamWriter ow = new OutputStreamWriter(fs);
    BufferedWriter bw = new BufferedWriter(ow);
    bw.write(giveToFile);
    bw.flush();
    bw.close();
}catch (IOException e){
    e.printStackTrace();
}finally{
    try{fs.close();} catch(IOException e){e.printStackTrace();}
}  

And again, notice that I didnt use the src/file.txt here either, rather I directed it to the file in the bin folder.

And it worked!!!

At the end I got ~70 row code inside /* ... */ marks labeled as '//almost works' and at least 10times more that I already deleted. But finally it works!!

Thank you for everyone who commented and answered here, I wouldnt be able to find the solutions without you all. Thank you everyone again!

  • So the main reason indeed seems to be what @ReutSharabani mentioned in his comment http://stackoverflow.com/questions/29970216/schrodingers-bug-bufferedwriter-doesnt-write-into-txt-unless-manually-checked/29970298#comment48060836_29970216 (and which I referred to later), namely that programmatically updating things in the `src` folder confuses Eclipses local history – Marco13 May 02 '15 at 11:29
0

Java makes a distinction between File and resource. A resource is on the class path, possibly in a jar. A resource hence should be considered read-only. It may also be cached.

In your case using a resource (at reading) is undesirable.

I would do something like the following.

String userDir = System.getProperty("user.home");
Path txtPath = Paths.get(userDir, "file.txt");

List<String> content = Files.readAllLines(txtPath, StandardCharsets.UTF_8);

Files.write(txtPath, content, StandardCharsets.UTF_8);

giveToFile = content.get(0);
content = Collections.singletonList(giveToFile);

This keeps the file out-of-the class path, so you may create an application in a jar.

You could maintain an initial read-only file.txt as resource, functioning as initial template, and copy that to the user directory.

I have used UTF-8 encoding here, so the file format is portable.

Joop Eggen
  • 107,315
  • 7
  • 83
  • 138