Paweł Tokarczyk
Piotr Mlekodaj
OPERACJE I/O W JAVA
Obsługa operacji wejścia-wyjścia (we/wy, IO, input-output) wiąże
się z
szeroką tematyką pobierania danych do naszych aplikacji ze
źródeł
zewnętrznych (w tym z interakcją z użytkownikiem) i
zwracaniem
danych na zewnątrz (np. zapisywaniem do pliku).
Większość klas Java związanych z operacjami IO znajduje się w
pakiecie java.io. W Javie operacje I/O realizuje się za pomocą
strumieni.
WEJŚCIE I WYJŚCIE
Strumień oznacza tor komunikacyjny między dowolnym źródłem
danych zdolnym
do produkcji porcji danych a ich odbiornikiem
zdolnym do przyjmowania porcji danych. Na strumieniach możemy
wykonywać dwie podstawowe operacje: odczytywanie danych i
zapisywanie danych.
STRUMIENIE - PODZIAŁ
Strumienie możemy podzielić na:
• Wejściowe klasy strumieni wejściowych
• Wyjściowe
klasy strumieni wyjściowych
W Javie – ze względu na przyjęcie standardu kodowania znaków (Unikod)
– wyróżnia się również inny „atom danych” – znak Unikodu, złożony z
dwóch bajtów. Wobec tego powstają kolejne dwie rozłączne hierarchie
klas strumieniowych:
• klasy strumieni bajtowych(„atomem” operacji we-wy jest
bajt, Bytestream)
• klasy strumieni znakowych („atomem” są znaki Unikodu – 2
bajty, Characterstream).
Przy przetwarzaniu tekstów należy korzystać ze strumieni znakowych ze
względu na to, iż w trakcie czytania/pisania wykonywane są odpowiednie
operacje kodowania/dekodowania ze względu na stronę kodową właściwą
dla źródła/odbiornika
Wejście
Wyjście
Strumienie bajtowe
InputStream
OutputStream
Strumienie znakowe
Reader
Writer
HIERARCHIE KLAS
STRUMIENIOWYCH
Są to klasy abstrakcyjne, zatem bezpośrednio nie można tworzyć
obiektów
tych
klas.
Dostarczają one natomiast podstaw dla wszystkich innych klas
strumieniowych oraz paru ogólnych użytecznych (choć bardzo
podstawowych) metod.
• read(..) (bajtów, znaków) - pozwala na przeczytanie jednego bajtu ze
strumienia bajtowego lub znaku ze strumienia znakowego albo całej
porcji bajtów/znaków,
• write(...)(bajtów/znaków) - pozwala zapisywać pojedyncze bajty/znaki
lub tablice bajtów/znaków, a w przypadku strumieni znakowych również
napisy (obiekty klasy String),
• metody skip(..), mark(..), reset() ) - każdy strumień może być
traktowany jako sekwencja bajtów/znaków, czytanie i zapisywanie
zawsze dotyczy bieżącej pozycji tej sekwencji; po wykonaniu operacji
czytania lub zapisu bieżąca pozycja zwiększa się o jeden; metody
pozycjonowania pozwalają zmieniać bieżącą pozycję.
• close())- strumień zawsze należy zamknąć po zakończeniu operacji na
nim.
STRUMIENIE - BINARNE
METODY INPUTSTREAM
METODY OUTPUTSTREAM
KLASA FILE
Operacje plikowe wykonuje się za pomocą metod klasy File.
.
•
Strumień wyjściowy do zapisu:
FileOutputStream out = new FileOutputStream ("nazwa.dat„);
•
Jeżeli chcemy dopisać bajty do istniejącego już pliku:
FileOutputStream out = new FileOutputStream("nazwa.dat", true);
out.write(x); //x – int od 0 do 255
out.Close();
•
Odczyt bajtów ze strumienia i pobranie danych:
FileInputStream in = new FileInputStream("a.dat");
while(true){
int x = in.read();
if(x == -1)
break;
System.out.print(x + " ");
}
in.Close();
STRUMIENIE PLIKOWE
PRZYKŁADY W
ŚRODOWISKU ECLIPSE:
-
ByteRade
r
-
ByteWrite
r
STRUMIENIE BUFOROWANE
Buforowanie ogranicza liczbę fizycznych odwołań do urządzeń
zewnętrznych, dzięki temu, że fizyczny odczyt lub zapis dotyczy całych
porcji danych, gromadzonych w buforze (wydzielonym obszarze
pamięci). Jedno
fizyczne odwołanie wczytuje dane ze strumienia do
bufora lub zapisuje
zawartość bufora do strumienia. W naszym
programie operacje czytania lub pisania dotyczą w większości bufora
(dopóki są w nim dane lub dopóki jest miejsce na dane) i tylko
niekiedy powodują fizyczny
odczyt (gdy bufor jest pusty) lub zapis
(gdy bufor jest pełny).
FileOutputStream out = new FileOutputStream("a.dat");
FileInputStream in = new FileInputStream("a.dat");
Deklaracja bufora i łączenie ze strumieniem:
BufferedOutputStream buffOut =new
BufferedOutputStream(out);
BufferedInputStream buffIn =new BufferedInputStream(in);
Operacje wykonujemy na buforze:
buffOut.write(127);
buffOut.close();
int x = buffIn.read();
buffIn.close();
STRUMIENIE DANYCH
Strumienie danych umozliwiaja posługiwanie sie
róznymi typami danych. Filtruja one połaczone z
nimi strumienie bajtów, pozwalajac na zapis i
odczyt prostych typów danych.
Podstawowe klasy to:
DataOutputStream,
DataInputStream.
STRUMIENIE DANYCH
Tworzenie wyjściowego strumienia danych
przebiega według poniższego schematu.
Deklarujemy strumień plikowy i łączymy go z
plikiem:
FileOutputStream out =
new FileOutputStream("a.dat");
Deklarujemy bufor i łączymy go ze strumieniem:
BufferedOutputStream buffOut =
new BufferedOutputStream(out);
STRUMIENIE DANYCH
Deklarujemy strumień danych i łączymy go z
buforem:
DataOutputStream data =
new DataOutputStream(buffOut);
Zapisujemy dane do strumienia danych:
data.writeDouble(12.5);
STRUMIENIE DANYCH
Ponownie, możliwe jest wygodne skrócenie
zapisu:
DataOutputStream data =
new DataOutputStream(
new BufferedOutputStream(
new FileOutputStream("a.dat")));
METODY STRUMIENI DANYCH
ZAPIS PROSTYCH TYPÓW OD
STRUMIENIA
import java.io.*;
public class WriteStream
{
public static void main(String[] args)
{
double[] v = {0.0, 1.2, 9.1, 2.5, 3.6, 12.4, 5.5};
try
{
DataOutputStream data =
new DataOutputStream(
new BufferedOutputStream(
new FileOutputStream("C:\\a.dat")););
for(int i = 0; i < v.length; i++)
data.writeDouble(v[i]);
data.close();
}
catch(IOException exc) { exc.printStackTrace(); }
}
}
ODCZYT PROSTYCH TYPÓW OD
STRUMIENIA
import java.io.*;
public class ReadStream
{
public static void main(String[] args)
{
try
{
DataInputStream data =
new DataInputStream(
new BufferedInputStream(
new FileInputStream("C:\\a.dat")));
try
{
while(true)
{
double x = data.readDouble();
System.out.print(x + " ");
}
}
catch(EOFException exc) { data.close(); }
}
catch(IOException exc) { exc.printStackTrace(); }
}
}
ODCZYT PROSTYCH TYPÓW OD
STRUMIENIA
Użyta w powyższym przykładzie metoda
readDouble() nie zwraca żadnej wartości, która
wskazywałaby na osiągniecie końca pliku.
Wykorzystujemy wiec fakt, ze po napotkaniu
końca pliku wystąpi wyjątek IOException.
STRUMIENIE ZNAKOWE
Reader i Writer to klasy bazowe strumieni
znakowych. Służą one do przesyłania znaków
Unicode o 16-bitowych kodach.
Wszystkie znakowe operacje wejścia-wyjścia
powinny być wykonywane na strumieniach
znakowych, które poprawnie obsługują znaki
Unikodu, np. FileWriter i BufferedWriter.
STRUMIENIE ZNAKOWE
Deklarujemy strumien plikowy i łaczymy go z
plikiem:
FileWriter writer =
new FileWriter("a.dat");
Deklarujemy bufor i łaczymy go ze strumieniem:
BufferedWriter buff =
new BufferedWriter(f);
Umieszczamy dane w strumieniu buforowanym:
buff.write('A');
Zamykamy strumien buforowany:
buff.close();
KLASY STRUMIENI ZNAKOWYCH
METODY KLASY READER
METODY KLASY WRITER