Конспект лекций по курсу Выбранные вопросы информатики (часть 2) для специальности Информатика Графика

Вид материалаКонспект
Закрывание потоков
Принудительный сброс буферов
Потоки в оперативной памяти
Класс ByteArrayOutputStream
Класс ByteArrayInputStream
Класс StringBufferInputStream
Класс StreamTokenizer для разбора входных потоков
Конструктор класса StreamTokenizer
Методы класса StreamTokenizer
Методы для разбора входного потока
Класс StringTokenizer
Работа с файлами и каталогами при помощи класса File
Создание объекта класса File
Определение атрибутов файлов и каталогов
Проверка существования файла или каталога
Проверка возможности чтения и записи
Определение типа объекта - файл или каталог
Получение имени файла или каталога
Получение абсолютного пути к каталогу
Определение типа указанного пути - абсолютный или относительный
...
Полное содержание
Подобный материал:
1   ...   9   10   11   12   13   14   15   16   17

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

Работая с файлами в среде MS-DOS или Windows средствами языка программирования С вы должны были закрывать ненужные более файлы. Так как в системе интерпертации приложений Java есть процесс сборки мусора, возникает вопрос - выполняет ли он автоматическое закрывание потоков, с которыми приложение завершило работу?

Оказывается, процесс сборки мусора не делает ничего подобного!

Сборка мусора выполняется только для объектов, размещенных в оперативной памяти. Потоки вы должны закрывать явным образом, вызывая для этого метод close.


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

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

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


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

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

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

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

Класс ByteArrayOutputStream

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

public ByteArrayOutputStream();

public ByteArrayOutputStream(int size);

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

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

public void reset();

public int size();

public byte[] toByteArray();

public void writeTo(OutputStream out);

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

С помощью метода size можно определить количество байт данных, записанных в поток.

Метод toByteArray позволяет скопировать данные, записанные в поток, в массив байт. Этот метод возвращает адрес созданного для этой цели массива.

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

Для выполнения форматированного вывода в поток, вы должны создать поток на базе класса 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.


Класс StreamTokenizer для разбора входных потоков

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

Конструктор класса StreamTokenizer

Для создание объектов класса StreamTokenizer предусмотрен всего один конструктор:

public StreamTokenizer(InputStream istream);

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

Методы класса StreamTokenizer

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

Методы для настройки параметров разборщика

Ниже мы привели прототипы методов, предназначенных для настройки параметров разборщика:

public void commentChar(int ch);

public void slashSlashComments(boolean flag);

public void slashStarComments(boolean flag);

public void quoteChar(int ch);


public void eolIsSignificant(boolean flag);

public void lowerCaseMode(boolean fl);


public void ordinaryChar(int ch);

public void ordinaryChars(int low,int hi);

public void resetSyntax();


public void parseNumbers();

public void whitespaceChars(int low, int hi);

public void wordChars(int low, int hi);

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

С помощью метода commentChar вы можете указать символ комментария. Если в строке входного потока попадется такой символ, то он и все следующие за ним до конца текущей строки символы будут проигнорированы.

Методы SlashSlashComments и slashStarComments позволяют указать, что для входного текста используются разделители комментариев в виде двойного символа '/' и '/* … */', соответственно. Это соответствует способу указания комментариев в программах, составленных на языках программирования С++ и С. Для включения режима выделения комментариев обоим методам в качетстве параметра необходимо передать значение true, а для отключения - false.

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

Если передать методу eolIsSignificant значение true, разделители строк будут интерпретироваться как отдельные элементы. Если же этому методу передать значение false, разделители строк будут использоваться аналогично пробелам для разделения элементов входного потока.

Метод lowerCaseMode позволяет включить режим, при котором все выделенные элементы будут перекодированы в строчные символы.

Методы ordinaryChar и ordinaryChars позволяют указать символы, которые должны интерпретироваться как обычные, из которых составляются слова или цифры. Например, если передать методу ordinaryChar символ '.', то слово java.io будет восприниматься как один элемент. Если же этого не сделать, то разборщик выделит из него три элемента - слово java, точку '.' и слово io. Метод ordinaryChars позволяет указать диапазон значений символов, которые должны интерпретироваться как обычные.

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

Метод parseNumbers включает режим разбора чисел, при котором распознаются и преобразуются числа в формате с плавающей десятичной точкой.

Метод whitespaceChars задает диапазон значений для символов-разделителей отдельных слов в потоке.

Метод wordChars позволяет указать символы, которые являются составными частями слов.

Методы для разбора входного потока

