Java: Strumienie

W Javie, podobnie jak i w wielu innych językach programowania, odczyt i zapis do plików realizowany jest na zasadzie strumieni. Koncepcja strumieni może początkowo wydawać się dziwna, ale po jej zrozumieniu można łatwo i efektywnie operować na plikach.

Po otwarciu pliku do odczytu, cała jego zawartość jest w strumieniu. Później, odczytując z niego dane, ‚wyciągamy’ je bajt po bajcie ze strumienia, aż strumień będzie pusty.

Standardowo czytanie i zapis do pliku odbywa się bajt po bajcie. Do odczytu pojedynczych bajtów służy klasa FileInputStream, a do zapisu FileOutputStream. Może na początek mały przykład wyświetlenia na ekran zawartości pliku odczytując jego zawartość bajt po bajcie.

FileInputStream fis = new FileInputStream("pliktestowy.txt");
int bajt = fis.read();
while(bajt != -1){
   System.out.print((char)bajt);
   bajt = fis.read();
}

Jak można zauważyć w powyższym przykładzie, metoda read() klasy FileInputStream odczytuje jeden znak z pliku i zwraca go w postaci typu int. W przypadku, gdy nie ma już znaków, metoda read() zwraca wartość -1. Przy wyświetlaniu, aby wyświetlony został znak a nie liczba, trzeba jawnie wymusić konwersję przez dodanie (char) przed nazwą zmiennej.

Przykład ten jednak nie zadziała. Dlaczego? Bo operacje na pliku muszą znajdować się wewnątrz bloku try{ … } catch(){}. Powyższy przykład można zapisać trochę ładniej (już wersja działająca).

try{
   FileInputStream fis = new FileInputStream("pliktestowy.txt");
   int bajt = 0;
   while((bajt = fis.read()) != -1){
      System.out.print((char)bajt);
   }
} catch (IOException ex){
   System.out.println("Błąd przy odczycie danych: "+ex);
}

Klasa FileInputStream ma jeszcze jedną przydatną metodę: available(), która zwraca ilość bajtów, które można jeszcze odczytać z danego strumienia, a więc powyższy przykład można zbudować jeszcze inaczej – czytając znaki z pliku jeśli available() jest większe od zera.

Strumień można zamknąć korzystając z metody close(), jednak jeśli nie zachodzi potrzeba, żeby zrobić to przed zakończeniem działania programu (np. jeśli najpierw chcemy plik odczytać, zamknąć go i coś do niego zapisać), to można spokojnie zostawić to zadanie Javie – wszystkie pliki są zamykane na końcu wykonywania programu.

Analogicznie realizowany jest zapis do pliku. Klasa FileOutputStream ma metodę write(), która zapisuje bajt (podany w postaci typu int). Trzeba pamiętać, że otwarcie pliku do zapisu spowoduje wyczyszczenie zawartości tego pliku i rozpoczęcie pisania od początku. Jeśli przed otwarciem plik nie istniał, to zostanie utworzony. Poniżej znajduje się przykład zapisu stringa do pliku.

try{
   String str = "Próba zapisu";
   FileOutputStream fos = new FileOutputStream("plik_zapis.txt");
   for(int i = 0; i < str.length(); i++){
      fos.write((int)str.charAt(i));
   }
} catch(IOException ex){
   System.out.println("Błąd operacji na pliku: "+ex);
}

Java, oprócz czytania bajt po bajcie, oferuje także możliwość odczytu całej linii do stringa. Możliwe jest to dzięki tzw. strumieniom buforowanym. Otwarcie takiego strumienia jest trochę bardziej skomplikowane. Najpierw trzeba utworzyć obiekt FileReader, a następnie trzeba stworzyć obiekt BufferedReader, podając mu obiekt FileReader jako parametr. Jak to wygląda w praktyce można zobaczyć poniżej.

FileReader fr = new FileReader("plik_testowy.txt");
BufferedReader bfr = new BufferedReader(fr);

W tym momencie można już korzystać z metody readLine() klasy BufferedReader, która odczytuje całą linijkę z pliku i zwraca ją w postaci stringa. Po odczytaniu całego pliku zwracana jest wartość null (nie string „null”, tylko wartość null). Czyli wygląda to tak:

String linia = "";
try{
   FileReader fr = new FileReader("plik_testowy.txt");
   BufferedReader bfr = new BufferedReader(fr);
   while((linia = bfr.readLine()) != null){
      System.out.println(linia);
   }
} catch( IOException ex ){
   System.out.println("Błąd przy operacji na pliku: "+ex);
}

To tyle z zagadnień teoretycznych. Może teraz coś z praktyki. Zadanie jest takie: plik „wejscie.txt” zawiera dowolny tekst; podmień wszystkie wystąpienia litery ‚a’ na sekwencję „>a<" i zapisz dane wyjściowe do pliku "wyjscie.txt". Są dwie metody rozwiązania tego zadania. Można najpierw otworzyć plik wejściowy do odczytu, wyciągnąć z niego dane do stringa, następnie zapisać poprawione dane do pliku wyjściowego. Dane można poprawiać albo przy zapisywaniu danych do stringa, albo już przy wpisywaniu do pliku. Druga metoda, znacznie krótsza, to jednoczesne czytanie z pliku, analizowanie danych (tzn. podmiana 'a' na ">a<") i od razu zapis tych danych do pliku. Poniżej znajduje się gotowe rozwiązanie dla pierwszego przypadku. [code lang="java"] // Deklaracje zmiennych // nazwy plików String nazwa = "wejscie.txt"; String nazwaOut = "wyjscie.txt"; // string odczytany z pliku String str = ""; // string do zapisu String out = ""; // znak pobrany z pliku int znak = 0; try{ // nowy strumień wejściowy FileInputStream wejscie = new FileInputStream(nazwa); // wczytanie całego pliku do stringu znak = wejscie.read(); while( znak != -1 ){ str += (char) znak; znak = wejscie.read(); } wejscie.close(); }catch(IOException ex){ System.out.println("Blad zwiazany z odczytem z pliku. "+ex); } // pętla konwertująca // sprawdzany jest każdy znak stringu str for(int i = 0; i<str.length(); i++){ // jeśli bieżący znak to 'a' if(str.charAt(i) == 'a') // to wstaw poprawiony string out += ">a<"; else // jeśli nie to poprostu wstaw ten znak out += str.charAt(i); } try{ // no i teraz zapis do pliku FileOutputStream wyjscie = new FileOutputStream(nazwaOut); for(int i=0; i < out.length(); i++){ wyjscie.write( out.charAt(i) ); } wyjscie.close(); }catch(IOException ex){ System.out.println("Blad zwiazany z zapisem do pliku. "+ex); }[/code]

Autor: leafnode

Architekt oprogramowania webowego, programista, analityk bezpieczeństwa serwisów internetowych, speaker, konsultant. Potrzebujesz pomocy? Skontaktuj się ze mną!

Dodaj komentarz

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *