сряда, 14 януари 2009 г.

Потоци InputStream и OutputStream в Java

Библиотеката за вход/изход (io) на Java предоставя множество класове за работа с потоци. Всички потоци, работещи с входа, се явяват подкласове на абстрактния клас InputStream, а всички, работещи с изхода – подкласове на абстрактния клас OutputStream. Единственото изключение е класът RandomAccessFile. В обект от типа RandomAccessFile може както да се пише, така и да се чете т.е. осъществява се и вход и изход.
InputStream и OutputStream осигуряват минималния интерфейс и вътрешната реализация на потока. Те поддържат методи за четене и запис на байт или последователност от байтове. Класът за четене поддъра още методи за прескачане на определен брой байтове, установяване на указателя на текуща позиция в началото на файла и др. Потоците се отварят автоматично при създаването на обект и се затварят явно с метода close() или неявно при освобождаване на заетата памет.




Методи на класа InputStream
В класа InputStream са дефинирани няколко метода, осигуряващи четене от поток данни. Тези методи са предефинирани в наследяващите класове.
1. Методът read() има три версии:
Първата е без параметри – извършва се четене на един байт и се връща цяло число в диапазона от 0 до 255. Ако в потока няма данни, се връща минус 1.
read ();

Втората е с един параметър - масив от байтове. В този случай се въвеждат определен брой байтове, който не може да е по-голям от големината на масива

(параметъра). Методът връща броя на въведените байтове или –1, ако не е въведен нито един байт.
read (byte [] <масив>);

Третата версия на метода има три параметъра. Първият е масив, в който ще се въвежда информацията, вторият е позицията в масива, от където ще започне да се въвежда, а третият е броят на символите, които трябва да се прочетат от потока.
read (byte [] <масив>, int<позиция>, int<брой>);

Пр.1
// четене на знаков низ от поток, записан в Паскал-стил

public void readPascalString(InputStream isExample, byte[] bString) {
int iNumber = isExample.read(); // вариант 1
int iRealBytes = isExample.read(bString, 0, iNumber); // вариант 2
if (iNumber != iReadBytes) {
// генериране на изключителна ситуация
// неправилен формат на входните данни
}
}

2. Методът skip(<брой>) пропуска зададен брой байтове от входния поток.

3. Методът available() връща броя на байтовете които могат да се прочетат от потока. Ако потока е закрит, връща –1.


Методи на класа OutputStream
В класа OutputStream са дефиниране няколко метода, осигуряващи запис на данни в поток. Тези методи са предефинирани в наследяващите класове.

1. Основен метод е write(). Той има един параметър във формата на цяло число. Като резултат от изпълнение на метода този байт се добавя към потока. Както и при read() се поддържат и останалите две версии – с масив и с масив и диапазон (начало и брой байтове). Техният синтаксис е както следва:
write(int <байт>);
write(byte[] <масив>);
write(byte[] <масив>, int <позиция>, int <брой>);

2. Методът flush() се използва за принудително записване на данните на носителя. Този метод е от особено значение за буферираните потоци.

Извлечени методи
От класовете InputStream и OutputStream са извлечени няколко класа за въвеждане и извеждане на байтове.
FileInputStream
FileOutputStream
За въвеждане от и извеждане на двоичен файл
ByteArrayInputStream
ByteArrayOutputStream
За въвеждане от и извеждане в/от масив в оперативната памет
PipedInputStream
PipedOutputStream
За организиране на временни файлове, които се свързват по двойки за формиране на канал между секции в една програма
SockedInputStream
SockedOutputStream
За организиране на временни файлове, които се свързват по двойки за формиране на канал между програми в мрежа
SequenceInputStream
За обединение на няколко входни потока в един
StringBufferInputStream
За въвеждане от StringBuffer



Филтриращи потоци
Филтриращите потоци са група от класове, които наследяват абстрактните класове FileInputStream и FileOutputStream. Последните път от своя страна наследяват съответно InputStream и OutputStream. Те дефинират интерфейс към филтри, които се използват автоматично за обработка на данни при тяхното въвеждане и извеждане. Отварянето на филтриращите потоци се извършва при създаване на обект от този клас. На конструктура на обекта се задава съответно обект от тип InputStream или OutputStream. Към филтриращите класове спадат следните потоци:

Потоци
Описание
DataInputStream
DataOutputStream
Съдържат съответно филтри за въвеждане и извеждане на примитивни типове в машинно-независим формат
BufferedInputStream
BufferedOutputStream
Буферират данните при въвеждане и извеждане с цел намаляване брой на обръщенията към носителя
PushbackInputStream
Входен поток с възможност за връщане на един байт
LineNumberInputStream
Поддържа бройч на редовете по време на четенето

