CodeGym /Blog Java /Random-PL /Wejście/wyjście w Javie. Klasy FileInputStream, FileOutpu...
Autor
Alex Vypirailenko
Java Developer at Toshiba Global Commerce Solutions

Wejście/wyjście w Javie. Klasy FileInputStream, FileOutputStream i BufferedInputStream

Opublikowano w grupie Random-PL
"Cześć! W dzisiejszej lekcji będziemy kontynuować rozmowę o strumieniach wejściowych i wyjściowych w Javie ( Java I/O ). To nie pierwsza lekcja na ten temat i na pewno nie ostatnia :) Wejście/wyjście w Javie.  Klasy FileInputStream, FileOutputStream i BufferedInputStream — 1Jak to zdarza się, że język Java zapewnia wiele sposobów pracy z operacjami wejścia/wyjścia.Istnieje sporo klas, które implementują tę funkcjonalność, więc podzieliliśmy je na kilka lekcji — aby nie pogubić się od samego początku :) W przeszłości lekcje, o których mówiliśmy BufferedReader, a także InputStreamklasy OutputStreamabstrakcyjne i kilka potomków.Dzisiaj rozważymy 3 nowe klasy: FileInputStream,  FileOutputStream, i  BufferedInputStream.

Klasa FileOutputStream

Głównym celem klasy FileOutputStreamjest zapisywanie bajtów do pliku. Nic skomplikowanego :) FileOutputStreamto jedna z implementacji OutputStreamklasy abstrakcyjnej. W konstruktorze obiekty tej klasy pobierają albo ścieżkę do pliku docelowego (gdzie mają być zapisane bajty), albo obiekt File. Przeanalizujemy przykłady każdego z nich:

public class Main { 

   public static void main(String[] args) throws IOException { 

       File file = new File("C:\\Users\\Username\\Desktop\\test.txt"); 
       FileOutputStream fileOutputStream = new FileOutputStream(file); 

       String greetings = "Hi! Welcome to CodeGym — The best site for would-be programmers!"; 

       fileOutputStream.write(greetings.getBytes()); 
       fileOutputStream.close(); 
   } 
}
Podczas tworzenia Fileobiektu przekazaliśmy konstruktorowi pożądaną ścieżkę. Nie musimy go wcześniej tworzyć: jeśli nie istnieje, program go utworzy. Możesz także obejść się bez tworzenia dodatkowego obiektu, po prostu przekazując ciąg znaków ze ścieżką:

public class Main { 

    public static void main(String[] args) throws IOException { 

       FileOutputStream fileOutputStream = new FileOutputStream("C:\\Users\\Username\\Desktop\\test.txt"); 
       String greetings = "Hi! Welcome to CodeGym — The best site for would-be programmers!"; 

       fileOutputStream.write(greetings.getBytes()); 
       fileOutputStream.close(); 
   } 
} 
Wynik w obu przypadkach będzie taki sam. Możemy otworzyć nasz plik i zobaczyć tam:

Hi! Welcome to CodeGym — The best site for would-be programmers!
Ale jest tu jeden niuans. Spróbuj uruchomić kod z powyższego przykładu kilka razy z rzędu. Następnie zajrzyj do pliku i odpowiedz na pytanie: ile ma wierszy? Tylko jeden. Ale uruchomiłeś kod kilka razy. Okazuje się, że dane są za każdym razem nadpisywane — stare jest zastępowane przez nowe. Co zrobić, jeśli nam to nie odpowiada i musimy pisać sekwencyjnie do pliku? Co zrobić, jeśli chcemy trzy razy z rzędu napisać nasze powitanie do pliku? To wszystko jest bardzo proste. Ponieważ język nie może wiedzieć, jakiego zachowania potrzebujemy w każdym przypadku, konstruktor FileOutputStreammoże przyjąć dodatkowy parametr —boolean append. Jeśli jego wartość to prawda, dane zostaną zapisane na końcu pliku. Jeśli jest fałszywa (a domyślnie jest fałszywa), wszelkie stare dane zostaną usunięte i zastąpione nowymi. Sprawdźmy to, uruchamiając nasz zmodyfikowany kod trzy razy:

public class Main { 

   public static void main(String[] args) throws IOException { 

       FileOutputStream fileOutputStream = new FileOutputStream("C:\\Users\\Username\\Desktop\\test.txt", true); 
       String greetings = "Hi! Welcome to CodeGym — The best site for would-be programmers!\r\n"; 

       fileOutputStream.write(greetings.getBytes()); 
       fileOutputStream.close(); 
   } 
} 
Zawartość pliku:

Hi! Welcome to CodeGym — The best site for would-be programmers! 
Hi! Welcome to CodeGym — The best site for would-be programmers! 
Hi! Welcome to CodeGym — The best site for would-be programmers!
Teraz jest inaczej! Nie zapomnij o tej funkcji podczas korzystania z klas I/O. Był czas, kiedy spędzałem godziny nad zadaniami, godzinami łamiąc sobie głowę, próbując zrozumieć, w jaki sposób moje dane znikają z plików :) I oczywiście, podobnie jak w przypadku innych klas I/O, nie zapomnij użyć close()metody uwolnić zasoby.

