Методы для чтения и записи форматированных данных.

Классы DataOutputStream и DataInputStream предлагают более удобные методы для записи и чтения из потока, допускающие форматированный вывод и ввод данных.

Вот, например, какой набор методов можно использовать для записи форматированных данных в поток класса DatаOutputStream:

public final void writeBoolean(boolean v);

public final void writeByte(int v);

public final void writeBytes(String s);

public final void writeChar(int v);

public final void writeChars(String s);

public final void writeDouble(double v);

public final void writeFloat(float v);

public final void writeInt(int v);

public final void writeLong(long v);

public final void writeShort(int v);

public final void writeUTF(String str);

Хотя имена этих методов говорят сами за себя, сделаем несколько замечаний. Метод writeByte() записывает в поток 1 байт (младший байт параметра v). В отличие от метода writeByte() метод writeChar() записывает в поток двухбайтовое символьное значение в кодировке Unicode.

Если необходимо записать в выходной поток текстовую строку, то это можно сделать с помощью методов writeBytes(), writeChars() или writeUTF(). Первый метод записывает в выходной поток только младшие байты символов, второй - двухбайтовые символы в кодировке Unicode, третий предназначен для записи строки в машинно-независимой кодировке UTF-8.

Все перечисленные выше методы в случае возникновения ошибки создают исключение IOException, которое необходимо обработать.

Для чтения форматированных данных из потока класса DatаInputStream используются следующие методы:

public final boolean readBoolean();

public final byte readByte();

public final char readChar();

public final double readDouble();

public final float readFloat();

public final void readFully(byte b[]);

public final void readFully(byte b[], int off, int len);

public final int readInt();

public final String readLine();

public final long readLong();

public final short readShort();

public final int readUnsignedByte();

public final int readUnsignedShort();

public final String readUTF();

public final static String readUTF(DataInput in);

public final int skipBytes(int n);

Следует обратить внимание на то, что среди этих методов нет тех, что специально предназначены для чтения данных, записанных из строк методами writeBytes() и writeChars() класса DataOutputStream.

Тем не менее, если входной поток состоит из отдельных строк, разделенных символами возврата каретки и перевода чтроки, то такие строки можно получить методом readLine(), например:

String s="",ss;

while((ss=dataIn.readLine())!=null) s=s+ss+"\r\n";

Также можно воспользоваться методом readFully, который заполняет массив байтов. Этот массив потом нетрудно преобразовать в строку типа String, так как в классе String предусмотрен соответствующий конструктор.

Отметим также метод skipBytes(), который позволяет пропустить из входного потока заданное количество байт.

Методы класса DataInputStream, предназначенные для чтения данных, могут создавать исключения IOException и EOFException. Первое из них возникает в процессе ошибки, второе - при достижении конца входного пока в процессе чтения.

2.3.3 Закрытие потоков

Так как в системе интерпретации приложений Java есть процесс «сборки мусора», то возникает вопрос - выполняет ли он автоматическое закрытие потоков, с которыми приложение завершило работу?

Нужно сказать, процесс «сборки мусора» не делает ничего подобного. «Сборка мусора» выполняется только для объектов, размещенных в оперативной памяти. Для потоков же программисты должны предусмотреть явное закрытие, для чего используется метод close().

2.3.4 Принудительный сброс буферов

Еще один важный момент работы связан с буферизированными потоками. Уже отмечалось, что буферизация ускоряет работу приложений с потоками, так как при ее использовании сокращается количество обращений к системе ввода-вывода. Можно постепенно в течении долгого времени добавлять в буферизированный поток данные, и только потом эти данные физически записать в файл на диске.

Во многих случаях, однако, приложение должно, не отказываясь совсем от буферизации, выполнять принудительную запись буферов в файл. Это можно сделать с помощью метода flush().

2.3.5 Приложение StreamDemo

Создадим приложение, которое демонстрирует наиболее распространенный способ работы с файлами через буферизированные потоки.

