Фільтруючі потоки

додають кілька нових конструкторів до базових конструкторам класів InputStream і OutputStream Їм передається потік відповідного типу (вхідний або вихідний), з яким необхідно зєднати обєкт дозволяють обєднувати потоки в ланцюжка і тим самим створювати складовою потік з великими можливостями Наведена програма друкує номер рядка файлу, в якій буде виявлено перше входження заданого символу:

import javaio*

class FindChar {

public static void main (String[] args)

throws Exception

{

if (argslength = 2)

throw new Exception(&quotneed char and file&quot)

int match = args[0]charAt(0) FileInputStream

fileIn = new FileInputStream(filein)

int ch

while ((ch == inread()) = -1) {

if (ch == match) {

Systemoutprintln(&quot’&quot + (char)ch +

&quot’ at line &quot + ingetLineNumber()) Systemexit(0)

}

}

Systemoutprintln(ch + &quot not found&quot) Systemexit(1)

}

}

Програма створює потік класу FileInputStream з імям fileIn для читання з вказаного файлу і потім вставляє перед ним обєкт класу LineNumberInputStream з імям in Обєкти LineNumberInputStream отримують введення від вхідних потоків, за якими вони закріплені, і стежать за нумерацією рядків При читанні байтів з in насправді відбувається читання з потоку fileIn, який отримує ці байти з вхідного файлу Якщо запустити програму з файлом її власного вихідного тексту і буквою I як аргументи, то результат роботи буде виглядати наступним чином:

‘I’ at line 10

Ви можете зчепити довільну кількість обєктів FilterInput Stream В якості вихідного джерела байтів допускається довільний обєкт InputStream, не обовязково відноситься до класу FilterInput Stream Можливість зчеплення є одним з основних достоїнств фільтруючих потоків, причому самий перший потік в ланцюжку не повинен ставитися до класу FilterInputStream

Обєкти FilterOutputStream можуть зчіплюється аналогічним чином При цьому байти, записані в один вихідний потік, будуть піддаватися фільтрації і записуватися в інший вихідний потік Всі потоки, від першого до передостаннього, повинні відноситися до класу FilterOutputStream, але останній потік може являти собою будь-яку з різновидів Output Stream

Застосування фільтрувальних потоків дозволяє вдосконалити поведінку стандартних потоків Наприклад, щоб завжди знати номер поточного рядка в Systemin, можна вставити в початок програми наступний фрагмент:

LineNumberInputStream

lnum = new LinenumberInputStream(Systemin) Systemin = lnum

У всьому іншому тексті програми виробляються звичайні операції з Systemin, однак тепер зявляється можливість стежити за нумерацією рядків Для цього використовується наступний виклик:

lnumgetLineNumber()

Потік LineNumberInputStream, закріплений за іншим потоком Input Stream, слід контрактом останнього, якщо InputStream – єдиний тип, до якого міг би ставитися даний потік Systemin може бути віднесений тільки до типу InputStream, так що весь код програми, в якому він використовується, має право розраховувати тільки на виконання контракту цього типу LineNu m berInputStream підтримує більш широкий спектр функцій, так що заміна вихідного обєкта на той же самий обєкт з доданими функціями нумерації рядків виявляється цілком допустимою

Вправа 112

Розширте FilterInputStream для створення класу, який здійснює Прогресивне читання і повернення даних, з використанням методу, блокуючого роботу програми до появи повної рядка введення

Вправа 113

Розширте FilterOutputStream для створення класу, який перетворює кожне слово вхідного потоку в заголовний регістр (title case) / Див розділ 135 – Примеч перев /

Вправа 114

Створіть пару фільтруючих потокових класів для роботи зі стиснутими в довільному форматі даними при цьому потік CompressInputStream повинен вміти розшифровувати дані, створені потоком Compress OutputStream

116 Клас PrintStream

Клас PrintStream використовується кожен раз, коли у вашій програмі зустрічається виклик методу print або println PrintStream є розширенням FilterOutputStream, так що передані байти можуть піддаватися фільтрації Клас містить методи print і println для наступних типів:

char

int

float

Object

boolean

char[]

long

double

String

Крім того, простий виклик println без параметрів здійснює перехід на інший рядок без виведення інформації

PrintStream містить два конструктора Один з них – конструктор FilterOutputStream, який одержує як параметр обєкт-потік У іншого конструктора є другий параметр логічного типу, який управляє автоматичним очищенням (autoflushing) потоку Якщо значення цього аргументу одно true, то запис у потік символу переходу на новий рядок \ n призводить до виклику методу flush В іншому випадку такий