После того как вы создали разборщик входного потока на базе класса StreamTokenizer и установили его параметры с помощью описанных выше методов, можно приступать собственно к разборке потока. Обычно для этого организуется цикл, в котором вызывается метод nextToken:

public int nextToken();

Этот метод может вернуть одно из следующих значений:

Значение

Описание

TT_WORDTT_WORD

Из потока было извлечено слово

TT_NUMBERTT_NUMBER

Из потока было извлечено численное значение

TT_EOLTT_EOL

Обнаружен конец строки. Это значение возвращается только в том случае, если при настройке параметров разборщика был вызван метод eolIsSignficant

TT_EOFTT_EOF

Обнаружен конец файла

Если метод nextToken вернул значение TT_EOF, следует завершить цикл разбора входного потока.

Как извлечь считанные элементы потока?

В классе StreamTokenizer определено три поля:

public String sval;

public double nval;

public int ttype;

Если метод nextToken вернул значение TT_WORD, в поле sval содержится извлеченный элемент в виде текстовой строки. В том случае, когда из входного потока было извлечено числовое значение, оно будет храниться в поле nval типа double. Обычные символы записываются в поле ttype.

Заметим, что если в потоке обнаружены слова, взятые в кавычки, то символ кавычки записывается в поле ttype, а слова - в поле sval. По умолчанию используется символ кавычек '"', однако с помощью метода quoteChar вы можете задать любой другой символ.

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

public int lineno();

После вызова метода pushBack следующий вызов метода nextToken приведет к тому, что в поле ttype будет записано текущее значение, а содержимое полей sval и nval не изменится. Прототип метода pushBack приведен ниже:

public void pushBack();

Метод toString возвращает текстовую строку, представляющую текущий элемент, выделенный из потока:

public String toString();


Класс StringTokenizer

Рассказывая о классе StreamTokenizer, нельзя не упомянуть о другом классе с похожим названием и назначением, а именно о классе StringTokenizer.

Определение этого класса достаточно компактно.

Конструкторы

public StringTokenizer(String str);

public StringTokenizer(String str, String delim);

public StringTokenizer(String str, String delim, boolean returnTokens);

Методы

public String nextToken();

public String nextToken(String delim);

public int countTokenss();

public boolean hasMoreElements();

public boolean hasMoreTokenss();

public Object nextElement();

}

Класс StringTokenizer не имеет никакого отношения к потокам, так как предназначен для выделения отдельных элементов из строк типа String.

Конструкторы класса получают в качетсве первого параметра str ссылку на разбираемую строку. Второй параметр delim, если он есть, задает разделители, с испльзованием которых в строке будут выделяться элементы. Параметр returnTokens определяет, надо ли вовзвращать обнаруженные разделители как элементы разбираемой строки.

Рассмотрим кратко методы класса StringTokenizer.

Для разбора строки приложение должно организовать цикл, вызывая в нем метод nextToken. Условием завершения цикла может быть либо возникновение исключения NoSuchElementException, либо возврат значения false методами hasMoreElements или hasMoreTokens.

Метод countTokens позволяет определить, сколько раз был вызван метод nextToken перед возникновением исключения NoSuchElementException.


Работа с файлами и каталогами при помощи класса File

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

Создание объекта класса File

У вас есть три возможности создать объект класса File, вызвав для этого один из трех конструкторов:

public File(String path);

public File(File dir, String name);

public File(String path, String name);

Первый из этих конструкторов имеет единственный параметр - ссылку на строку пути к файлу или каталогу. С помощью второго конструктора вы можете указать отдельно каталог dir и имя файла, для которого создается объект в текущем каталоге. И, наконец, третий конструктор позволяет указать полный путь к каталогу и имя файла.

Если первому из перечисленных конструкторов передать ссылку со значением null, возникнет исключение NullPointerException.

Пользоваться конструкторам очень просто. Вот, например, как создать объект класса File для файла c:\autoexec.bat и каталога d:\winnt:

f1 = new File("c:\\autoexec.bat");

f2 = new File("d:\\winnt");

Определение атрибутов файлов и каталогов

После того как вы создали объект класса File, нетрудно определить атрибуты этого объекта, воспользовавшись соответствующими методами класса File.

Проверка существования файла или каталога

С помощью метода exists вы можете проверить существование файла или катлога, для которого был создан объект класса File:

public boolean exists();

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

Проверка возможности чтения и записи

Методы canRead и canWrite позволяют проверить возможность чтения из файла и записи в файл, соответственно:

public boolean canRead();

public boolean canWrite();

Их полезно применять перед созданием соответствующих потоков, если нужно избежать возникновение исключений, связанных с попыткой выполнения доступа неразрешенного типа. Если доступ разрешен, эти методы возвращают значение true, а если запрещен - false.

Определение типа объекта - файл или каталог

С помощью методов isDirectory и isFile вы можете проверить, чему соответствует созданный объект класса File - каталогу или файлу:

public boolean isDirectory();

public boolean isFile();

Получение имени файла или каталога

Метод getName возвращает имя файла или каталога для заданного объекта класса File (имя выделяется из пути):

public String getName();

Получение абсолютного пути к каталогу

Метод getAbsolutePath возвращает абсолютный путь к файлу или каталогу, который может быть машинно-зависимым:

public String getAbsolutePath();

Определение типа указанного пути - абсолютный или относительный

С помощью метода isAbsolute вы можете определить, соответствует ли данный объект класса File файлу или каталогу, заданному абсолютным (полным) путем, либо относительным путем:

public boolean isAbsolute();

Определение пути к файлу или каталогу

Метод getPath позволяет определить машинно-независимый путь файла или каталога:

public String getPath();

Определение родительского каталога

Если вам нужно определить родительский каталог для объекта класса File, то это можно сделать методом getParent:

public String getParent();

Определение длины файла в байтах

Длину файла в байтах можно определить с помощью метода length:

public long length();

Определение времени последней модификации файла или каталога

Для определения времени последней модификации файла или каталога вы можете вызвать метод lastModified:

public long lastModified();

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

Получение текстового представления объекта

Метод toString возвращает текстовую строку, представляющую объект класса File:

public String toString();

Получение значения хэш-кода

Метод hashCode возвращает значение хэш-кода, соответствующего объекту File:

public int hashCode();

Удаление файлов и каталогов

Для удаления ненужного файла или каталога вы должны создать соответствующий объект File и затем вызвать метод delete:

public boolean delete();

Создание каталогов

С помощью методов mkdir и mkdirs можно создавать новые каталоги:

public boolean mkdir();

public boolean mkdirs();

Первый из этих методов создает один каталог, второй - все подкаталоги, ведущие к создаваемому каталогу (то есть полный путь).

Переименование файлов и каталогов

Для переименования файла или каталога вы должны создать два объекта класса File, один из которых соответствует старому имени, а второй - новому. Затем для перовго из этих объектов нужно вызвать метод renameTo, указав ему в качестве параметра ссылку на второй объект:

public boolean renameTo(File dest);

В случае успеха метод возвращает значение true, при возникновении ошибки - false. Может также возникать исключение SecurityException.

Сравнение объектов класса File

Для сравнения объектов класса File вы должны использовать метод equals:

public boolean equals(Object obj);

Заметим, что этот метод сравнивает пути к файлам и каталогам, но не сами файли или каталоги.

Получение списка содержимого каталога

С помощью метода list вы можете получить список содержимого каталога, соответствующего данному объекту класса File. В классе File предусмотрено два варианта этого метода - без параметра и с параметром:

public String[] list();

public String[] list(FilenameFilter filter);

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


Произвольный доступ к файлам

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

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

В классе RandomAccessFile определено два конструктора, прототипы которых показаны ниже:

public RandomAccessFile(String name, String mode);

public RandomAccessFile(File file, String mode);

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

Если файл открывается только для чтения, вы должны передать конструктору текстовую строку режима "r". Если же файл открывается и для чтения, и для записи, конструктору передается строка "rw".

Позиционирование внутри файла обеспечивается методом seek, в качестве параметра pos которому передается абсолютное смещение файла:

public void seek(long pos);

После вызова этого метода текущая позиция в файле устанавливается в соответствии со значением параметра pos.

В любой момент времени вы можете определить текущую позицию внутри файла, вызвав метод getFilePointer:

public long getFilePointer();

Еще один метод, который имеет отношение к позиционированию, называется skipBytes:

public int skipBytes(int n);

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

С помощью метода close вы должны закрывать файл, после того как работа с им завершена:

public void close();

Метод getFD позволяет получить дескриптор файла:

public final FileDescriptor getFD();

С помощью метода length вы можете определить текущую длину файла:

public long length();

Ряд методов предназначен для выполнения как обычного, так и форматированного ввода из файла. Этот набор аналогичен методам, определенным для потоков:

public int read();

public int read(byte b[]);

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

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 readUnsignedBytee();

public final int readUnsignedShort();

public final String readUTF();

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

public void write(byte b[]);

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

public void write(int b);

public final void writeBoolean(boolean v);

public final void writeBytee(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);

Имена приведенных методов говорят сами за себя, поэтому мы не будем их описывать.