Java PrintWriter strumienia nie ma w pliku


(pain3hp) #1

W książce z której uczę się Javy jest taki w rozdziale o strumieniach jest porozdział "Zapisywanie tekstu".

W nim przykład użycia klasy PrintWriter

 PrintWriter strumien = new PrintWriter("plik.txt");
   
        String name = "Jan Nowak";
        strumien.print(name);

Autor pisze że rezultatem będzie wysłanie napisu "Jan Nowak" do strumienia out, znaki zostaną przekonwertowane na bajty i zapisane w pliku plik.txt

 

Jeśli na końcu wywołam out.checkError() - zwraca false w obu przypadkach.

 

Wynik jest różny nie wiem od czego zależy, kiedy raz zmieniłem out.print(name) na out.write(name) zaczęło działać. Później zmieniłem spowrotem na out.print(name) i też zaczęło działać! W tej chwili nie działa ani write ani print  przy tym samym kodzie...

 

To jest cała klasa która tworzy pusty plik

package javaapplication1;

import java.io.FileNotFoundException;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;

public class JavaApplication1 {
    public static void main(String[] args) throws FileNotFoundException, IOException {
       
        
       PrintWriter strumien = new PrintWriter("jakos.txt");
       
       
       String name = "Jan Nowak";
       
       strumien.print(name);
        
        
    }
    
}

Żeby tego było mało gdzieś czytałem, że jeśli tego pliku nie będzie, zostanie rzucony wyjątek FileNotFoundException, co również się nie dzieje, plik się sam tworzy.

 

 

Czy ktoś mógłby mi to wytłumaczyć dlaczego tak się dzieje?


(kostek135) #2

Nie wywołałeś metody close() (albo flush(), jeśli chcesz opróżnić bufor, ale nie chcesz go zamykać). W ten sposób doprowadzasz do wycieków pamięci oraz nie otrzymujesz oczekiwanego rezultatu.


(pain3hp) #3

Dzięki, tak rzeczywiście działa. Dziwi mnie tylko dlaczego raz mi to jakoś przeszło a na 100% nie użyłem żadnej z tych metod. Kombinowałem tylko w taki sposób.

new PrintWriter(new FileWriter("jakos.txt", false/true));

Zapytam jeszcze o zadziałanie takiego przykładu:

  PrintWriter strumien = new PrintWriter("plik.txt");
       
       
 String name = "Jan Nowak";
       
       double kwota = 2222;
       
       strumien.print(name);
       strumien.print(" ");
       strumien.print(kwota);
       
       strumien.close();
       
        strumien.print(name);
       System.out.println(name);
        strumien.close();

W pliku jest zapis do pierwszego close, kolejny print robi wyciek? Czyli jak to jest? Zamknąłem strumień czyli zwolniłem wszystko to co w nim siedziało w pamięci RAM i zostało skierowane do pliku (na dysk)? Następnie próbowałem do niego jeszcze coś dopisać ale już nie mogę -to zrozumiałe.

 

Tylko gdzie tu jest wyciek? Otwarty strumień w którym są jakieś dane już jest wyciekiem?


(Frankfurterium) #4
  1. Nie używaj gołego PrintWritera. Ta klasa sama dobiera kodowanie znaków i niekiedy do plików mogą ci się zapisać krzaki. Wsadź do środka coś, na czym możesz wymusić kodowanie UTF-8.

 

  1. FileNotFoundException zostałby rzucony, gdybyś próbował zapisać plik w miejscu, gdzie nie masz prawa zapisu (wiem, logiczne jak cholera...), np. w niektórych katalogach systemowych.

 

  1. Czasami działało, bo boolean w konstruktorze odpala autoflusha - automatyczne "przepychanie" bitów, kiedy tylko wejdą do strumienia.

(kostek135) #5

Przeszło ci dlatego, że zależnie od implementacji tej klasy została uruchomiona wewnętrzna procedura odpowiedzialna za oczyszczenie bufora, np.

  • Przesłałeś więcej znaków, niż mieści się w buforze, więc normalne, żeby przyjąć kolejne, to trzeba wyczyścić te co są
  • Istniała okazja do wysłania danych z powodu małego chwilowego obciążenia
  • Cokolwiek innego zależnego od implementacji, której trzeba było się przyjrzeć (polecam JAD w tym celu)

Jeśli wywołasz to tak: new PrintWriter(new FileWriter("jakos.txt", true));

To ustawisz autoflush (za to odpowiada ten boolean, domyślnie false). To znaczy bufor będzie czyścił się na bieżąco, niezalecane.

 

[EDIT]

Nie. Wyciekiem jest nie zamknięcie buffora, ale utracenie referencji do niego. O ile dzieje się to w 3 linijkowym main, to nic nie zauważysz, bo JVM zgaśnie tak szybko jak się pojawił. Napisz jednak przetwarzanie dużych plików w pętli, bez zamykania plików/z usuwaniem referencji, to zobaczysz co się stanie. Zakładając, że podczas finalizacji obiektu nie zostanie uruchomiony close, jest to wyciek na poziomie JVM. To co chcesz zrobić, to zapisać coś do już zamkniętego pliku, co nie powinno mieć miejsca. Powinno to wyglądać jakoś tak:

PrintWriter strumien = new PrintWriter("plik.txt");
 
String name = "Jan Nowak";
double kwota = 2222;
       
strumien.print(name);
strumien.print(" ");
strumien.print(kwota);
       
strumien.flush();
       
strumien.print(name);
System.out.println(name);
strumien.close();

PS


(pain3hp) #6

Dzięki Panowie za odpowiedzi bardzo mi to pomaga na codzień programuję w PHP więc chyba zrozumiecie moją niewiedzę :slight_smile: A Java podoba mi się coraz bardziej :slight_smile:

 

Czy ja wiem czy to jest logiczne? NotFound raczej mowi że wszędzie gdzie go nie ma, nie ma tu nic o prawach dostępu no ale cóż, przyjmuję że tak jest


(kostek135) #7

Ja sądzę, że on użył sarkazmu. Poza tym, nie musisz niczego "przyjmować", wystarczy przeczytać, że tak jest. Java ma bardzo dobrą dokumentację techniczną. http://docs.oracle.com/javase/8/docs/api/java/io/FileNotFoundException.html

 


(pain3hp) #8

Ma się rozumieć że PrintWriter sam obsłużył sobie wyjątek i stworzył plik?


(Frankfurterium) #9

Nie. Ewentualny wyjątek rzuci w niebyt metoda main (fragment throws FileNotFoundException, IOException). Plik zazwyczaj tworzy się automatycznie w chwili pisania do niego. Na systemach linuxowych zdarza się, że plik trzeba stworzyć ręcznie przez wywołanie na File'u metody mkdir/createNewFile.