Символьные потоки Char Array
Поможем в ✍️ написании учебной работы
Поможем с курсовой, контрольной, дипломной, рефератом, отчетом по практике, научно-исследовательской и любой другой работой

Символьные потоки семейства CharArray (CharArrayReader и CharArrayWriter) аналогичны по назначению потокам ByteArray – они позволяют в качестве источника или получателя данных использовать массивы типа char, размещённые в памяти. Конструкторы класса Char Array Reader , принимающие в качестве параметров массивы типа char , описаны ниже:

· public Char Array Reader ( char [] buf , int offset , int length )
Создаёт объект потока Char Array Reader на основе части заданного массива символов buf , начиная с элемента buf [ offset ] и заканчивая buf [ offset + length -1] либо последним элементом массива, в зависимости от того, какой из двух будет достигнут раньше. Массив используется непосредственно (он не копируется), поэтому в процессе ввода данные изменять не следует.

· public Char Array Reader ( char [] buf )
Конструктор аналогичен предыдущему при условии Char Array Reader ( buf , 0, buf . length )

Ввод данных из потока Char Array Reader никогда не блокируется.

В составе класса Char Array Writer предусмотрены средства динамического наращивания объёма массива типа char , получающего выводимые данные. Перечислим конструкторы и методы класса.

· public Char Array Writer ()
Создаёт объект потока Char Array Writer , предусматривающий вывод данных в массив типа char с размером, предусмотренным по умолчанию.

· public Char Array Writer ( int size )
Создаёт объект потока Char Array Writer , предусматривающий вывод данных в массив типа char заданного размера size .

· public char [] toCharArray ()
Возвращает копию массива символов, ранее выведенных потоком

· public int size ()
Возвращает значение количества символов, ранее выведенных потоком.

· public void reset ()
Очищает буфер – массив потока, позволяя использовать его заново.

· public String toString ()
Возвращает текущее содержимое буфера массива в виде объекта типа String .

· public void writeTo ( OutputStream out ) throws IOException
Выводит содержимое массива – буфера в заданный поток out .

Если Char Array Writer используется в качестве получателя данных, выводимых из «внешних» фильтрованных потоков, прежде чем обратиться к методу to Char Array, следует выполнить операцию сброса (flush) фильтрованных потоков.










Символьные потоки String

Объект класса StringReader позволяет считывать символы из строки String и никогда не блокируется. Единственный конструктор класса в качестве параметра принимает ссылку на объект String, содержимое которого подлежит чтению.

Объект класса StringWriter позволяет записывать символы в буфер, который может интерпретироваться как объект типа String или StringBuffer. Перечислим конструкторы и методы класса:

· public StringWriter ()
Создаёт объект потока StringWriter , предусматривающий вывод данных в буфер с размером, предусмотренным по умолчанию.

· public StringWriter ( int size )
Создаёт объект потока StringWriter , предусматривающий вывод данных в буфер заданного размера size . Удачный выбор размера буфера во многих случаях способен привести к существенному росту производительности программы.

· public StringBuffer getBuffer ()
Возвращает буфер данных, фактически используемый потоком, в виде объекта StringBuffer . Поскольку возвращается именно рабочий буфер, а не его копия, в процессе вывода данных изменять его напрямую не следует.

· public String toString ()
Возвращает текущее содержимое буфера потока в виде объекта String .





Потоки Print

Классы семейства Print – PrintStream и PrintWriter - содержат объявление ряда методов, которые упрощают задачу вывода (записи) в поток значений простых типов и объектов в удобочитаемом текстовом формате. В потоковых классах Print реализованы перегруженные версии методов print и println для вывода следующих значений типов – char, char[], int, long, float, double, Object, String, boolean. Эти методы гораздо удобнее для применения, нежели обычные методы потокового вывода (записи) write. Метод println добавляет в конец порции выводимых данных, переданной в виде параметра, признак завершения строки; инструкция println без параметров просто завершает текущую строку. Содержимое признака завершения строки определяется значением системного свойства line . separator , которое не обязательно должно ограничиваться единственным символом (например, \ n ).

Каждый из потоков Print действует как поток Filter и поэтому в процессе вывода данные могут быть подвержены дополнительной фильтрации.

Класс PrintStream работает с байтовыми потоками, а PrintWriter – с символьными. Поскольку чаще возникает потребность в записи символов, в обычных ситуациях следует пользоваться средствами класса PrintWriter. Исторически сложилось так, что стандартные потоки System.out и Sustem.err, воспринимающие байты как символы из набора Latin-1, относятся к классу PrintStream, собственно говоря, это единственные потоки PrintStream, которые уместно применять на практике. Мы рассмотрим только класс PrintWriter , так как PrintStream реализует почти аналогичный интерфейс. В составе класса PrintWriter имеется четыре конструктора, которые описаны ниже:

· public PrintWriter ( Writer out , boolean autoflush )
Создаёт объект класса PrintWriter , осуществляющий запись данных в заданный поток out . Если значение флага autoflush равно true , при обращении к println вызывается метод flush . В противном случае println не получает преимуществ перед другими методами, и сброс потока с помощью flush не производится. После создания потока свойство, связанное с автоматическим вызовом flush , изменено быть не может.

· public PrintWriter ( Writer out )
Конструктор аналогичен предыдущему при условии public PrintWriter ( Writer out , false ).

· public PrintWriter ( OutputStream out , boolean autoflush )
Конструктор аналогичен первому при условии PrintWriter ( new OutputStreamWriter ( out ), autoflush ).

· public PrintWriter(OutputStream out)
Конструктор аналогичен предыдущему при условии PrintWriter(new OutputStreamWriter(out), false).

Одна из важных характеристик поведения потоков Print связана с тем, что ни один из реализованных в них методов вывода (записи) не выбрасывает исключения типа IOException. Если при передаче данных «внутреннему» потоку возникает ошибка, методы завершают выполнение нормальным образом. Проверить наличие ошибки можно с помощью вызова метода checkError , возвращающего результат типа boolean ; метод осуществляет сброс ( flush ) потока и проверяет его состояние. Если произошла ошибка, средств очистки состояния потока не существует. Если любая из операций, выполняемых «внутренним» потоком, вызвала появление исключения InterruptedIOException , состояние ошибки не регистрируется, но вместо этого текущий поток вычислений заново получает уведомление о прерывании посредством Thread . currentThread (). intgerrupt ().





Класс StreamTokenizer

Задачи лексического анализа потока данных относятся к числу традиционных и в составе пакета java.io представлен класс StreamTokenizer, позволяющий решать некоторые из них. Поток разбивается на лексемы с помощью объекта StreamTokenizer, конструктор которого принимает в качестве параметра объект типа Reader, выполняющий функцию источника данных; объект StreamTokenizer действует в соответствии с заданными параметрами сканирования данных. На каждой итерации цикла сканирования вызывается метод nextToken, который возвращает очередную считанную из потока лексему и информацию о её типе, присваивая эти данные полям объекта StreamTokenizer.

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

Когда метод nextToken распознаёт лексему, он возвращает её тип в виде значения и присваивает последнее полю ttype. Существует четыре типа лексем, воспринимаемых методом nextToken:

· TT_WORD; обнаружено слово; оно сохраняется в поле sval типа String;

· TT_NUMBER; обнаружено число; оно сохраняется в поле nval типа double; распознаются только десятичные числа с плавающей запятой (с десятичной точкой или без таковой) в нормальной нотации (анализатор не распознает ни 3.4е79 как число с плавающей запятой, ни 0xffff как шестнадцатеричное число);

· TT_EOL; обнаружен признак завершения строки;

· TT_EOF; достигнут конец файла.

Подразумевается, что текст, подлежащий анализу, состоит из байтов, значение которых относятся к диапазону от \u0000 до \u00FF, - корректность распознавания символов Unicode, не принадлежащих указанному интервалу, не гарантируется. Поток ввода включает специальные и обычные символы. К категории специальных относятся те символы, которые анализатор трактует специальным образом, а именно: символы пробела, символы, обозначающие числа и слова, и т.д. Любой другой символ воспринимается как обычный. Когда очередным символом потока является обычный символ, в качестве его типа возвращается значение этого символа.

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

Пример 37.

static double sumStream(Reader in) throws IOException {

StreamTokenizer nums = new StreamTokenizer(in);

double result = 0.0;

while (nums.nextToken() != StreamTokenizer.TT_EOF) {

     if (nums.ttype == StreamTokenizer.TT_NUMBER)

          result += nums.nval;

}

return result;

}

Вначале на основе потока in типа Reader создаётся объект типа StreamTokenizer, а затем на каждом шаге цикла считывается очередная лексема; если лексема представляет собой число, его значение прибавляется к содержимому переменной result . По достижении конца потока значение result возвращается в код-инициатор.

Байтовые потоки Data

Технологии обработки символьной информации находят самое широкое применение, но достаточно часто возникают и задачи трансляции бинарных данных конкретных типов. В интерфейсах DataInput и DataOutput определены методы потокового ввода-вывода данных простых типов, а классы DataInputStream и DataOutputStream обеспечивают реализацию интерфейсов, предлагаемую по умолчанию. Вначале мы рассмотрим интерфейсы, а затем их реализации.

DataInput и DataOutput

Интерфейсы, предусматривающие потоковый ввод и вывод бинарных данных, обладают почти одинаковой структурой. Ниже перечислены методы семейств read и write, обеспечивающих ввод и вывод данных каждого из простых типов:

readBoolean (writeBoolean), readChar (writeChar), readByte (writeByte), readShort (writeShort), readInt (writeInt), readLong (writeLong), readFloat (writeFloat), readDouble (writeDouble), readUTF (writeUTF).

readUTF (writeUTF) – чтение (запись) строк (String) в формате UTF (Unicode Tranceformation Format) – формат преобразования Unicode. Символы Unicode передаются в соответствии со спецификацией Unicode-1-1-UTF-8, определяющий компактный бинарный формат, предназначенный для кодирования 16-битовых символов Unicode посредством 8-битовых байтов.

Методы интерфейса DataInput обычно реагируют на событие достижения конца потока, выбрасывая исключение типа EOFException, производного от IOException.

Помимо перечисленных выше методов в составе интерфейсов DataInput и DataOutput есть ряд других методов. Например , в интерфейсе DataInput

public abstract void readFully (byte[] buf, int offset, int count) throws IOException
Осуществляет ввод данных из потока в часть байтового массива buf. Наибольшее количество байтов, подлежащих считыванию, задаётся параметром count . Байты сохраняются в элементах массива, начиная с buf [ offset ] и заканчивая buf [ offset + count -1]. Если содержимое count равно нулю, ввод данных не производится. Метод выполняет блокировку до тех пор, пока имеются доступные для ввода байты, не достигнут конец потока (в этом случае генерируется исключение типа EOFException ) либо не выброшено исключение, связанное с ошибкой ввода-вывода.

В интерфейсе DataOutput есть метод

public abstract void writeChars (String s) throws IOException
Выводит в поток содержимое строки s в виде последовательности значений char. Каждый символ сохраняется в виде пары байтов, и старший байт выводится первым.



Классы потоков Data

Для каждого интерфейса Data существует определённый поток Data. Кроме того, имеется класс RandomAccessFile, который реализует одновременно оба интерфейса Data – ввода и вывода. Каждый класс Data является расширением соответствующего класса Filter, так что потоки Data могут использоваться в целях фильтрации данных других потоков. В составе каждого класса Data есть конструкторы, в качестве параметра принимающие ссылки на другой подходящий поток ввода или вывода. Например, фильтрацию выводимых в файл данных можно осуществить, если передать конструктору DataOutputStream ссылку на объект FileOutputStream. Для последующего ввода данных надлежит использовать поток DataInputStream, построенный на основе объекта FileInputStream. Примеры реализации соответствующих методов приведены в примере 38.

Пример 38.

public static void writeData(double[] data, String file) throws IOException {
        OutputStream fout = new FileOutputStream(file);

DataOutputStream out = new DataOutputStream(fout);

out.writeInt(data.length);

for (int i =0; i < data.length; i++)

     out.writeDouble(data[i]);

out.close();

}

public static double[] readData(String file) throws IOException {
        InputStream fin = new FileInputStream(file);

DataInputStream in = new DataInputStream(fin);

double[] data = new double[in.readInt()];

for (int i =0; i < data.length; i++)

     data[i] = in.readDouble();

in.close();

  return data;

}

В теле метода writeData вначале открывается файл, в который записывается длина массива, подлежащего сохранению. Далее в цикле осуществляется вывод элементов массива. Содержимое файла может быть считано с помощью метода readData .



Класс File

Класс File реализует ряд полезных средств для работы с файлами и директориями: создание, проверка атрибутов, удаление, переименование.

· exists ; возвращает true , если файл существует в файловой системе

· canWrite ; возвращает true , если файл существует и его содержимое может быть изменено.

В составе класса File также объявлены методы, которые позволяют создавать временные файлы, используемые, например, для хранения промежуточных результатов и удаляемые при завершении работы программы.

Потоки File

Потоковые классы семейства File – FileInputStream, FileOutputStream, FileReader и FileWriter – позволяют трактовать файл как поток, предназначенный для ввода(чтения) или вывода(записи) данных. В составе каждого из типов предусмотрены три конструктора, принимающие в качестве пареметра одно из следующих значений:

· строку String, задающую имя файла;

· объект класса File, указывающий на файл;

· объект FileDescriptor.

Если при использовании байтового или символьного потоков ввода (чтения), обнаруживается, что файл не существует, выбрасывается исключение типа FileNotFoundException. При доступе к файлу во всех случаях проверяется наличие соответствующих полномочий: если необходимые права на обращение к файлу отсутствуют, генерируется исключение типа SecurityException.

