Чтение и запись файлов

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

Два из наиболее часто используемых потоковых класса — это FilelnputStream и FileOutputStream, которые создают байтовые потоки, связанные с файлами. Чтобы открыть файл, вы просто создаете объект одного из этих классов, указав имя файла в качестве аргумента конструктора. Хотя оба класса имеют и дополнительные переопределенные конструкторы, мы будем использовать только следующие из них:

FilelnputStream(String fileName)
throws FileNotFoundException FileOutputStream(String fileName)
throws FileNotFoundException

Здесь filename — имя файла, который вы хотите открыть. Когда вы создаете входной поток, то если файл не существовал, возбуждается исключение FileNotFoundException. Для выходных потоков, если файл не может быть создан, также возбуждается исключение FileNotFoundException. Когда выходной файл открыт, любой ранее существовавший файл с тем же именем уничтожается.

Когда вы завершаете работу с файлом, вы должны закрыть его вызовом метода close (). Этот метод определен и в FilelnputStream и в FileOutputStream, как показано ниже:

void close () throws IOException

Чтобы читать файл, вы можете применять версию метода read (), который определен в FilelnputStream. Та, что мы будем использовать, выглядит так:

int read() throws IOException

Всякий раз, когда вызывается этот метод, он читает единственный байт из файла и возвращает его как целое число. read() возвращает -1, когда достигнут конец файла. Метод может возбуждать исключение IOException.

В следующей программе read () используется для ввода и отображения содержимого текстового файла, имя которого специфицировано в аргументе командной строки. Обратите внимание на блок try/catch, обрабатывающий две ошибки, которые могут возникнуть при работе программы — когда указанный файл не найден, либо когда пользователь забыл указать имя файла. Вы можете применять тот же подход всякий раз при использовании аргументов командной строки. Другие возможные исключения ввода-вывода просто передаются в main (), что вполне приемлемо для такого простого примера. Однако, работая с файлами, часто вы пожелаете обработать самостоятельно все исключения ввода-вывода.

/* Отображение текстового файла.
Чтобы использовать эту программу, укажите имя файла, который хотите просмотреть.
Например, чтобы просмотреть файл TEST.TXT, используйте следующую командную строку:
java ShowFile TEST.TXT
*/
import j ava.io.*;
class ShowFile {
public static void main(String args[])
throws IOException
{
int i;
FilelnputStream fin;
try {
fin = new FilelnputStream(args[0]);
}
catch(FileNotFoundException e) {
System.out.println("Файл не найден");
return;
}
catch(ArraylndexOutOfBoundsException e) {
System.out.println("Использование: ShowFile Файл");
return;
// читать символы до получения символа EOF (конец файла)
do {
i = fin.read();
if(i != -1) System.out.print((char) i) ;
}
while(i != -1) ;
fin.close();
}
}

Для записи в файл вы будете использовать метод write (), определенный в FileOutputStream. Его простейшая форма выглядит так:

void write(int byteval) throws IOException

Этот метод пишет в файл байт, переданный в byteval. Хотя byteval объявлен как целочисленный, в файл записываются только его младшие восемь бит. Если при записи произойдет ошибка, возбуждается исключение IOException. В следующем примере write () используется для копирования текстового файла:

/* Копирование текстового файла.
Для использования этой программы укажите
имена исходного и целевого файлов.
Например, чтобы скопировать файл FIRST.TXT в файл
SECOND.TXT, используйте следующую командную строку:
Java CopyFile FIRST.TXT SECOND.TXT
import java.io.*;
class CopyFile {
public static void main(String args[]) throws IOException
{ int i;
FilelnputStream fin; FileOutputStream fout;
try {
// открыть входной файл try {
fin = new FilelnputStream(args[0]);
}
catch(FileNotFoundException e) {
System.out.println("Входной файл не найден");
return;
}
// открыть выходной файл try {
fout = new FileOutputStream(args[1]);
}
catch(FileNotFoundException e) {
System.out.println("Ошибка открытия выходного файла");
return;
}
}
catch(ArraylndexOutOfBoundsException e) {
System.out.println("Использование: CopyFile Исходный Целевой");
return;
}
// Копировать файл try {
do {
i = fin.read();
if(i != -1) fout.write (i);
}
while(i != -1);
}
catch(lOException e) {
System.out.println("Ошибка файла");
}
fin.close ();
fout.close ();
}
}

Обратите внимание на способ обработки потенциальных ошибок ввода-вывода в этой программе. В отличие от некоторых других языков программирования, включая С и С++, которые используют коды ошибок для обнаружения файловых ошибок, Java применяет свой механизм исключений. Это не только делает управление файлами понятнее, но также позволяет Java просто отличать условие достижения конца файла от файловых ошибок во время ввода. В C/C++ многие функции ввода возвращают одно и то же значение, когда происходит ошибка и когда достигается конец файла. (То есть в C/C++ условие EOF часто отображается на то же значение, что и ошибка ввода.) Обычно это означает, что программист обязан включать дополнительные операторы для определения того, какое событие на самом деле произошло. В Java ошибки передаются вашей программе в виде исключений, а не через значение, возвращаемое read (). То есть, когда read () возвращает -1, это значит только одно: достигнут конец файла.