piątek, 8 marca 2013

Obsługa plików w Javie

Obsługa plików w Javie jest wg. mnie rozwiązana trochę dziwnie. Ale ja programowałem głównie w C++ (w innych językach również, ale bez grzebania się w plikach), więc może się czepiam.

Tak czy siak, muszę się tego nauczyć i zanim wejdzie mi to w krew, postanowiłem sobie to zapisać.

Po pierwsze, z jakiegoś jeszcze dla mnie niejasnego powodu, operacje na plikach muszą zawierać się w bloku try-catch:

try {
    //operacje na plikach
}
//można użyć bardziej specyficznego kodu
//w szczególności przechwytywać konkretne wyjątki
//to jest tylko tak orientacyjnie
catch(Exception e) {
    e.printStackTrace();
}

Następnie musimy otworzyć jakiś plik. Ogólna rada jest taka:

FileReader / FileWriter - dla plików tekstowych

FileInputStream / FileOutputStream - dla innych (binarnych, z niestandardowym kodowaniem...)

Pliki tekstowe

FileReader / FileWriter używamy głównie dla plików tekstowych, więc najlepiej od razu obudować je w BufferedReader / BufferedWriter:

BufferedReader br=new BufferedReader(new FileReader(nazwa_pliku));

i podobnie Writer.

Dane odczytujemy z takiego strumienia linia po linii:

String line;
while( (line=bufferedReader.readLine()) != null ) {
    //operacje na wczytanej linii
}

Po skończeniu operacji na plikach zamykamy plik (ale tylko ten strumień, który jest „na zewnątrz” - w przypadku plików tekstowych obudowanych strumieniem Buffered* będzie to właśnie ów strumień Buffered*):

br.close();

Pliki binarne

Dla plików binarnych, jeśli chcemy zapisywać/odczytywać same bajty (najlepiej w tablicach), możemy używać „gołego” File*Stream:

byte buffer[]=new byte[ROZMIAR];
FileOutputStream outputStream=new FileOutputStream(nazwa_pliku);
outputStream.write(buffer);
outputStream.close();

Tablice w Javie mają wbudowaną informację o wielkości (buffer.length), więc nie trzeba podawać tego parametru. Po dokładniejsze informacje odsyłam do dokumentacji.

Jeśli jednak chcielibyśmy zapisywać zmienne innego rodzaju, warto obudować taki strumień strumieniami odpowiednio DataInputStream i DataOutputStream:

...
float zmienna;
DataOutputStream outputData=new DataOutputStream(outputStream);
outputData.writeFloat(zmienna);
outputData.close();

I to właściwie wszystko (na razie). Jeszcze zapiszę tu parę specyficznych użyć takich strumieni.

Odczyt plików z internetu

Pliki z internetu, a konkretnie strony internetowe, odczytujemy praktycznie tak samo, jak pliki tekstowe. Jedyna różnica jest taka, że musimy stworzyć połączenie z danym adresem (podobnie jak zwykłe plik, także i te operacje muszą znajdować się w bloku try-catch):

URL strona=new URL(adres_strony);
URLConnection polaczenie=strona.openConnection();
InputStreamReader inputStream=new InputStreamReader(polaczenie);
BufferedReader input=new BufferedReader(inputStream);
//wczytujemy plik html
input.close();

Otwieranie plików za pomocą interfejsu graficznego

Jeśli mamy jakiś ładny programik z interfejsem graficznym i chcielibyśmy dodać tam jakiś komponent, dzięki któremu moglibyśmy otwierać okienko wyboru plików (coś na kształt SaveFileDialog i OpenFileDialog w C++ Builderze), to musimy zadeklarować zmienną typu JFileChooser (najlepiej jako final i/lub static):

final JFileChooser fileDialog=new JFileChooser();

W takim FileChooserze możemy ustawiać odpowiednie filtry, katalogi startowe itp., ale muszę powiedzieć, że jest to o wiele bardziej niewygodne od tego, z czym miałem do czynienia w C++ Builderze. Po konkretne informacje odsyłam do dokumentacji, ew. do googli.

Potrzebna nam też będzie zmienna typu File, która będzie przechowywała informacje o wybranym w ten sposób pliku:

File plik;

W jakimś evencie (najczęściej w naciśnięciu buttona) wpisujemy kod:

int returnVal;
returnVal=fileDialog.showOpenDialog(this); //albo SaveDialog
if(returnVal==JFileChooser.APPROVE_OPTION) {
    plik=fileDialog.getSelectedFile();
    if(plik.exists()) {
        //jeśli chcemy, możemy coś z tym zrobić
    } else {
        //i ewentualnie dać znać, że pliku nie ma
    }
}

Informacji przechowywanych w zmiennej typu File nie musimy wykorzystywać od razu - dany plik można otworzyć gdzieś indziej w programie. Nazwę pliku pobieramy w następujący sposób:

nazwaPliku=plik.getPath();

i dalej już otwieramy i dokonujemy odpowiednich operacji na danym pliku wg. poznanych wcześniej metod.


Strony, z których korzystałem:

3 komentarze:

  1. "Po pierwsze, z jakiegoś jeszcze dla mnie niejasnego powodu, operacje na plikach muszą zawierać się w bloku try-catch: "

    To jest spowodowane tym że możemy mieć wiele różnych błędów na operacjach plikowych (np. FileNotFoundExceltion, itp), które w Javie powinny być obsłużone. Zawsze można zostawić blok catch z nieobsłużonym wyjątkiem (ale to nie jest zalecane, bo wówczas po co wyjątki) lub wyrzucić wyjątek na funkcję która wywołała naszą metodę. Wówczas obsługa wyjątku będzie w funkcji wołającej, a tam przez printStackTrace można zobaczyć w której klasie+lini kodu błąd został wyrzucony.

    Swoją drogą zainteresowanym polecam książkę "Thinking in Java": http://www.mindview.net/Books/TIJ/
    Jest ona również dostępna darmowo do pobrania w wersji elektronicznej

    OdpowiedzUsuń
    Odpowiedzi
    1. "...które w Javie powinny być [wyjątki] obsłużone."

      Chodzi mi bardziej o to, że nie mogę tych operacji zapisać poza blokiem try-catch. Wywala mi błąd (piszę w NetBeansie).

      Jak robię np. Integer.ParseInt to mogę dać w bloku try-catch, żeby wyłapać błąd, ale nie muszę, jeśli jestem leniwy, albo piszę coś na szybkiego.

      Jak robię operacje na plikach, to bezwarunkowo muszę umieścić je w bloku try-catch. W przeciwnym wypadku pokazuje mi błąd "unreported exception FileNotFoundException; must be caught or declared to be thrown"

      Usuń
    2. PS. Dzięki za linka. Już sobie ściągnąłem (i parę innych też, chociaż pewnie i tak na razie z nich nie będę korzystał).

      Usuń