Два первых конструктора при построении байтового или символьного потоков вывода(записи) предусматривают создание файла, если такого не существует, при наличии файла его содержимое усекается. Процессом усечения данных можно управлять, используя соответствующие варианты конструкторов с двумя параметрами: строкой String, задающей имя файла, и флагом типа boolean, который, будучи равным true, устанавливает, что в процессе записи каждая новая порция данных должна передаваться в поток, не уничтожая ранее сохранённого содержимого. Если в качестве значения флага задаётся false, вместо прежних данных будут записаны новые. Если файл не существует, он будет создан (значение флага в этом случае во внимание не принимается).

Объект FileDescriptor служит для представления сущности, описывающий открытый файл и определяемой особенностями применяемой опреационной системы. Объект дескриптора открытого файла может быть получен вызовом метода getFD объекта байтового потока File, но его нельзя определить для символьных потоков File. Объект FileDescriptor может быть применен для создания нового потока File на основе того же файла, который используется другим потоком, информация об имени и мепстоположении файла в данном случае не нужна. Следует избегать опасности непредусмотренного взаимодействия между потоками, выполняющими различные функции над данными одного и того же файла: невозможно предсказать, что случится, например, если два потока вычислений примутся единовременно зыписывать данные в один файл посредством двух различных объектов FileOutputStream .

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

Одной из существенных возможностей многих реальных приложений является их способность преобразовывать объекты в байтовые потоки, которые могут передаваться в пределах сети - скажем, с целью использования в рамках технологии удалённого вызова метода, - сохраняться на диске в виде файлов, а затем при необходимости восстанавливаться в форме «живых» объектов. Процесс преобразования содержимого объекта в поток байтов принято называть сериализацией объекта (object serialization), а обратное преобразование – восстановление объекта из данных потока – десериализацией (deserialization). Потоки Object – ObjectOutputStream и ObjectInputStream - позволяют осуществлять сериализацию и десериализацию объектов.

ObjectInputStream и ObjectOutputStream, помимо данных стандартных классов (простых типов, строк и их массивов) позволяют вводить и выводить графы объектов (objec graphs). Под термином граф объекта имеется в виду, что когда содержимое объекта выводится в поток ObjectOutputStream средствами методов writeObject, в потоки сохраняются наборы байтов, представляющие и текущий объект, и все другие объекты, на который тот ссылается. Поскольку данные об объекте, подвергшимся сериализации, представляются в форме байтов, в семействе потоков Object отсутствуют символьные разновидности Reader и Writer.

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

В примере 39 представлены примеры сериализации и десериализации объекта.

Пример 39.

// Сериализация

LinkedList ls = new LinkedList ();

FileOutputStream fileOut = new FileOutputStream("list");

ObjectOutputStream out = new ObjectOutputStream(fileOut);

out.writeObject(ls);

// Десериализация

FileInputStream fileIn = new FileInputStream("list");

ObjectInputStream in = new ObjectInputStream(fileIn);

LinkedList ls = (LinkedList) in.readObject();

Подготовка классов к сериализации

Необходимым условием возможности осуществления сериализации объектов класса средствами потока ObjectOutputStream является реализация классом интерфейса-маркера Serializable. Поддержка классом интерфейса Serializable свидетельствует о том, что объекты класса готовы к сериализации.

По умолчанию процесс сериализации заключается в сериализации каждого поля объекта, которое не обозначено как transient или static. Данные простых типов и строки сохраняются с помощью того же механизма, какой поддерживается потоками DataOutputStream, а сериализация объектов выполняется средствами метода writeObject. Схема сериализации, предусмотренная по умолчанию, предполагает, что все поля-объекты, подлежащие сериализации, должны указывать на типы, в свою очередь поддерживающие возможность сериализации. Кроме того, требуется, чтобы класс, базовый по отношению к рассматриваемому, либо обладал конструктором без параметров (чтобы он мог быть вызван в процессе десериализации), либо сам в свою очередь обеспечивал реализацию интерфейса Serializable. В больщинстве ситуаций для обеспечения возможности сериализации объектов класса вполне достаточно тех мер, которые предусмотрены схемой сериализации, предлагаемой по умолчанию, и проблема исчерпывается простым упоминанием интерфейса Serializable в объявлении класса.

Схема десериализации, предлагаемая по умолчанию, предусматривает считывание из потока байтовых данных, сохранённых в процессе сериализации. Статические поля класса остаются в неприкосновенности – в ходе загрузки класса будут выполнены все обычные процедуры инициализации, и статические поля получат требуемые исходные значения. Каждому полю transient в восстановленном объекте присваивается значение по умолчанию, соответствующее типу этого поля.

Иногда встречаются классы, которые сами по себе допускают возможность сериализации, но их отдельные элементы – нет. Например, контейнерный класс как таковой, позволяющий сериализацию, может содержать ссылки на объекты, которые сериализации не поддаются. Любая попытка осуществления сериализации объекта, не поддерживающего эту возможность, приводит к выбрасыванию исключения типа NotSerializableException.

Дата: 2019-02-19, просмотров: 277.