Партнерка на США и Канаду по недвижимости, выплаты в крипто

  • 30% recurring commission
  • Выплаты в USDT
  • Вывод каждую неделю
  • Комиссия до 5 лет за каждого referral

Занятие 22.

2004/2005 учебный год, 3-ий курс.

Пакет java.io. Обеспечение ввода/вывода

1)  Работа с файлами и каталогами.

Запись и чтение информации не могут обходиться без файлов. Java обеспечивает все процедуры обращения с файлами классом java. io. File. Здесь есть методы создания, переименования и удаления файлов, проверки прав доступа к файлу и методы, позволяющие получить объекты, соответствующие другим формам источников данных (например, URL). Конструкторы класса файл создают объект типа File по имени в различной форме – файл не может быть безымянным. Обратите внимание, для создания объекта типа File не требуется существования файла с таким именем. Объект типа File – только Java-оболочка системных способов обращения с файлами.

Пример. Класс FileTest. java из каталога File содержит обращение почти ко всем методам класса File.

Задание 1. Напишите программу переименования файлов.

Класс File одинаково «обслуживает» и обычные файлы, и каталоги. Но метод, позволяющий выводить содержимое, применим только к объектам-каталогам (см. пример DirTest. java).

Задание 2. Напишите программу рекурсивного просмотра содержимого указанного каталога со «сдвинутым» выводом.

2)  Иерархия классов ввода/вывода.

Операции ввода и вывода данных производятся объектами типа Какой-тоStream (Какой-тоWriter, Какой-тоReader). Reader). Операции открытия файлов (стандартные потоки ввода-вывода тоже можно считать файлами) на чтение или на запись скрыты в конструкторах объектов этих классов. Даже самый беглый взгляд на перечень классов пакета java. io повергает в недоумение: подавляющее большинство классов упомянутых типов объявлено абстрактными. Однако, если понять принцип организации различных потоков данных, все становится ясным – есть классы реального ввода и есть классы-оболочки, обеспечивающие дополнительные сервисы при обмене. Попробуем разобраться в этом множестве.

НЕ нашли? Не то? Что вы ищете?

Прежде всего, классы этого пакета нужно систематизировать по двум парам характеристик:

·  класс обеспечивает ввод или вывод информации;

·  класс поддерживает байтовый или символьный обмен.

В названиях классов, поддерживающих байтовый обмен, всегда присутствует слово Stream – поток; направление движения информации для них указывается словами Input или Output. Символьный обмен поддерживается классами с названиями Reader или Writer. То есть, класс с названием InputStream должен содержать методы, обеспечивающие чтение информации в виде байтов, а класс с названием FileWriter обеспечивает запись символьных данных в файл. Таким образом, иерархия классов ввода распадается на две группы:

группа Readers во главе с абстрактным классом Reader

и группа InputStreams, наследующая абстрактный класс InputStream

Для классов выводы картина аналогичная:

группа Writers с классом Writer во главе

и группа OutputStreams с классом OutputStream

Неконструируемые классы – абстрактные или не имеющие доступного конструктора –

размещены в диаграммах на белых табличках. То есть, порождающие классы всех четырех иерархий абстрактные; их прямое конструирование невозможно:

InputStream is=new InputStream(); !Ошибка, класс InputStream – абстрактный.

Сконструировать можно только объекты классов, представляющие источники (или приемники) реальных данных. Реальные данные могут находиться

·  в файле (классы FileInputStream, FileOutputStream, FileReader, FileWriter)

·  в оперативной памяти (классы ByteArrayInputStream, ByteArrayOutputStream, CharArrayReader, CharArrayWriter, StringReader, StringWriter, StringBufferInputStream)

·  в сети (классы URL и Socket пакета java.net).

Поэтому сначала необходимо создать объект-источник (приемник) реальных данных, например FileInputStream:

String filename=”file. txt”;

FileInputStream fis=new FileInputStream(filename);

Затем преобразовать его к типу базового класса иерархии[1]:

InputStream is=(InputStream) fis;

И только потом использовать полученный экземпляр для создания на его основе желаемого класса ввода с требуемым сервисом. Например, если необходимо осуществлять прокрутку данных в потоке «назад» для повторного считывания, потребуется создать объект типа PushbackInputStream, который конструируется из объекта типа InputStream:

PushbackInputStream pbs=new PushbackInputStream(is);

Без промежуточных ссылок можно обойтись:

PushbackInputStream pbs =new PushbackInputStream(

(InputStream)newFileInputStream(filename));

3)  Организация ввода/вывода.

Схема чтения и записи информации очень проста:

Чтение

Запись

1)  создать поток

2)  пока информация еще есть, читать