При използване на FileIputStream и FileOutputStream се прави точно двоично копие на данните от паметта. При DataInputStream и DataOutputStream се прави двоично копие в машинно-независим формат.
В класовете DataInputStream и DataOutputStream са реализирани различни методи, позволяващи въвеждане и извеждане на примитивни типове. Тези класове са полезни за директно извежданеи въвеждане на данни. Методите и съответните данни са следните:
readBoolean/ writeBoolean – прехвърля се един байт, винаги 0 – за неистина, а за истина се счита всичко останало при четене и 1 при запис;
readByte/ writeByte – прехвърля се един байт, без да се преобразува към цял тип;
readChar/ writeChar – прехвърля се един символ (2 байта), първо се прехвърля старшият байт;
readInt/ writeInt – прехвърля се 32-битово знаково число;
readShort/ writeShort – прехвърля се 16-битово число със знак, но при четене то се преобразува до 32-битово;
readLong/ writeLong – прехвърля се 64-битово число със знак;
readFloat/ writeFloat – прехвърля се 32-битово реално число;
readDouble/ writeDouble – прехвърля се 64-битово реално число.



FileOutputStream s = new FileOutputStream(“test.dat”);
DataOutputStream f = new DataOutputStream (s); //обект от
// тип OutputStream
f.writeInt(12);
f.writeLong(50091909);
f.writeDouble(3.14159);
f.writeBytes(“Plovdiv”);


Буферирано въвеждане и извеждане
Буферираното въвеждане и извеждане позволява намаляване на броя на операциите по записване и четене на операционната система. За целта се използва вътрешен буфер. Записването се извършва, когато се изпълнят методите flush() и close() или се запълни вътрешният буфер. Четенето се извършва при отваряне на потока или изчерпване на съдържанието на вътрешния буфер.

Пр. 3
FileOutputStream s = new FileOutputStream(“test.dat”);
BufferedOutputStream b = new BufferedOutputStream(s);
… b.write(…); … // буферирано извеждане
b.close();
s.close();

Обработка на файлове с директен достъп
В езика Java се поддържа клас RandomAccessFile, който включва интерфейсите за вход и изход. При създаване на обект от този клас може да се зададе и начинът на използването му – само за четене или за четене и запис.
С методите от вида read<тип>() и write<тип>() може да се извършват операции за четене и запис на примитивни данни. С помощта на наследяване, препокриване и дефиниране на нови методи от вида read<тип>() и write<тип>() могат да се дефинират филтри за достъп.
Класът поддържа методи за директна работа с указателя на текущата позиция. Това са skipByte() – премества указателя с указан брой байтове напред, seek() - позиционира указателя на зададена позиция и getFilePointer() – връща текущата позиция на указателя. При отваряне на файла указателят на текущата позиция се позиционира в началото и при четене или запис се премества с дължината на прочетените или записани данни. С помощта на метода seek() тази позиция може да бъде променяна. Със seek(0)
указателят се позиционира в началото на файла, а с <обект>.seek(<обект>.length()) – в края на файла (ако е необходимо да се добавя във файла).

Пр.4
import java.io.*;
class RandAccessTest {
public static void main(String[] args) throws IOException{
RandomAccessFile f=new RandomAccessFile(“test.dat”, “rw”);
f.writeInt(12);
f.writeLong(50091909);
f.writeDouble(3.14159);
f.writeBytes(“Plovdiv”);
f.seek(0);
int I = f.readInt();
long l = f.readLong();
double d = f.readDouble();
String s = f.readLine();
f.close();
System,out.println(“From file ” +I+”,”+l+”,”+d+”,”+s);
System.in.read();
}
}

В този пример се имползва файл с име test.dat. Той се обработва с помощта на обект от тип RandomAccessFile. При създаване на този обект се задава името на файла и режимът на отваряне. Този режим може да бъде “r” - само за четене или “rw” – за четене и запис.
В примерната програма се записват четири типа данни, след което файлът се позиционира в началото и се прочита записаната информация. Накрая тази информация се отпечатва.

Клас File
За да се получи информация за файл или директория може да се използва класът File. В конструктура се задава името на файла при създаване на обект от този клас. С метода exists() може да се провери дали файл или директория с такова име съществува, а с isDirectory() и с isFile() може да се провери дали това е директория или файл. Методите getName() и getPath() връщат
името и пътя на файла, а неговата големина може да се получи с lenfth(). Всички aтрибути на файла или директорията могат да бъдат проверени или получени с множество от методи като canRead(), canWrite(), lastModified() и др.
Файлът може да бъде изтрит или преименуван с delete() и renameTo().

File f = new File(“test.dat”);
if (f.exists()) {
System.out.println(“File is ”+f.length()+”bytes long.”);
if (f.isDirectory()) {

}
}

Няма коментари:

Публикуване на коментар