Задание. Создать автономное приложение StreamDemo, в окне которого содержится область редактирования, в которой пользователь может вводить текст. Приложение должно иметь выпадающее меню «File» (пункты «Open File» и «Save File»). При выборе пункта «Open File» в область редактирования записывается содержимое файла out.txt, а при выборе пункта Save File» содержимое области редактирования записывается в файл out.txt. Для записи и чтения необходимо использовать буферизированные потоки форматированных данных.

Замечание. Исходные тексты этого приложения сохранить для их последующей модификации в процессе создания приложения FileDialogDemo (см. далее).

Методические указания.Приложение должно быть создано на основе шаблона, содержащегося в Приложении 7.

Объявление элементов класса фрейма.

В классе фрейма объявим следующие элементы - ссылки на объекты классов:

TextArea text; // ссылка на область редактирования

Конструктор класса фрейма (метод MainWndFrame()).

В конструкторе класса фрейма создадим панель меню mainMenu класса MenuBar и при помощи метода setMenuBar()установим эту панель для фрейма.

После этого создадим меню «File» - объект file класса Menuи добавим его в панель меню mainMenu методом add().

Затем в меню file введем методом add() элементы «Open File» и «Save File» - объекты класса MenuItem.

В качестве поля редактирования создадим текстовую область text класса TextArea, а затем добавим ее в центральную часть фрейма методом add().

Обработка событий (метод handleEvent()).

В этот метод добавим обработку события Event.ACTION_EVENT (сообщения от меню). Следует обратить внимание, что фрагменты кода, где будет происходить работа с файлами, должны быть заключены в блоки try-catch, что является необходимым для обработки возможных исключительных ситуаций:

case Event.ACTION_EVENT:

if(evt.target instanceof MenuItem) // события меню

{ String label=(String) evt.arg;

if(label.equals("Open File"))

{ // обработка исключения при работе с файлами

try

{ // добавить необходимые действия

}

catch(IOException e)

{ // вывод названия возникшего исключения

System.out.println(e.toString());

}

}

else if(label.equals("Save File"))

{ // обработка исключения при работе с файлами

try

{ // добавить необходимые действия

}

catch(IOException e)

{ // вывод названия возникшего исключения

System.out.println(e.toString());

}

}

else return false;

}

return true;

При выборе пользователем пункта меню «Open File» объявим переменные s и ss класса String (переменную s проинициализируем пустой строкой).

Затем создадим входной буферизированный поток форматированных данных, связанный с файлом out.txt. Для этого выполним следующие действия:

· создадим поток класса FileInputStream, связанный с файлом out.txt, и ссылку на этот поток присвоим переменной file класса FileInputStream;

· поток file, связанный с файлом, передадим конструктору класса BufferedInputStream, в результате чего образуется буферизированный поток, ссылку на который присвоим переменной buf класса BufferedInputStream;

· буферизированный поток buf передадим конструктору класса DataInputStream, который и создаст нужный поток, ссылку на который присвоим переменной dataIn класса DataInputStream.

Далее прочитаем в строку s все строки из потока dataIn при помощи метода readLine(). После считывания файла поток dataIn закроем методом close().

Содержимое строки s запишем в текстовую область, применяя метод setText() для объекта text и передавая этому методу строку s.

При выборе пользователем пункта меню «Save File» объявим переменную s класса String, в которую при помощи метод setText() для объекта text прочитаем содержимое текстовой области.

Затем создадим выходной буферизированный поток форматированных данных, связанный с файлом out.txt. Для этого выполним следующие действия:

· создадим поток класса FileOutputStream, связанный с файлом out.txt, и ссылку на этот поток присвоим переменной file класса FileOutputStream;

· поток file, связанный с файлом, передадим конструктору класса BufferedOutputStream, в результате чего образуется буферизированный поток, ссылку на который присвоим переменной buf класса BufferedOutputStream;

· буферизированный поток buf передадим конструктору класса DataOutputStream, который и создаст нужный поток, ссылку на который присвоим переменной dataOut класса DataOutputStream.

Далее запишем строку s в поток dataOut при помощи метода writeBytes(). После записи выполним принудительный сброс буфера потока dataOut методом flush() и закроем поток dataOut методом close().

2.4 Потоки в оперативной памяти

С оперативной памятью можно работать также как и с файлом (а точнее говоря, с потоком). Это очень удобно во многих случаях, в частности, файлы, отображаемые на память можно использовать для передачи данных между одновременно работающими потоками выполнения задачи. Кроме того, так как апплетам запрещено обращаться к файлам,расположенным на удаленном компьютере, на котором эти апплеты были запущены, то при необходимости создания временных потоков ввода и вывода (временных файлов) последние могут быть размещены в оперативной памяти.

В библиотеке классов Java есть три класса, специально предназначенных для создания потоков в оперативной памяти. Рассмотрим эти классы подробнее.

Класс ByteArrayOutputStream

Класс ByteArrayOutputStream создан не базе класса OutputStream. В нем имеется два конструктора:

public ByteArrayOutputStream();

public ByteArrayOutputStream(int size);

Первый конструктор создает выходной поток в оперативной памяти с начальным размером буфера, равным 32 байта. Второй позволяет указать необходимый размер буфера.

В классе ByteArrayOutputStream определено несколько достаточно полезных методов:

· public void reset() - сбрасывает счетчик байтов, записанных в выходной поток. Если данные, записанные в поток, больше не нужны, то можно вызвать этот метод и использовать затем память, выделенную для потока, для записи других данных.

· public int size() - с помощью этого метода можно определить количество байт данных, записанных в поток.

· public byte[] toByteArray() - позволяет скопировать данные, записанные в поток, в массив байт. Этот метод возвращает ссылку на созданный для этой цели массив.

· public void wtiteTo(OutputStream out) - с помощью этого метода можно скопировать содержимое данного потока в другой выходной поток, ссылка на который передается методу через параметр.

Для выполнения форматированного вывода в поток в оперативной памяти необходимо создать поток на базе класса DataOutputStream, передав соответствующему конструктору ссылку на поток класса ByteArrayOutputStream

Класс ByteArrayInputStream

С помощью класса ByteArrayInputStream можно создать входной поток на базе массива байтов, расположенного в оперативной памяти. В этом классе определено два конструктора:

public ByteArrayInputStream(byte buf[]);

public ByteArrayInputStream(byte buf[], int offset, int length);

Первый конструктор получает через единственный параметр ссылку на массив, который будет использован для создания входного потока. Второй позволяет дополнительно указать смещение offset и размер области памяти length, которая будет использована для создания потока.

Вот несколько методов, определенных в классе ByteArrayInputStream:

public int available();

public int read();

public int read(byte b[], int off, int len);

public void reset();

public long skip(long n);

Наиболее интересным является метод available(), с помощью которого можно определить, сколько байт имеется во входном потоке.

Обычно класс ByteArrayInputStream используется совместно с классом DataInputStream, что позволяет организовать форматный вывод данных.

Класс StringBufferInputStream

Класс StringBufferInputStream предназначен для создания входного потока на базе текстовой строки класса String. Ссылка на эту строку передается конструктору класса StringBufferInputStream через параметр:

public StringBufferInputStream(String s);

В классе StringBufferInputStream определены те же методы, что и в только что рассмотренном классе ByteArrayInputStream.

Для более удобной работы можно создать поток форматированных данных, для чего на базе потока класса StringBufferInputStream конструируется поток класса DataInputStream.

3 Работа с локальной файловой система

В предыдущих разделах рассматривались классы, предназначенные для чтения и записи потоков. Однако часто возникает необходимость выполнения и таких операций, как определение атрибутов файла, создание или удаление каталогов, удаление файлов, получение списка всех файлов в каталоге и т.д.

Для выполнения всех этих операций в приложениях Java используется класс с именем File.