3)  закрыть поток

1)  создать поток

2)  пока информация еще есть, писать

3)  закрыть поток

Необходимо учитывать (и понимать), какой тип данных и как будет считываться и записываться. Рассмотрим следующие примеры.

o  Считывание и запись байтов без буферизации (копирование файлов произвольного содержания):

o  Считывание и запись символов (копирование текстовых файлов в символах UNICODE)

o  Считывание и запись строк (считывание строк с консольного ввода с последующим сохранением в оперативной памяти)

Примеры самого простого использования байтовых и символьных потоков приведены в классах SimpleByteInput и SimpleCharInput.. Класс SimpleByteInput открывает входной поток байтов из файла, считывает по одному байту из этого потока и записывает каждый байт в выходной поток байтов, направленный в новый файл:

import java. io.*;

class SimpleByteInput{

public static void main(String[] args){

if(args. length > 0)

try{

File old=new File(args[0]);

InputStream is=new FileInputStream(old);

OutputStreamWriter os=new OutputStreamWriter(System. out);

int c;

while( (c=is. read()) != -1 ){

System. out. print(" "+ c);

os. write(c);

}

System. out. println("\n The end of bytes.");

is. close(); os. close();

}catch(IOException ie){ ie. printStackTrace(); }

}

}

Класс SimpleCharInput выполняет все те же действия, используя операции записи и чтения символов:

import java. io.*;

class SimpleCharInput{

public static void main(String[] args){

if(args. length > 0)

try{

File old=new File(args[0]);

Reader is=new FileReader(old);

Writer os=new PrintWriter(System. out);

int c;

while( (c=is. read()) !=-1){

System. out. print(" "+ c);

os. write(c);

}

System. out. println("\n The End of chars.");

is. close(); os. close();

}catch(IOException ie){ ie. printStackTrace(); }

}

}

Обратите внимание на то, что и байты, и символы UNICODE считываются как целые.

Задание 3. Откомпилируйте оба класса и используйте полученные программы для чтения файлов с русским и английским текстом. Обратите внимание на результаты промежуточной печати считанных данных. Объясните результаты. Попытайтесь исправить программы так, чтобы вывод данных на экран производился корректно.

Задание 4. Напишите программу копирования файлов.

4)  Потоки-оболочки.

Возможности базовых классов ввода/вывода ограничены поэлементными и буферными операциями обмена; для получения дополнительного сервиса нужно использовать соответствующе классы-оболочки:

Классы

Сервис

BufferedInputStream, BufferedOutputStrem

BufferedReader, BufferedWriter

Буферизованный ввод-вывод: сокращает число обращений к физическому устройству, что повышает производительность обмена.

DataInputStream, DataOutputStream

Содержат методы чтения/записи данных примитивных типов.

InputStreamReader, OutputStreamWriter

«Мост» между байтовыми и символьными потоками.

LineNumberReader

Превращает поток одиночных символов в поток нумерованных строк.

ObjectInputStream, ObjectOutputStream

Специализированные потоки объектного ввода-вывода.

PipedInputStream, PipedOutputStream

PipedReader, PipedWriter

«Конвейерные» потоки для передачи данных между подпроцессами.

PrintStream, PrintWriter

Содержат методы для удобного вывода данных в виде символов.

PushbackInputStream, PushbackReader

Содержат методы, позволяющие «затолкать прочитанное обратно»

SequenceInputStream

Организует последовательное чтение (катенацию) нескольких входных потоков.

FilterInputStream, FilterOutputStream

FilterReader, FilterWriter

Классы-заготовки для реализации фильтрующего обмена

Рассмотрим применение оболочек на примере программы считывания консольного ввода. Консольный ввод для Java нетипичен, но с точки зрения использования классов-оболочек показателен: чтение стандартного ввода обеспечивается устройством System. in, которое представляет собой InputStream. InputStream читает байты по одному, а пользователь вводит строки символов. «Оборачивание» стандартного ввода классами InputStreamReader и LineNumberReader позволяет читать символьные строки консольного ввода без утомительного анализа системной кодировки и поиска в потоке байтов перевода строки.

import java. io.*;

class ConsoleInput{

public static void main(String[] args){

try{

LineNumberReader lnr=

new LineNumberReader(new InputStreamReader(System. in));

String line;

while(true){

line=lnr. readLine();

System. out. println("Line "+lnr. getLineNumber()+":"+line);

}

}catch(IOException ie){ie. printStackTrace();}

}

}

LineNumberReader позволяет еще и нумеровать считанные строки.

Построчный ввод текста гарантировано потребуется при чтении любого текстового файла.

Задание 5. Напишите программу-диалог, задающую 3 произвольных вопроса пользователю и записывающую протокол опроса в файл; пустые ответы опрашиваемого не допускаются.

Класс InputStreamReader преобразует байты в символы с заданной или определенной по умолчанию кодировкой; класс OutputStreamWriter выполняет обратное действие, преобразуя символы в байты. Используя эти классы, можно написать программу перекодирования текстовых файлов:

import java. io.*;

class Recoder {

public static void main(String[] args){

if(args. length == 3)

try{

File old_file = new File(args[0]);

InputStreamReader is = new InputStreamReader(new FileInputStream(old_file),args[1]);

String enc=args[2];

OutputStreamWriter os = new OutputStreamWriter(System. out, enc);

int c=0;

while( (c=is. read()) !=-1 ) os. write(c);

is. close();

os. close();

}catch(Exception e) {System. out. println("Something is wrong..2");}

else

System. out. println(“Usage: java Recoder file_name encoding_from encoding_to”);

}

}

Классы типа Piped используются для конвейерной передачи данных от одного подпроцесса другому. Следующая программа подбирает рифмующиеся английские слова из заданного алфавитного списка в три этапа:

1.  слова переворачиваются

2.  сортируются

3.  снова переворачиваются

Таким образом, получается, что слова, имеющие похожие окончания оказываются рядом. Результаты операций передаются по потоку-конвейеру:

import java. io.*;

public class RhymingWords {

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

//Открывается FileReader на файл с исходным списком слов:

FileReader words = new FileReader("words. txt");

//Выполняются обращения и сортировка

Reader rhymedWords = reverse(sort(reverse(words)));

//Вывод будет буферизован

BufferedReader in = new BufferedReader(rhymedWords);

String input;

//Осталось только прочитать полученное на выходе конвейера:

while ((input = in. readLine()) != null)

System. out. println(input);

in. close();

}

//Обращение слов выполняется отдельным потоком ввода:

public static Reader reverse(Reader source) throws IOException {

BufferedReader in = new BufferedReader(source);

//Создаем PipedWriter

PipedWriter pipeOut = new PipedWriter();

//Присоединяем к нему PipedReader

PipedReader pipeIn = new PipedReader(pipeOut);

//Результат будем записывать сюда

PrintWriter out = new PrintWriter(pipeOut);

//Стартуем обращающий поток

new ReverseThread(out, in).start();

//Возвращаем PipedReader

return pipeIn;

}

//Другой поток занимается сортировкой

public static Reader sort(Reader source) throws IOException {

BufferedReader in = new BufferedReader(source);

PipedWriter pipeOut = new PipedWriter();

PipedReader pipeIn = new PipedReader(pipeOut);

PrintWriter out = new PrintWriter(pipeOut);

new SortThread(out, in).start();

return pipeIn;

}

}

Классы обращения и сортировки расширяют класс Thread:

import java. io.*;

public class ReverseThread extends Thread {

private PrintWriter out = null;

private BufferedReader in = null;

public ReverseThread(PrintWriter out, BufferedReader in) {

this. out = out;

this. in = in;

}

public void run() {

if (out!= null && in!= null) {

try {

String input;

while ((input = in. readLine()) != null) {

out. println(reverseIt(input));

out. flush();

}

out. close();

} catch (IOException e) {

System. err. println("ReverseThread run: " + e);

}

}

}

private String reverseIt(String source) {

int i, len = source. length();

StringBuffer dest = new StringBuffer(len);

for (i = (len - 1); i >= 0; i--)

dest. append(source. charAt(i));

return dest. toString();

}

}

import java. io.*;

public class SortThread extends Thread {

private PrintWriter out = null;

private BufferedReader in = null;

public SortThread(PrintWriter out, BufferedReader in) {

this. out = out;

this. in = in;

}

public void run() {

int MAXWORDS = 50;

if (out!= null && in!= null) {

try {

String[] listOfWords = new String[MAXWORDS];

int numwords = 0;

while ((listOfWords[numwords] = in. readLine()) != null)

numwords++;

quicksort(listOfWords, 0, numwords-1);

for (int i = 0; i < numwords; i++)

out. println(listOfWords[i]);

out. close();

} catch (IOException e) {

System. err. println("SortThread run: " + e);

}

}

}

private static void quicksort(String[] a, int lo0, int hi0) {

int lo = lo0;

int hi = hi0;

if (lo >= hi)

return;

String mid = a[(lo + hi) / 2];

while (lo < hi) {

while (lo<hi && a[lo].compareTo(mid) < 0)

lo++;

while (lo<hi && a[hi].compareTo(mid) > 0)

hi--;

if (lo < hi) {

String T = a[lo];

a[lo] = a[hi];

a[hi] = T;

lo++;

hi--;

}

}

if (hi < lo) {

int T = hi;

hi = lo;

lo = T;

}

quicksort(a, lo0, lo);

quicksort(a, lo == lo0 ? lo+1 : lo, hi0);

}

}

Задание 6. Попробуйте применить программу поиска рифм для русских слов.

5)  Сериализация объектов.

Классы-оболочки ObjectInputStream и ObjectOutputStream позволяют записывать и считывать объекты - например, в файл и из файла. Сохранение объектов в файле или в других приемниках данных называется сериализацией объекта. Обратный процесс – восстановление объектов по сериализованным экземплярам называется десериализацией. Процесс сериализации весьма не прост и содержит множество непредсказуемых подпроцессов. Как правило, объект содержит ссылки на другие объекты, и не все из них могут быть сериализованы. Для примера мы опишем очень простой класс, хранящий одну строку текста и возвращающий ее методом getText( ). Чтобы объекты класса поддавались сериализации, класс должен имплементировать номинальный интерфейс Serializable:

import java. io.*;

public class SerialText implements Serializable{

private String text;

public SerialText(String text){

this. text=text;

}

public String getText(){ return text; }

public static void main(String[] args){

try{

FileOutputStream fs=new FileOutputStream("text. ser");

ObjectOutputStream os=new ObjectOutputStream(fs);

SerialText t=new SerialText("We shall overcome!");

os. writeObject((Object) t);

os. close();

}catch(IOException io){ io. printStackTrace(); }

}

}

Метод main этого класса создает объект типа SerialText и записывает его в файл text. ser. Следующий класс использует сериализованный объект для извлечения из него текста для метки:

import java. awt.*;

import java. awt. event.*;

import java. io.*;

public class SerialDemo extends Frame{

public SerialDemo(String name){

super(name);

}

public static void main(String[] args){

SerialDemo frame=new SerialDemo("SerialTextDemo");

frame. setSize(300,275);

frame. setLocation(300,300);

frame. setBackground(Color. yellow);

frame. setForeground(Color. red);

Label label=new Label();

Font font=new Font("Courier",Font. BOLD,14);

label. setFont(font);

File sertext=new File("text. ser");

if(sertext. exists())

try{

ObjectInputStream ois=

new ObjectInputStream(new FileInputStream(sertext));

SerialText st=(SerialText) ois. readObject();

label. setText(st. getText());

ois. close();

}catch(Exception e) { label. setText("No serialized text found."); }

frame. add(label);

frame. show();

frame. addWindowListener(new WindowAdapter(){

public void windowClosing(WindowEvent we){

System. exit(0);

}

});

}

}

Задание 7. Напишите программу, сериализующую и десериализующие объекты более сложного типа. Поля объекта, не подлежащие сериализации (заведомо исключаемые из процесса сериализации) помечаются модификатором transient.

6)  Файл произвольного доступа.

Источники данных, рассмотренные выше, реализуют различные нюансы последовательного обмена данных. Принципиально другой способ работы с данными – обмен с произвольным доступом к данным – обеспечивает класс RandomAccessFile. Этот класс замечателен тем, что включает все способы чтения и записи (интерфейс DataOutput) и полностью независим от всех остальных классов пакета. Произвольность доступа - возможность выполнения чтения и записи в любом месте файла – обеспечивается позиционирующим курсором. Чтение или запись производятся начиная с позиции курсора. В процессе операции курсор смещается на число прочитанных или записанных байтов. Метод getFilePointer() возвращает текущее положение курсора в файле, так что его можно отслеживать. Курсор может перемещаться по файлу без чтения или записи (метод seek(long pos)). Если позиция находится за пределами файла, ошибки или выброса исключения не происходит. Длина файла изменяется либо после записи с новой позиции, либо после применения метода setLength(long newLength). В последнем случае часть файла между старым концом файла и значением вновь установленной длины оказывается неопределенной. Пример аккуратного дополнения файла произвольного доступа приведен в классе RAFDemo. java:

import java. io.*;

class RAFDemo{

public static void main(String[] args){

if( args. length > 0 ) {

File file=new File(args[0]);

try{

RandomAccessFile raf=new RandomAccessFile(file,"rw");

String line=new String("Added text.");

long pos=raf. length();

raf. setLength(pos+line. length());

raf. seek(pos);

raf. writeBytes(line);

raf. close();

}catch(Exception e){ e. printStackTrace(); }

}

}

}

Задание 8. Запишите строку текста в начало файла произвольного доступа, перед концом файла и за его концом, оставив «прореху» в файле. Проанализируйте результаты.

[1] Необязательно.