Klasa FileInputStream

Ma FileInputStreamodwrotny cel — odczytywanie bajtów z pliku. Podobnie jak FileOutputStreamdziedziczenie OutputStream, ta klasa wywodzi się z InputStreamklasy abstrakcyjnej. Napiszemy kilka wierszy tekstu w naszym pliku „ test.txt ”:

"So close no matter how far 
Couldn't be much more from the heart 
Forever trusting who we are 
And nothing else matters"
Wejście/wyjście w Javie.  Klasy FileInputStream, FileOutputStream i BufferedInputStream — 2Oto, jak wygląda odczyt danych z pliku za pomocą FileInputStream:

public class Main { 

   public static void main(String[] args) throws IOException { 

       FileInputStream fileInputStream = new FileInputStream("C:\\Users\\Username\\Desktop\\test.txt"); 

       int i; 

       while((i=fileInputStream.read())!= -1){ 

           System.out.print((char)i); 

       } 
   } 
}
Odczytujemy jeden bajt z pliku, konwertujemy odczytane bajty na znaki i wyświetlamy je na konsoli. A oto wyjście konsoli:

So close no matter how far 
Couldn't be much more from the heart 
Forever trusting who we are 
And nothing else matters

Klasa BufferedInputStream

Myślę, że biorąc pod uwagę wiedzę z poprzednich lekcji, można łatwo powiedzieć, po co nam ta BufferedInputStreamklasa i jakie ma zalety w porównaniu FileInputStream:) Ze strumieniami buforowanymi już się zetknęliśmy, więc spróbuj zgadnąć (lub zapamiętać) zanim przeczytasz dalej :) Buforowane strumienie są potrzebne głównie do optymalizacji wejścia/wyjścia. Dostęp do źródła danych, taki jak odczyt z pliku, jest kosztowną operacją pod względem wydajności, a dostęp do pliku w celu odczytania każdego bajtu jest marnotrawstwem. Dlatego BufferedInputStreamodczytuje dane nie bajt po bajcie, ale w blokach i tymczasowo przechowuje je w specjalnym buforze. To pozwala nam zoptymalizować program, zmniejszając liczbę dostępów do pliku. Zobaczmy, jak to wygląda:

public class Main { 

   public static void main(String[] args) throws IOException { 

       FileInputStream fileInputStream = new FileInputStream("C:\\Users\\Username\\Desktop\\test.txt"); 

       BufferedInputStream bufferedInputStream = new BufferedInputStream(fileInputStream, 200); 

       int i; 

       while((i = bufferedInputStream.read())!= -1){ 

           System.out.print((char)i); 
       } 
   } 
} 
Tutaj stworzyliśmy BufferedInputStreamobiekt. Jego konstruktor bierze instancję klasy InputStreamlub któregokolwiek z jej potomków, więc to FileInputStreamzrobi. Jako dodatkowy argument przyjmuje rozmiar bufora w bajtach. Dzięki temu argumentowi dane będą teraz odczytywane z pliku nie po jednym bajcie na raz, ale po 200 bajtów na raz! Wyobraź sobie, jak bardzo zmniejszyliśmy liczbę dostępów do plików. Aby porównać wydajność, możesz wziąć duży plik tekstowy (kilka megabajtów tekstu) i porównać, ile czasu zajmuje w milisekundach odczytanie i wyprowadzenie na konsolę za pomocą FileInputStreami BufferedInputStream. Oto kod demonstrujący obie opcje:

public class Main { 

   public static void main(String[] args) throws IOException { 

       Date date = new Date(); 

       FileInputStream fileInputStream = new FileInputStream("C:\\Users\\Username\\Desktop\\textBook.rtf"); 
       BufferedInputStream bufferedInputStream = new BufferedInputStream(fileInputStream); 

       int i; 
       while((i = bufferedInputStream.read())!= -1){ 

           System.out.print((char)i); 
       } 

       Date date1 = new Date(); 
       System.out.println((date1.getTime() - date.getTime())); 
   } 
} 

 
public class Main { 

   public static void main(String[] args) throws IOException { 

       Date date = new Date(); 
       FileInputStream fileInputStream = new FileInputStream("C:\\Users\\Username\\Desktop\\26951280.rtf"); 

       int i; 
       while((i = fileInputStream.read())!= -1){ 

           System.out.print((char)i); 
       } 


       Date date1 = new Date(); 
       System.out.println((date1.getTime() - date.getTime())); 
   }
}
Czytając plik 1,5 MB na moim komputerze, FileInputStreamukończyłem pracę w ~ 3500 milisekund, ale BufferedInputStreamzarządzałem nią w ~ 1700 milisekund. Jak widać, buforowany strumień zoptymalizował pracę, skracając ją o połowę! :) Będziemy kontynuować naukę klas I/O — do zobaczenia!
Komentarze
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION