Java: руководство для начинающих
Шрифт:
Ниже приведен конструктор класса DataOutputStream. Обратите внимание на то, что при вызове ему передается экземпляр класса OutputStream. DataOutputStream(OutputStream OutputStream)
где OutputStream — это поток вывода, в который записываются данные. Для того чтобы организовать запись данных в файл, следует передать конструктору в качестве параметра OutputStream объект типа FileOutputStream.
Класс DatalnputStream реализует интерфейс Datalnput, в котором объявлены методы для чтения всех простых типов данных в Java (табл. 10.6). В каждом из этих методов может быть сгенерировано исключение IOException при возникновении ошибки ввода-вывода. В качестве своего основания класс DatalnputStream использует экземпляр класса InputStream, перекрывая его методами для чтения различных типов данных в Java. Однако в потоке типа DatalnputStream данные читаются в двоичном виде, а не в удобной для чтения
где inputStream — это поток, связанный с создаваемым экземпляром класса DatalnputStream. Для того чтобы организовать чтение данных из файла, следует передать конструктору в качестве параметра inputStream объект типа FilelnputStream.
Таблица 10.6. Наиболее часто употребляемые методы ввода данных, определенные в классе DatalnputStream Метод ввода данных Описание boolean readBoolean Читает значение типа boolean byte readByte Читает значение типа byte char readChar Читает значение типа char double readDouble Читает значение типа double float readFloat Читает значение типа float int readlnt Читает значение типа int long readLong Читает значение типа long short readShort Читает значение типа short
Ниже приведен пример программы, демонстрирующий применение классов DataOutputStream и DatalnputStream. В этой программе данные разных типов сначала записываются в файл, а затем читаются из файла. // Запись и чтение двоичных данных.Для компиляции этого кода // требуется JDK 7 или более поздняя версия данного комплекта. import java.io.*; class RWData { public static void main(String args[]) { int i = 10; double d = 1023.56; boolean b = true; // записать ряд значений try (DataOutputStream dataOut = new DataOutputStream(new FileOutputStream("testdata"))) { // Запись двоичных данных в файл testdata. System.out.println("Writing " + i) ; dataOut.writelnt(i); System.out.println("Writing " + d) ; dataOut.writeDouble(d); System.out.println("Writing " + b); dataOut.writeBoolean(b); System.out.println("Writing " + 12.2 * 7.4); dataOut.writeDouble(12.2 * 7.4); } catch(IOException exc) { System.out.println("Write error."); return; } System.out.println ; // а теперь прочитать записанные значения try (DatalnputStream dataln = new DatalnputStream(new FilelnputStream("testdata"))) { // Чтение двоичных данных из файла testdata. i = dataln.readlnt; System.out.println("Reading " + i) ; d = dataln.readDouble; System.out.println("Reading " + d); b = dataln.readBoolean ; System.out.println("Reading " + b); d = dataln.readDouble; System.out.println("Reading " + d) ; } catch(IOException exc) { System.out.println("Read error."); } } }
Выполнение этой программы дает следующий результат: Writing 10 Writing 1023.56 Writing true Writing 90.28 Reading 10 Reading 1023.56 Reading true Reading 90.28
Пример для опробования 10.1. Утилита сравнения файлов
В этом проекте предстоит создать простую, но очень полезную утилиту для сравнения содержимого файлов. В ходе выполнения этой сервисной программы сначала открываются два сравниваемых файла, а затем данные читаются из них и сравниваются по соответствующему количеству байтов. Если на какой-то стадии операция сравнения дает отрицательный результат, это означает, что содержимое обоих файлов не одинаково. Если же конец обоих файлов достигается одновременно, это означает, что они содержат одинаковые данные.
Последовательность действий
Создайте файл CompFiles.java.
Введите в файл CompFiles.java приведенный ниже исходный код. /* Для того чтобы воспользоваться этой программой, укажите имена сравниваемых файлов в командной строке, например: java CompFile FIRST.TXT SECOND.TXT Для компиляции этого кода требуется JDK 7 или более поздняя версия данного комплекта. */ import java.io.*; class CompFiles { public static void main(String args[]) { int i=0, j=0; // Прежде всего следует убедиться, что файлы были указаны, if(args.length !=2 ) { System.out.println("Usage: CompFiles fl f2"); return; } // сравнить файлы try (FilelnputStream fl = new FilelnputStream(args[0]); FilelnputStream f2 = new FilelnputStream(args[1])) { // проверить содержимое каждого файла do { i = f1.read; j = f2.read; if(i != j) break; } while (i != -1 && j != -1) ; if(i != j) System.out.println("Files differ."); else System.out.println("Files are the same."); } catch(IOException exc) { System.out.println("I/O Error: " + exc); } } }
Для опробования программы скопируйте сначала файл CompFiles. java во временный файл temp, а затем введите в командной строке следующее:java CompFiles CompFiles.java temp
Программа сообщит, что файлы одинаковы. Далее
Эти файлы содержат разные данные, о чем и сообщит программа CompFiles.
Попробуйте самостоятельно внедрить в программу CompFiles ряд дополнительных возможностей. В частности, введите в нее возможность выполнять сравнение без учета регистра символов. Программу CompFiles можно также доработать таким образом, чтобы она выводила место, где обнаружено первое отличие сравниваемых файлов. Файлы с произвольным доступом
До сих пор нам приходилось иметь дело с последовательными файлами, содержимое которых вводилось и выводилось побайтно, т.е. строго по порядку. Но в Java предоставляется также возможность обращаться к хранящимся в файле данным в произвольном порядке. Для этой цели предусмотрен класс RandomAccessFile, инкапсулирующий файл с произвольным доступом. Класс RandomAccessFile не является производным от класса InputStream или OutputStream. Вместо этого он реализует интерфейсы Datalnput и DataOutput, в которых объявлены основные методы ввода-вывода. В нем поддерживаются также запросы позиционирования, т.е. возможность задавать положение указателя файла произвольным образом. Ниже приведен конструктор класса RandomAccessFile, которым мы будем пользоваться далее. RandomAccessFile(String имя_файла, String доступ) throws FileNotFoundException
Здесь конкретный файл указывается с помощью параметра имя_файла, а параметр доступ определяет, какой именно тип доступа будет использоваться для обращения к файлу. Если параметр доступ принимает значение "г", то данные могут читаться из файла, но не записываться в него. Если же указан тип доступа "rw", то файл открывается как для чтения, так и для записи.
Метод seek , общая форма объявления которого приведена ниже, служит для установки текущего положения указателя файла, void seek(long newPos) throws IOException
Здесь параметр newPos определяет новое положение указателя файла в байтах относительно начала файла. Операция чтения или записи, следующая после вызова метода seek , будет выполняться относительно нового положения указателя файла.
В классе RandomAccessFile определены методы read и write . Этот класс также реализует интерфейсы Datalnput и DataOuput, т.е. в нем доступны методы чтения и записи простых типов, например readlnt и writeDouble .
Ниже приведен пример программы, демонстрирующий ввод-вывод с произвольным доступом. В этой программе шесть значений типа double сначала записываются в файл, а затем читаются из него, причем порядок чтения их отличается от порядка записи. // Демонстрация произвольного доступа к файлам. // Для компиляции этого кода требуется JDK 7 // или более поздняя версия данного комплекта. import java.io.*; class RandomAccessDemo { public static void main(String args[]) { double data[] = { 19.4, 10.1, 123.54, 33.0, 87.9, 74.25 }; double d; // открыть и использовать файл с произвольным доступом // Файл с произвольным доступом открывается для записи и чтения. try (RandomAccessFile raf = new RandomAccessFile("random.dat", "rw")) { // записать значения в Файл for(int i=0; i < data.length; i++) { raf.writeDouble(data[i]); } //а теперь прочитать отдельные значения из файла // Для установки указателя файла служит метод seek. raf.seek(0); // найти первое значение типа double d = raf.readDouble; System.out.println("First value is " + d) ; raf.seek(8); // найти второе значение типа double d = raf.readDouble; System.out.println("Second value is " + d) ; raf.seek(8 * 3); // найти четвертое значение типа double d = raf.readDouble; System.out.println("Fourth value is " + d); System.out.println; // а теперь прочитать значения через одно System.out.println("Here is every other value: "); for(int i=0; i < data.length; i+=2) { raf.seek(8 * i); // найти i-e значение типа double d = raf.readDouble; System.out.print(d + " ") ; } } catch(IOException exc) { System.out.println("I/O Error: " + exc) ; } } }
Результат выполнения данной программы выглядит следующим образом: First value is 19.4 Second value is 10.1 Fourth value is 33.0 Here is every other value: 19.4 123.54 87.9
Обратите внимание на расположение каждого числового значения. Ведь значение типа double занимает 8 байтов, и поэтому каждое последующее число начинается на 8-байтовой границе предыдущего числа. Иными словами, первое числовое значение начинается на позиции нулевого байта, второе — на позиции 8-го байта, третье — на позиции 16-го байта и т.д. Поэтому для чтения четвертого числового значения нужно установить указатель файла на позиции 24-го байта при вызове метода seek . Применение символьных потоков в Java