Работа с файлами и папками
Концепция работы с файлами в Java включает две составляющие:
- Работа с файлами и папками с помощью объектов типа File.
Обеспечивает работу с именами файлов (проверка существования файла или папки с заданным именем, нахождение абсолютного пути по относительному и наоборот, проверка и установка атрибутов файлов и папок).
- Работа потоками ввода-вывода.
Обеспечивает работу не только с файлами, но и с памятью, а также различными устройствами ввода-вывода.
Работа с файлами и папками с помощью объектов типа File
Объекты типа File могут рассматриваться как абстракции, инкапсулирующие работу с именами файлов и папок. При этом папка рассматривается как разновидность файла, обладающая особыми атрибутами.
Создание объекта типа File осуществляется с помощью конструкторов, имеющих следующие варианты:
File(”Имя папки”)
File(”Имя файла”)
File(”Имя папки”,”Имя файла”).
При этом имена могут быть как короткими (локальными), без указания пути к файлу или папке, так и длинными (абсолютными), с указанием пути. В приведённой далее таблице файлы (папки) ищутся по имени в соответствии с правилами поиска файлов в операционной системе. Для платформы Windows® вместо символа ”\” в строках, соответствующих путям, должна использоваться последовательность ”\\”.
Важнейшие файловые операции, инкапсулированные классом File:
Поле или метод | Что содержит или делает |
Переменные класса | |
String pathSeparator | Содержит строку с символом разделителя пути в операционной системе. Это ”/” в Unix-подобных системах и ”\” в Windows®. |
char pathSeparatorChar | Содержит символ разделителя пути в операционной системе. Это ’/’ в Unix-подобных системах и ’\’ в Windows®. |
String separator | Содержит строку с символом разделителя между именами файлов и файловых масок в операционной системе. |
char separatorChar | Содержит символ разделителя между именами файлов и файловых масок в операционной системе. |
Проверка параметров файла или папки | |
boolean exists() | Возвращает true в случае, когда файл (или папка) с заданным в конструкторе именем существует. Иначе false. |
long length() | Возвращает длину файла в байтах в случае, когда файл с заданным в конструкторе именем существует и не является папкой. Иначе 0L. |
boolean canRead() | Возвращает true в случае, когда файл (или папка) с заданным в конструкторе именем существует и доступен по чтению. Иначе false. (В Unix-подобных системах существуют файлы, доступные только по записи). Может возбуждать SecurityException. |
boolean setReadOnly() | Возвращает true в случае, когда файл (или папка) с заданным в конструкторе именем существует, и ему удалось установить статус “доступен только по чтению”. Иначе false. |
boolean canWrite() | Возвращает true в случае, когда файл (или папка) с заданным в конструкторе именем существует и доступен по записи. Иначе false. (В операционных системах существуют файлы, доступные только по чтению). Может возбуждать SecurityException. |
boolean isDirectory() | Возвращает true в случае, когда файл или папка с заданным в конструкторе именем существует и является папкой. Иначе false. |
boolean isFile() | Возвращает true в случае, когда файл или папка с заданным в конструкторе именем существует и является файлом. Иначе false. |
boolean isHidden() | Возвращает true в случае, когда файл или папка с заданным в конструкторе именем существует и является скрытым. Иначе false. В Unix-образных системах скрытыми являются файлы, имена которых начинаются с точки. В Windows® – те, которые имеют атрибут “hidden” (“скрытый”). |
long lastModified() | Возвращает время последней модификации файла, если он существует и доступен по чтению. Иначе 0L. Время отсчитывается в миллисекундах, прошедших с 0 часов 1 января 1970 года (по Гринвичу). |
boolean setLastModified(long time) | Устанавливает время последней модификации файла. Возвращает true, если он существует и доступен по записи. Иначе false. Время отсчитывается в миллисекундах, прошедших с 0 часов 1 января 1970 года (по Гринвичу). |
Путь и имя файла (папки) | |
String getName() | Возвращает короткое имя файла или папки. |
String getParent() | Возвращает абсолютное имя родительской папки – то есть папки, в которой находится файл (или папка), соответствующий файловому объекту. |
String getAbsolutePath() | Возвращает абсолютный путь к файлу или папке, включая имя файла. При этом если в имени файла в конструкторе была задана относительная адресация, соответствующая часть пути сохраняется в возвращаемой строке. |
String getCanonicalPath() | Возвращает абсолютный путь к файлу или папке, включая имя файла. При этом если в имени файла в конструкторе была задана относительная адресация, соответствующая часть пути заменяется в возвращаемой строке на канонический вариант адресации – без элементов относительной адресации. Возбуждает IOException, если канонический путь не может быть построен. |
int compareTo(File f) | Сравнение имён файлов (папок), сопоставляемых текущему файловому объекту и объекту f. Возвращает 0 в случае, когда абсолютные имена файлов (папок) совпадают. Иначе возвращает число, зависящее от разницы в длинах имён и кодов составляющих их символов. Сравнение зависимо от операционной системы – в Unix-образных системах регистр символов имеет значение, в Windows® – не имеет. Соответствие понимается абстрактно на уровне имён и путей – самих файлов может не существовать. |
boolean isAbsolute() | Возвращает true в случае, когда адресация к имени файла (папки) текущего файлового объекта является абсолютной. Хотя может содержать элементы относительной адресации, то есть не быть канонической. |
boolean equals(Object obj) | Возвращает true тогда и только тогда, когда текущий объект и параметр obj соответствуют одному и тому же файлу (папке) . С учётом правил о путях и регистрах символов, задаваемых операционной системой. Соответствие понимается абстрактно на уровне имён и путей – самих файлов может не существовать. |
Создание/уничтожение/переименование файлов и папок | |
boolean createNewFile() | Попытка создания файла или папки по имени, которое было задано в конструкторе объекта. В случае успеха возвращается true, иначе false. Возбуждает IOException, если файл не может быть создан (например, уже существует). |
File createTempFile(String prefix, String suffix) File createTempFile(String prefix, String suffix, File folder) | Метод класса. Обеспечивает создание пустого файла (или папки), задаваемого коротким именем prefix+suffix в папке операционной системы, предназначенной для временных файлов. Возвращает ссылку на объект. Префикс должен быть не менее 3 символов. Возбуждает IOException, если файл не может быть создан (например, уже существует). |
boolean mkdir() | Попытка создания папки по имени, которое было задано в конструкторе объекта. Возвращает true в случае успешного создания и false в других случаях. |
boolean mkdirs() | Попытка создания папки по имени, которое было задано в конструкторе объекта, причём заодно создаются все папки, заданные в пути, если они не существовали. Возвращает true в случае успешного создания и false в других случаях. |
boolean delete() | Попытка удаления файла или папки по имени, которое было задано в конструкторе объекта. Возвращает true в случае успешного удаления и false в других случаях. |
boolean renameTo(File dest) | Попытка переименования файла или папки с имени, которое было задано в конструкторе объекта, на новое, задаваемое параметром dest. Возвращает true в случае успешного переименования и false в других случаях. |
Создание нового файлового объекта с помощью имеющегося | |
File getAbsoluteFile() | Создание нового файлового объекта по абсолютному пути, соответствующему текущему файловому объекту. |
File getCanonicalFile() | Создание нового файлового объекта по каноническому пути, соответствующему текущему файловому объекту. Возбуждает IOException, если канонический путь не может быть построен. |
File getParentFile() | Создание нового файлового объекта по абсолютному пути, соответствующему родительской папке для текущего файлового объекта. |
Списки папок и файлов | |
String[] list() String[] list(FilenameFilter filter) | Возвращает массив строк (список) коротких имён находящихся в папке файлов и папок. Имена элементов, находящихся во вложенных папках, не показываются. Если файловый объект не соответствует существующей папке, возвращает null. При наличии фильтра возвращаются только те имена, которые соответствуют маске фильтра. |
File[] listFiles() File[] listFiles(FilenameFilter filter) | Возвращает массив файловых объектов, соответствующих находящимся в папке файлов и папок. Элементы, находящиеся во вложенных папках, не учитываются. Если текущий файловый объект не соответствует существующей папке, возвращает null. При наличии фильтра возвращаются объекты только для тех имён, которые соответствуют маске фильтра. |
File[] listRoots() | Возвращает массив файловых объектов, соответствующих возможным на данном компьютере корневым папкам. В Unix это папка “/”, в Windows® – корневые папки всех возможных дисков. |
Пример работы с файловыми объектами:
File f1=new File(".."); // "." , "/" , "C:/../"
System.out.println("getAbsolutePath(): "+f1.getAbsolutePath());
try{
System.out.println("getCanonicalPath(): "+f1.getCanonicalPath());
}
catch(Exception e){
System.out.println("Исключение от getCanonicalPath() ");
};
System.out.println("exists(): "+f1.exists());
System.out.println("canRead(): "+f1.canRead());
System.out.println("canWrite(): "+f1.canWrite());
Выбор файлов и папок с помощью файлового диалога
При работе с файлами в подавляющем большинстве приложений требуется вызов файлового диалога. В нашем приложении из палитры компонентов (правое верхнее окно среды разработки) перетащим на экранную форму JLabel (“Метка”) – первый компонент в палитре Swing. А затем повторим эту операцию ещё раз. В первой метке мы будем показывать имя выбранного файла, а во второй – путь к этому файлу.
Для того, чтобы вызвать файловый диалог, назначим обработчик события пункту файлового меню openMenuItem (“Открыть...”) – подузел [JFrame]/menuBar[JMenu]/fileMenu[JMenu]/openMenuItem[JMenuItem]. Двойной щелчок по узлу openMenuItem приводит к автоматическому созданию заготовки обработчика openMenuItemActionPerformed и открытию редактора исходного кода.
Сначала мы создаём в приложении объект типа JFileChooser, соответствующий файловому диалогу. Если в начале записать import javax.swing.*, что желательно, то в соответствующих местах кода не потребуется квалификация javax.swing. Но в данном примере импорт не сделан намеренно для того, чтобы было видно, где используются классы данного пакета.
javax.swing.JFileChooser fileChooser=new javax.swing.JFileChooser();
Добавим в обработчик необходимый код:
private void openMenuItemActionPerformed(java.awt.event.ActionEvent evt) {
if(fileChooser.showOpenDialog(null)!= fileChooser.APPROVE_OPTION){
System.out.println("Отказались от выбора");
return;
};
System.out.println("Нажали Open");
jLabel1.setText(fileChooser.getSelectedFile().getName());
jLabel2.setText(fileChooser.getSelectedFile().getParent());
}
В первой строке обработчика вызывается метод fileChooser.showOpenDialog(openMenuItem). Он показывает диалог на экране. В качестве параметра должен быть задан родительский компонент – в этом качестве мы используем пункт меню openMenuItem. Сравнение с переменной класса APPROVE_OPTION позволяет выяснить, была ли выбрана кнопка Open - “Открыть”.
Следует обратить внимание на характерный приём – выход из подпрограммы с помощью оператора return в случае, когда не был осуществлён выбор файла. Неопытные программисты написали бы данный фрагмент кода таким образом:
if(fileChooser.showOpenDialog(openMenuItem)== fileChooser.APPROVE_OPTION){
System.out.println("Нажали Open");
jLabel1.setText(fileChooser.getSelectedFile().getName());
jLabel2.setText(fileChooser.getSelectedFile().getParent());
}
else
System.out.println("Отказались от выбора");
На первый взгляд принципиальной разницы нет. Но при усовершенствовании программы код, соответствующий выбору файла с помощью диалога, заметно разрастётся, а код, соответствующий отказу от выбора, останется тем же. В результате практически весь код обработчика во втором варианте кода окажется вложенным в оператор if – а это может быть несколько страниц кода. В таком коде трудно разбираться. В первом варианте оператор if обладает небольшой областью действия, что позволяет легко разобраться с относящимся к нему кодом.
Можно было бы перенести строку
javax.swing.JFileChooser fileChooser=new javax.swing.JFileChooser()
в обработчик события. В этом случае при каждом нажатии пункта меню “Файл/Открыть…” создавался бы новый объект-диалог. Такой код был бы работоспособен. Но создание диалога является относительно долгим процессом, требующим большого количества ресурсов операционной системы. Поэтому лучше создавать в приложении глобальную переменную, которой назначен диалог. Помимо прочего это позволяет в повторно открываемом диалоге оказываться в той же папке, где происходил последний выбор файла.
Перед вызовом диалога можно программно установить папку, в которой он будет открываться:
File folder=…;
fileChooser.setCurrentDirectory(folder);
Диалог сохранения файла открывается аналогичным образом – showSaveDialog.
Практически всегда при использовании файловых диалогов требуется задавать фильтр, по которому просматриваются файлы. В подавляющем большинстве случаев требуется определённое расширение в имени файла. К сожалению, компонент JFileChooser до сих пор не поддерживает встроенной возможности настраивать фильтр – для этого требуется создание специального класса. Приведём пример простейшего такого класса:
package java_gui_example;
import java.io.*;
public class SimpleFileFilter extends javax.swing.filechooser.FileFilter {
String ext;
SimpleFileFilter(String ext){
this.ext=ext;
}
public boolean accept(File f){
if(f==null)
return false;
if(f.isDirectory()){
return true ;
}
else
return (f.getName().endsWith(ext));
}
/**
* Описание фильтра, возникающее в строке фильтра
* @see FileView#getName
*/
public String getDescription(){
return "Text files (.txt)";
}
}
Использование приведённого класса следующее: задаётся глобальная переменная
javax.swing.filechooser.FileFilter fileFilter=new SimpleFileFilter(".txt");
После чего она используется в обработчике события:
fileChooser.addChoosableFileFilter(fileFilter);
Данный оператор добавляет фильтр в выпадающий список возможных фильтров (масок) файлового диалога. Если добавляется уже существующий фильтр, операция добавления игнорируется.
По умолчанию показывается последний из добавленных фильтров. Если требуется показать другой фильтр, который уже был добавлен в список фильтров диалога, требуется вызвать оператор
fileChooser.setFileFilter(fileFilter);
Он делает добавленный фильтр текущим, то есть видимым при открытии диалога.
Кроме стандартных диалогов открытия и сохранения файлов имеется возможность вызова диалога с дополнительными программно задаваемыми элементами - с помощью метода showCustomDialog.
Также имеется возможность выбирать несколько файлов. Для этого до вызова диалога требуется задать разрешение на такой выбор:
fileChooser.setMultiSelectionEnabled(true);
Получение массива выбранных файлов после вызова диалога идёт следующим образом:
java.io.File[] files = fileChooser.getSelectedFiles();
if (files != null && files.length > 0) {
String filenames = "";
for (int i=0; i<files.length; i++) {
filenames = filenames + "\n" + files[i].getPath();
}
}
Имеется возможность выбора папки (директории), а не файла. В этом случае следует задать режим, когда позволяется выбирать только папки
fileChooser.setFileSelectionMode(fileChooser.DIRECTORIES_ONLY);
либо и файлы, и папки
fileChooser.setFileSelectionMode(fileChooser.FILES_AND_DIRECTORIES);
Возврат в обычный режим:
fileChooser.setFileSelectionMode(fileChooser.FILES_ONLY);
Для того, чтобы в выбранной папке просмотреть список файлов в папке, с которой связана переменная File folder, используется вызов вида
String[] filenames= folder.list(filter);
Получается массив строк с короткими именами файлов. Используется переменная filter, тип которой является классом, реализующим интерфейс java.io.FilenameFilter. О том, что такое интерфейсы, будет рассказано в отдельном разделе. Пример простейшего такого класса SimpleFilenameFilter:
package java_gui_example;
import java.io.*;
public class SimpleFilenameFilter implements FilenameFilter{
String ext;
public SimpleFilenameFilter(String ext) {
this.ext=ext;
}
public boolean accept(File dir,String fileName){
return ext==""||fileName.endsWith(ext);
}
}
Пример с показом файлов, содержащихся в выбранной папке, в компонент jTextArea1 (текстовая область) типа JTextArea, позволяющий показывать произвольное число строк:
String[] filenamesArray;
File folder=fileChooser.getSelectedFile();
SimpleFilenameFilter filter=new SimpleFilenameFilter("");
String filenames = "";
if(folder.isDirectory()){
filenamesArray=folder.list(filter);
for (int i=0; i<filenamesArray.length; i++) {
filenames = filenames + "\n" + filenamesArray[i];
};
jTextArea1.setText(filenames);
}
Аналогичный пример вывода выбранных в диалоге имён в режиме “мультиселект”, позволяющем отмечать несколько файлов и/или папок:
java.io.File[] files = fileChooser.getSelectedFiles();
if (files != null && files.length > 0) {
String filenames = "";
for (int i=0; i<files.length; i++) {
filenames = filenames + "\n" + files[i].getPath();
};
jTextArea1.setText(filenames);
};
Работа с потоками ввода-вывода
Стандартные классы Java, инкапсулирующие работу с данными (оболочечные классы для числовых данных, классы String и StringBuffer, массивы и т.д.), не обеспечивают поддержку чтения этих данных из файлов или запись их в файл. Вместо этого используется весьма гибкая и современная, но не очень простая технология использования потоков (Streams).
Поток представляет накапливающуюся последовательность данных, поступающих из какого-то источника. Порция данных может быть считана из потока, при этом она из потока изымается. В потоке действует принцип очереди – “первым вошёл, первым вышел”.
В качестве источника данных потока может быть использован как стационарный источник данных (файл, массив, строка), так и динамический – другой поток. При этом в ряде случаев выход одного потока может служить входом другого. Поток можно представить себе как трубу, через которую перекачиваются данные, причём часто в таких “трубах” происходит обработка данных. Например, поток шифрования шифрует данные, полученные на входе, и при считывании из потока передаёт их в таком виде на выход. А поток архивации сжимает по соответствующему алгоритму входные данные и передаёт их на выход. “Трубы” потоков можно соединять друг с другом – выход одного со входом другого. Для этого в качестве параметра конструктора потока задаётся имя переменной, связанной с потоком - источником данных для создаваемого потока.
Буферизуемые потоки имеют хранящийся в памяти промежуточный буфер, из которого считываются выходные данные потока. Наличие такого буфера позволяет повысить производительность операций ввода-вывода, а также осуществлять дополнительные операции – устанавливать метки (маркеры) для какого-либо элемента, находящегося в буфере потока и даже делать возврат считанных элементов в поток (в пределах буфера).
Абстрактный класс InputStream (“входной поток”) инкапсулирует модель входных потоков, позволяющих считывать из них данные. Абстрактный класс OutputStream (“выходной поток”) - модель выходных потоков, позволяющих записывать в них данные. Абстрактные методы этих классов реализованы в классах-потомках.
Методы класса InputStream:
Метод | Что делает |
int available() | Текущее количество байт, доступных для чтения из потока. |
int read() | Читает один байт из потока и возвращает его значение в виде целого, лежащего в диапазоне от 0 до 255. При достижении конца потока возвращает -1. |
int read(byte[] b) int read(byte[] b, int offset, int count) | Пытается прочесть b.length байт из потока в массив b. Возвращает число реально прочитанных байт. После достижения конца потока последующие считывания возвращают -1. |
long skip(long count) | Попытка пропускать (игнорировать) count байт из потока. count ≤0, пропуска байт нет. Возвращается реально пропущенное число байт. Если это значение ≤0, пропуска байт не было. |
boolean markSupported() | Возвращает true в случае, когда поток поддерживает операции mark и reset, иначе – false. |
void mark(int limit) | Ставит метку в текущей позиции начала потока. Используется для последующего вызова метода reset, с помощью которого считанные после установки метки данные возвращаются обратно в поток. Эту операцию поддерживают не все потоки. См. метод markSupported. |
void reset() | Восстанавливает предшествующее состояние данных в начале потока, возвращая указатель начала потока на помеченный до того меткой элемент. То есть считанные после установки метки данные возвращаются обратно в поток. Попытка вызова при отсутствии метки или выходе её за пределы лимита приводит к возбуждению исключения IOException. Эту операцию поддерживают не все потоки. См. методы markSupported и mark. |
void close() | Закрытие потока. Последующие попытки чтения из этого потока приводят к возбуждению исключения IOException. |
Все методы класса InputStream, кроме markSupported и mark, возбуждают исключение IOException – оно возникает при ошибке чтения данных.
Методы класса OutputStream:
Метод | Что делает |
void write(int b) | Записывает один байт в поток. Благодаря использованию типа int можно использовать в качестве параметра целочисленное выражение без приведения его к типу byte. Напомним, что в выражениях “короткие” целые типы автоматически преобразуются к типу int. |
void write(byte[] b) void write(byte[] b, int offset, int count) | Записывает в поток массив байт. Если заданы параметры offset и count, записывается не весь массив, а count байт начиная с индекса offset. |
void flush() | Форсирует вывод данных из выходного буфера и его очистку. Необходимость этой операции связана с тем, что в большинстве случаев данные уходят “во внешний мир” на запись не каждый раз после вызова write, а только после заполнения выходного буфера. Таким образом, если операции записи разделены паузой (большим интервалом времени), и требуется, чтобы уход данных из выходного буфера совершался своевременно, после последнего вызова оператора write, предшествующего паузе, надо вызывать оператор flush. |
void close() | Закрытие потока. Последующие попытки записи в этот поток приводят к возбуждению исключения IOException. |
Все методы этого класса возбуждают IOException в случае ошибки записи.
Не все классы потоков являются потомками InputStream/OutputStream. Для чтения строк (в виде массива символов) используются потомки абстрактного класса java.io.Reader (“читатель”). В частности, для чтения из файла – класс FileReader. Аналогично, для записи строк используются классы- потомки абстрактного класса java.io.Writer (“писатель”). В частности, для записи массива символов в файл– класс FileWriter.
Имеется ещё один важный класс для работы с файлами, не входящий в иерархии InputStream/OutputStream и Reader/ Writer. Это класс RandomAccessFile (“файл с произвольным доступом”), предназначенный для чтения и записи данных в произвольном месте файла. Такой файл с точки зрения класса RandomAccessFile представляет массив байт, сохранённых на внешнем носителе. Класс RandomAccessFile не является абстрактным, поэтому можно создавать его экземпляры.
Все операции чтения и записи начинаются с позиции, задаваемой файловым указателем перед началом операции. После каждой такой операции файловый указатель сдвигается к концу файла на число позиций (байт), соответствующее типу считанных данных. В случае, когда во время записи файловый указатель выходит за конец файла, размер файла автоматически увеличивается.
Важнейшие методы класса RandomAccessFile:
Метод | Что делает |
Общие параметры файла | |
long getFilePointer() | Возвращает значение файлового указателя - текущую позицию в файле считывающей/записывающей головки. Индексация начинается с нуля и даётся в числе байт. |
long length() | Длина файла (в байтах). |
void setLength(long newLength) | Изменение длины файла – она устанавливается равной newLength. Если newLength меньше текущей длины, файл укорачивается путём отбрасывания его конца. При этом если файловый указатель находился в отбрасываемой части, он устанавливается в конец файла, иначе его позиция не меняется. Если newLength больше текущей длины, файл увеличивает размер путём добавления в конец нового пространства, содержимое которого не гарантируется. |
void close() | Закрытие потока. Последующие попытки доступа к этому потоку приводят к возбуждению исключения IOException. |
Побайтные операции установки позиции | |
void seek(long pos) | Установка файлового указателя в позицию pos. |
int skipBytes(int n) | Пропуск n байт – передвижение считывающей/записывающей головки на n байт. Если n>0, то вперёд, к концу файла. Если n≤0, то позиция головки не меняется. Возвращается число реально пропущенных байт. Оно может быть меньше n – например, из-за достижения конца файла. |
Побайтные операции чтения | |
int read() | Читает один байт из потока и возвращает его значение в виде целого, лежащего в диапазоне от 0 до 255. При достижении конца потока возвращает -1. |
int read(byte[] b) | Пытается прочесть b.length байт из потока в массив b. Возвращает число реально прочитанных байт. После достижения конца потока последующие считывания возвращают -1. |
int read(byte[] b, int offset, int count) | Пытается прочесть count байт из потока в массив b, записывая их начиная с позиции offset. Возвращает число реально прочитанных байт. После достижения конца потока последующие считывания возвращают -1. |
void readFully(byte[] b) | Считывает из файла b.length байт, начиная с текущей позиции файлового указателя, и заполняет весь буферный массив b. |
void readFully(byte[] b, int offset, int count) | Считывает из файла в массив count байт, записывая их начиная с байта, имеющего позицию offset. |
Побайтные операции записи | |
void write(int b) | Запись в файл одного байта b. То же, что writeByte(b). |
void write(byte[] b) | Запись в файл всего массива байт b, т.е. b.length байт. |
void write(byte[] b, int offset, int count) | Запись в файл count байт массива b начиная с байта массива, имеющего индекс offset. |
Чтение одиночных значений примитивного типа | |
boolean readBoolean() | Считывает значение boolean |
byte readByte() | Считывает значение byte |
short readShort() | Считывает значение short |
int readInt() | Считывает значение int |
long readLong() | Считывает значение long |
int readUnsignedByte() | Считывает значение типа “беззнаковый байт”( тип отсутствует в Java) и преобразует в значение типа int |
int readUnsignedShort() | Считывает значение типа “беззнаковое короткое целое”( тип отсутствует в Java) и преобразует в значение типа int |
char readChar() | Считывает значение char |
float readFloat() | Считывает значение float |
double readDouble() | Считывает значение double |
Запись одиночных значений примитивного типа | |
void writeBoolean(boolean v) | Запись в файл значение boolean |
void writeByte(int v) | Запись в файл значение byte. Благодаря использованию типа int можно использовать в качестве параметра целочисленное выражение без приведения его к типу byte. |
void writeShort(int v) | Запись в файл значение short. Благодаря использованию типа int можно использовать в качестве параметра целочисленное выражение без приведения его к типу short. |
void writeInt(int v) | Запись в файл значение int |
void writeLong(long v) | Запись в файл значение long |
void writeChar(int v) | Запись в файл значение char. Благодаря использованию типа int можно использовать в качестве параметра целочисленное выражение без приведения его к типу char. |
void writeFloat(float v) | Запись в файл значение float |
void writeDouble(double v) | Запись в файл значение double |
Чтение отдельных строк | |
String readLine() | Считывает из файла строку символов от текущей позиции файлового указателя до места переноса на новую строку (символ возврата каретки или последовательность “\n”) или конца файла. При этом старшие байты получившихся в строке символов UNICODE оказываются равными нулю, т.е. поддерживается только часть кодовой таблицы. Метод полезен для считывания текста, записанного в кодировке ANSI. |
String readUTF | Считывает из файла строку символов в кодировке UTF-8 от текущей позиции файлового указателя. Число последующих считываемых байт строки в кодировке UTF-8 задаётся первыми двумя считанными байтами. |
Запись отдельных строк | |
void writeBytes(String s) | Запись в файл строки s как последовательности байт, соответствующих символам строки. При этом каждому символу соответствует один записываемый байт, так как у каждого символа UNICODE отбрасывается старший из двух байт ( записывается младший байт). Используется для записи символов в кодировке ANSI. См. также метод writeChars, в котором записываются оба байта. |
void writeChars(String s) | Запись в файл строки s как последовательности байт, соответствующих символам строки. При этом каждому символу UNICODE соответствует два записываемых байта. |
void writeUTF(String s) | Запись в файл строки s как последовательности байт, соответствующих символам строки в кодировке UTF-8. |
Все методы класса RandomAccessFile, кроме getChannel(), возбуждают исключение IOException – оно возникает при ошибке записи или ошибке доступа к данным.
В конструкторе класса требуется указать два параметра. Первый – имя файла или файловую переменную, второй – строку с модой доступа к файлу. Имеются следующие варианты:
- "r"- от read,- “только читать”
- "rw"- от read and write,- “читать и писать”
- "rws"- от read and write synchronously,- “читать и писать с поддержкой синхронизации” (см. далее раздел про потоки, Threads)
- "rwd"- от read and write to device,- “читать и писать с поддержкой синхронизации устройства” (см. далее раздел про потоки, Threads)
Например:
java.io.RandomAccessFile rf1=new java.io.RandomAccessFile("q.txt","r");
java.io.RandomAccessFile rf2=new java.io.RandomAccessFile(file,"rw");
Рассмотрим примеры, иллюстрирующие работу с потоками.
Пример чтения текста из файла:
File file;
javax.swing.JFileChooser fileChooser=new javax.swing.JFileChooser();
javax.swing.filechooser.FileFilter fileFilter=new SimpleFileFilter(".txt");
private void openMenuItemActionPerformed(java.awt.event.ActionEvent evt) {
fileChooser.addChoosableFileFilter(fileFilter);
if(fileChooser.showOpenDialog(null)!=fileChooser.APPROVE_OPTION){
return;//Нажали Cancel
};
file = fileChooser.getSelectedFile();
try{
InputStream fileInpStream=new FileInputStream(file);
int size=fileInpStream.available();
fileInpStream.close();
char[] buff=new char[size];
Reader fileReadStream=new FileReader(file);
int count=fileReadStream.read(buff);
jTextArea1.setText(String.copyValueOf(buff));
javax.swing.JOptionPane.showMessageDialog(null,
"Прочитано "+ count+" байт");
fileReadStream.close();
} catch(Exception e){
javax.swing.JOptionPane.showMessageDialog(null,
"Ошибка чтения из файла \n"+file.getAbsolutePath());
}
}
Переменной fileInpStream , которая будет использована для работы потока FileInputStream , мы задаём тип InputStream . Это очень характерный приём при работе с потоками, позволяющий при необходимости заменять в дальнейшем входной поток с FileInputStream на любой другой без изменения последующего кода. После выбора имени текстового файла с помощью файлового диалога мы создаём файловый поток:
fileInpStream=new FileInputStream(file);
Функция fileInpStream.available()возвращает число байт, которые можно считать из файла. После её использования поток fileInpStream нам больше не нужен, и мы его закрываем. Поток FileReader не поддерживает метод available() , поэтому нам пришлось использовать поток типа FileInputStream.
Массив buff используется в качестве буфера, куда мы считываем весь файл.
Если требуется читать файлы большого размера, используют буфер фиксированной длины и в цикле считывают данные из файла блоками, равными длине буфера, до тех пор, пока число считанных байт не окажется меньше размера буфера. Это означает, что мы дошли до конца файла. В таком случае выполнение цикла следует прекратить. При такой организации программы нет необходимости вызывать метод available() и использовать поток типа FileInputStream.
В операторе
Reader fileReadStream=new FileReader(file);
используется тип Reader, а не FileReader, по той же причине, что до этого мы использовали InputStream , а не FileInputStream.
строки правильно работает только для ANSI
Построчное чтение из файла осуществляется аналогично, но содержимое блока try следует заменить на следующий код:
FileReader filReadStream=new FileReader(file);
BufferedReader bufferedIn=new BufferedReader(filReadStream);
String s="",tmpS="";
while((tmpS=bufferedIn.readLine())!=null)
s+=tmpS+"\n";
jTextArea1.setText(s);
bufferedIn.close();
Пример записи текста в файл очень похож на пример чтения, но гораздо проще, так как не требуется вводить промежуточные буферы и потоки:
private void saveAsMenuItemActionPerformed(java.awt.event.ActionEvent evt) {
fileChooser.addChoosableFileFilter(fileFilter);
if(fileChooser.showSaveDialog(null)!=fileChooser.APPROVE_OPTION){
return;//Нажали Cancel
};
file = fileChooser.getSelectedFile();
try{
Writer filWriteStream=new FileWriter(file);
filWriteStream.write(jTextArea1.getText() );
filWriteStream.close();
} catch(Exception e){
javax.swing.JOptionPane.showMessageDialog(null,
"Ошибка записи в файл \n"+file.getAbsolutePath());
}
}
Краткие итоги по главе 7
ü В Java массивы являются объектами, но особого рода – их объявление отличается от объявления других видов объектов. Переменная типа массив является ссылочной. Массивы Java являются динамическими - в любой момент им можно задать новую длину.
ü Двумерный массив представляет собой массив ячеек, каждая из которых имеет тип “одномерный массив”. Трёхмерный – массив двумерных массивов. Соответствующим образом они и задаются.
ü Для копирования массивов лучше использовать метод System.arraycopy. Быстрое заполнение массива одинаковыми значениями может осуществляться методом Arrays.fill - класс Arrays расположен в пакете java.util Поэлементное сравнение массивов следует выполнять с помощью метода Arrays.equals (сравнение на равенство содержимого массивов) либо Arrays.deepEquals (глубокое сравнение, то есть на равенство содержимого, а не ссылок – на произвольном уровне вложенности). Сортировка (упорядочение по значениям) массива производится методом Arrays.sort.
ü Коллекции (Collections) – “умные” массивы с динамически изменяемой длиной, поддерживающие ряд важных дополнительных операций по сравнению с массивами. Доступ к элементам коллекции в общем случае не может осуществляться по индексу, так как не все коллекции поддерживают индексацию элементов. Эту функцию осуществляют с помощью специального объекта – итератора (iterator). У каждой коллекции имеется свой итератор который умеет с ней работать.
ü Класс String инкапсулирует действия со строками. Объект типа String – строка, состоящая из произвольного числа символов, от 0 до 2*109. Литерные константы типа String представляют собой последовательности символов, заключённые в двойные кавычки. В классе Object имеется метод toString(), обеспечивающий строковое представление любого объекта.
ü Строки типа String являются неизменяемыми объектами – при каждом изменении содержимого строки создаётся новый объект-строка. Для того чтобы сделать работу с многочисленными присваиваниями более эффективной, используются классы StringBuffer и StringBuilder.
ü Вывод графики осуществляется с помощью методов объекта типа java.awt.Graphics. Для того, чтобы результаты вывода не пропадали, в классе приложения требуется переопределить метод paint, вызываемый при отрисовке. А также обработчик события ComponentResized.
ü Исключительные ситуации в Java являются объектами. Их типы являются классами-потомками объектного типа Throwable .От Throwable наследуются классы Error (“Ошибка”) и Exception (“Исключение”). Экземплярами класса Error являются непроверяемые исключительные ситуации, которые невозможно перехватить в блоках catch. Экземплярами класса Exception и его потомков являются проверяемые исключительные ситуации. Кроме одного потомка – класса RuntimeException (и его потомков).
ü Непроверяемые исключения генерируются и обрабатываются системой автоматически – как правило, приводя к завершению приложения. При этом их типы нигде не указываются, и слово throws в заголовке метода указывать не надо. Если же в теле реализуемого метода используется вызов метода, который может возбуждать исключительную ситуацию, и это исключение не перехватывается, в заголовке реализуемого метода требуется указывать соответствующий тип возбуждаемого исключения.
ü Объекты типа File обеспечивают работу с именами файлов и папок (проверка существования файла или папки с заданным именем, нахождение абсолютного пути по относительному и наоборот, проверка и установка атрибутов файлов и папок).
ü При работе с файлами в подавляющем большинстве приложений требуется вызов файлового диалога JFileChooser (пакет javax.swing).
ü Потоки ввода-вывода обеспечивают работу не только с файлами, но и с памятью, а также различными устройствами ввода-вывода. Соответствующие классы расположены в пакете java.io. Абстрактный класс InputStream (“входной поток”) инкапсулирует модель входных потоков, позволяющих считывать из них данные. Абстрактный класс OutputStream (“выходной поток”) - модель выходных потоков, позволяющих записывать в них данные.
ü Для чтения строк (в виде массива символов) используются потомки абстрактного класса Reader (“читатель”). В частности, для чтения из файла – класс FileReader. Аналогично, для записи строк используются классы- потомки абстрактного класса Writer (“писатель”). В частности, для записи массива символов в файл– класс FileWriter.
ü Имеется ещё один важный класс для работы с файлами - RandomAccessFile (“файл с произвольным доступом”), предназначенный для чтения и записи данных в произвольном месте файла. Такой файл с точки зрения класса RandomAccessFile представляет массив байт, сохранённых на внешнем носителе. Класс RandomAccessFile не является абстрактным, поэтому можно создавать его экземпляры.
ü
Задания