символ нічим не відрізняється від всіх інших, і flush не викликається Після

конструювання потоку його поведінку щодо автоматичного очищення вже не може бути змінено

При включенні автоматичного очищення виклик будь-якого з методів write, записуючого масив байтів, призводить до обігу до flush Символи \ n, які зустрічаються всередині масивів, не викликають flush, незалежно від стану прапора автоматичного очищення

Методи print (String) і print (char []) є синхронізованими Всі інші методи print і println реалізуються за допомогою цих двох методів, так що печатка в обєкт PrintStream є безпечною при багатопотокової роботі

117 Буферизованная потоки

Обєкти класів BufferedInputStream і BufferedOutputStream мають властивість буферизації, завдяки чому вдається уникнути виклику операцій читання / запису при кожному новому зверненні до потоку Ці класи часто використовуються в поєднанні з файловими потоками – робота з файлом на диску відбувається порівняно повільно, і буферизація дозволяє скоротити кількість звернень до фізичного носія

При створенні буферизованного потоку можна явно задати розмір буфера або покластися на значення, прийняте за умовчанням Буферізованние потік використовує масив типу

byte для проміжного зберігання байтів, що проходять через потік

Якщо метод read викликається для порожнього потоку BufferedInputStream, він виконує наступні дії: звертається до методу read потоку-джерела, заповнює буфер максимально можливою кількістю байтів і повертає запитані дані з буфера

Аналогічно поводиться і BufferedOutputStream Коли черговий виклик write призводить до заповнення буфера, викликається метод write потоку-приймача, направляючий вміст буфера в потік

Буферізованние вихідний потік, що використовується для запису даних у файл, створюється наступним чином:

OutputStream bufferedFile(String path)

throws IOExceptioon

{

OutputStream out = new FileOutputStream(path)

return new BufferedOutputStream(out)

}

Спочатку для зазначеного шляху створюється FileOutputStream, потім породжується BufferedOutputStream і повертається отриманий буферізованние обєкт-потік Подібна схема дозволяє буферизувати висновок, призначений для занесення у файл

Щоб користуватися методами обєкта FileOutputStream, необхідно зберегти посилання на нього, оскільки для фільтруючих потоків не існує способу отримати обєкт, наступний за даним обєктом-потоком в ланцюжку Перед тим як працювати з наступним потоком, необхідно очистити буфер, інакше дані в буфері не досягнуть наступного потоку

118 Байтові потоки

Байтові масиви, використовувані як джерел вхідних або приймачів вихідних потоків, можуть застосовуватися для побудова рядків з даними для друку, декодування даних і т д Ці можливості надаються потоками ByteArray Методи потоків ByteArray є синхронізованими, а отже – безпечними в умовах багатопотокової середовища

Клас ByteArrayInput використовує як джерело даних масив типу byte Він містить два конструктора:

public ByteArrayInputStream(byte[] buf)

Створює обєкт ByteArrayInputStream по заданому байтовому масиву Масив використовується безпосередньо, а не копіюється Досягнення кінця масиву buf означає завершення введення даних з потоку

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

Створює обєкт ByteArrayInputStream по заданому байтовому масиву, однак використовується лише частина масиву buf від buf [offset] до buf [offset + length-1] або до кінця масиву (залежно від того, яка величина виявиться менше)

Клас ByteArrayOutput здійснює виведення в динамічно збільшували байтовий масив Він містить наступні конструктори і методи:

public ByteArrayOutputStream()

Створює обєкт ByteArrayOutputStream, розмір якого вибирається за замовчуванням public ByteArrayOutputStream(int size)

Створює обєкт ByteArrayOutputStream із заданим початковим розміром public synchronized byte [] toByteArray()

Метод повертає копію даних Це дозволяє програмісту працювати з масивом, що не

змінюючи вихідних даних public int size()

Повертає поточний розмір буфера

public String toString(int hiByte)

Створює новий обєкт String на основі вмісту байтового масиву Старші 8 біт кожного 16-розрядного символу в рядку встановлюються рівними 8 молодшим бітам hiByte Також є перевизначення безаргументний форма toString, еквівалентна toString (0)

119 Клас StringBufferInputStream

StringBufferInputStream читає дані з рядка String, а не з байтового масиву Клас містить єдиний конструктор, параметром якого є рядок – джерело вводу Робота з символами рядка здійснюється так, як якщо б це були байти Наприклад, наведена нижче програма читає символи з командного рядка або з Systemin:

class Factor {

public static void main(String[] args) {

if (argskength == 0) {

factorNumbers(Systemin)

} else {

InputStream in

for (int i = 0 i &lt&lt argslengthl i++) {

in = new StringBufferInputStream(args[i])

factorNumbers(in)

}

}

}

// ..

}

Якщо команда викликається без параметрів, то factorNumbers бере числа зі стандартного вхідного потоку Якщо ж у командному рядку присутні параметри, то для кожного з них створюється обєкт StringBufferInput Stream і викликається метод factorNumbers

Вхідні дані цього методу розглядаються як єдина послідовність байтів, незалежно від того, взяті вони з командного рядка або зі стандартного вхідного потоку

Зверніть увагу на те, що в конструктор StringBufferInputStream передається обєкт класу String, а не StringBuffer

Парного потоку для StringBufferOutputStream не існує При необхідності його можна імітувати, застосовуючи метод toString до потоку Byt eArrayOutputStream

1110 Файлові потоки і FileDescriptor

Введення і виведення в додатках часто повязаний з читанням / записом файлів Файловий ввід / вивід в Java представлений двома потоками – FileInput Stream і FileOutputStream Обєкти кожного з цих типів створюються одним з трьох конструкторів:

Конструктор з параметром типу String, що містить імя файлу Конструктор з параметром File (див розділ Клас File) Конструктор з параметром класу FileDescriptor

Файловий дескриптор FileDescriptor являє собою системно-залежний обєкт,

службовець для опису відкритого файлу Він може бути отриманий викликом методу getFD для будь-якого обєкта класу File або Random AccessFile Обєкти FileDescriptor дозволяють створювати нові потоки File або RandomAccessFile для тих же файлів, що й інші потоки, але при цьому не потрібно знання імені файлів Необхідно дотримуватися обережності і стежити за тим, щоб різні потоки не намагалися одночасно здійснювати з файлом різні операції Наприклад, неможливо передбачити, що трапиться, коли два потоки спробують одночасно записати інформацію в один і той же файл з використанням двох різних обєктів File Descriptor

Метод flush класу FileOutputStream гарантує лише скидання вмісту буфера у файл Він НЕ гарантує, що дані будуть записані на диск – файлова система може здійснювати свою власну буферизацію

1111 Конвеєрні потоки

Конвеєрні (piped) потоки використовуються парами, призначеними для введення / виводу байти, записані у вхідний потік пари, зчитуються на виході Конвеєрні потоки безпечні в багатопотокової середовищі на Насправді, один з цілком надійних способів роботи з конвеєрними потоками полягає у використанні двох програмних потоків – одного для читання, а іншого для запису У разі заповнення конвеєра відбувається блокування програмного потоку, що здійснює запис Якщо ж читання і запис виробляються в одному програмному потоці, то він блокується назавжди

У наведеному нижче прикладі створюється новий програмний потік, який одержує вхідні дані від деякого обєкта-генератора, а його висновок направляється в обєкт OutputStream:

class Pipe {

public static void main(String[] args) {

try {

PipedOutputStream out = new PipeOutputStream() PipedInputStream in = new PipedInputStream(out)

/ / Генератор даних виводить дані

/ / В наданий йому вихідний потік DataGenerator data = new DataGenerator (out) datasetPriority (ThreadMIN_PRIORITY) datastart ()

int ch

while ((ch = inread()) = -1) Systemoutprint((char)ch)

Systemoutprintln()

} catch (IOException e) { Systemoutprintln(&quotException: &quot + e)

}

}

}

Ми створюємо конвеєрні потоки, задаючи PipedInputStream як параметр конструктора PipedOutputStream Порядок значення не має: з тим же успіхом можна було передавати вихідний потік конструктору вхідного Важливо, щоб парні потоки

введення / виведення були сполучені один з одним Далі конструюється обєкт DataGenerator і вихідним потоком згенерованих даних призначається PipedOutputStream Потім у циклі відбувається читання даних від генератора і запис їх в системний вихідний потік Наприкінці необхідно переконатися, що остання виводиться рядок буде належним чином завершена

Джерело: Арнольд К, Гослінг Д – Мова програмування Java (1997)

Схожі статті:


Сподобалася стаття? Ви можете залишити відгук або підписатися на RSS , щоб автоматично отримувати інформацію про нові статтях.

Коментарів поки що немає.

Ваш отзыв

Поділ на параграфи відбувається автоматично, адреса електронної пошти ніколи не буде опублікований, допустимий HTML: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

*

*