Java. Полное руководство. 8-е издание. С. Н. Тригуб Перевод с английского и редакция
Скачать 25.04 Mb.
|
static Path get(String имяпути, String ... части) Напомню, что путь может быть определен двумя способами. Он может быть передан по частям — в параметре имяпут и и последующих частях, определенных аргументами переменной длины параметра части. В качестве альтернативы весь путь может быть определен в параметре им япут и, а параметр части не используется. Именно этот подход используется в примерах. Использование системы N10 для канального ввода-вывода Важнейшей областью использования системы NIO является получение доступа к файлу через каналы и буферы. Следующие разделы демонстрируют несколько способов использования канала для чтения файла и записи в него. Ч тение файла через канал Есть несколько способов чтения данных из файла с использованием канала. Вероятно, наиболее распространенный способ — создание буферов вручную и по 6 5 4 Часть II. Библиотека следующее явное выполнение операций чтения, которые загружают эти буферы данными из файла. Именно с этого подхода и начнем. Прежде чем можно будет читать из файла, его необходимо открыть. Для этого сначала создайте объект интерфейса, который описывает файл. Затем используйте его, чтобы открыть файл. Есть разные способы открытия файла, в зависимости оттого, как он будет использоваться. В этом примере файл будет открыт для байтового ввода при помощи явных операций ввода. Поэтому данный пример откроет файл и установит канал доступа к нему при вызове метода Files. newBy- t eChanne 1 (). Метод newByt eChanne 1 () имеет следующую общую форму SeekableByteChannel newByteChanne 1 (Path путь. как Он возвращает объект интерфейса, инкапсули рующий канал для файловых операций. Объект интерфейса, который описывает файл, передается в параметре путь Параметр как определяет, как файл будет открыт. Поскольку это параметр для аргумента переменной длины, вы можете определить любое количество аргументов, отделяемых запятыми. Допустимые значения обсуждались ранее и приведены в табл. 20.7.) Если никаких аргументов не определено, файл открывается для операций ввода. Интерфейс описывает канал, применяемый для файловых операций. Он реализуется классом FileChannel. Когда используется файловая система, заданная по умолчанию, возвращенный объект может быть приведен к классу FileChannel. По завершении работы с каналом следует закрыть его. Поскольку все каналы, включая класс FileChannel, реализуют интерфейс AutoCloseable, вы можете использовать оператор t r y -с ресурсами д д я автоматического закрытия файла, вместо явного вызова метода close () . Именно этот подходи используется в примерах. Затем следует получить буферы, которые будут использоваться каналом либо при заключении в оболочку существующего массива, либо при динамическом резервировании буферов. В примерах используется резервирование, но выбор завами. Поскольку файловые каналы работают с байтовыми буферами, для их получения мы будем использовать метод allocateO, определенный в классе ByteBuf fer. Его общая форма такова ByteBuffer allocate(int емкость) Здесь параметр емкость определяет емкость буферов. Возвращается ссылка на буферы. После создания буферов вы вызываете метод read () объекта канала, передавая ссылку на буферы. Ниже представлена версия метода read () , которую мы будем использовать далее read(ByteBuffer буфер throws При каждом вызове метод read () заполняет данными из файла буферы, определенные параметром буфер Чтение осуществляется последовательно, значит, каждый вызов метода read () читает из файла в буфер следующую последовательность байтов. Метод read () возвращает количество фактически прочитанных байтов. При попытке чтения после конца файла, возвращается значение -1 Следующая программа демонстрирует изложенное выше на практике, при чтении файла test. txt через канал с использованием явных операций ввода Использование канала ввода-вывод для чтения файла. Требует JDK 7. import java.io.*; import java.nio.*; import java.nio.channels.*; Глава 20. Исследование N10 6 5 5 import java.n i o .file.*; public class ExplicitChannelRead { public static void main(String a r g s []) { int count; Path filepath = null; // Сначала получить путь к файлу try { filepath = Paths.get("test.txt"); } c a t c h (InvalidPathException e) { System.out.println("Path Error " + e ) ; return; } // Затем получить канал к этому файлу в пределах // блока t гу-с-ресурсами. try (SeekableByteChannel fChan = Files.newByteChannel(filepath)) { // Зарезервировать буфер mBuf = ByteBuffer.allocate(128); do { // Читать в буфер = fChan.read(mBuf); // Остановиться при достижении конца файла != -1) { // Подготовить буфер для чтения m B u f .rewind(); // Читать байты в буфер и отображать их / / на экране как символы for(int i=0; i < count; i + +) System.out.print((char)mBuf.get()); } } while(count != -1); System.out.pri n t l n (); } catch (IOException e) { System.out.println("I/O Error " + e ) Вот как работает эта программа. Сначала создается объект интерфейса, содержащий относительный путь к файлу по имени t e s t . t x t . Ссылка на этот объект присваивается переменной f i l e p a t h . Затем, при вызове метода newBy- - e C h a n n e l ( ), создается канал, связанный с файлом, указанным переданной переменной f i l e p a t h . Поскольку никаких параметров не определено, файл открывается для чтения. Обратите внимание, что этот канал — объект, управляемый оператором t r y с ресурсами Таким образом, канал автоматически закрывается в конце блока. Затем программа вызывает метод a l l o c a t e () класса B y te B u f f e r , чтобы зарезервировать буфер для хранения содержимого файла вовремя чтения. Ссылка на этот буфер хранится в объекте mBuf. Затем, при вызове метода r e a d (), содержимое файла читается по одному буферу зараз в объект mBuf. Количество прочитанных байтов сохраняется в переменной c o u n t. Затем буфер возобновля 6 5 6 Часть II. Библиотека Java ется при вызове метода rewind ( ). Этот вызов необходим, поскольку после вызова метода read () текущая позиция находится в конце буфера. Ее следует возвратить в начало буфера, чтобы при вызове метода get () байты могли быть прочитаны в объект mBuf . (Напомню, что метод get () определен в классе Byte Buffer.) Поскольку объект mBuf — это байтовый буфер, значения, возвращенные методом get () , являются байтами. Они приводятся к типу char, таким образом, файл может быть отображен как текст. (В качестве альтернативы можно создать буфер, который перекодирует байты в символы, а затем прочитает этот буфер) При достижении конца файла метод read () возвращает значение -1 . Когда это происходит, программа заканчивает работу и канал автоматически закрывается. Обратите внимание на интересный момент программа получает объект интерфейса Path в пределах одного блока try, а затем использует его в другом блоке try для получения канала, связанного с этим путем, и работы с ним. Хотя ничего неправильного, по существу, в этом подходе нет, во многих случаях он может быть упрощен, чтобы использовался только один блок try. В этом случае вызовы методов Paths .get () и newByt eChanne 1 () объединяются. Вот, например, переделанная версия программы, которая использует этот подход Более компактный способ открытия канала. Требует JDK 7. import java.io.*; import java.nio.*; import java.nio.channels.*; import java.n i o .file.*; public class ExplicitChannelRead { public static void main(String a r g s []) { int count; // Здесь канал открывается по пути, возвращенному // методом Paths.get(). // Переменная filepath больше ненужна Зарезервировать буфер mBuf = ByteBuffer.allocate(128); do { // Читать в буфер = fChan.read(mBuf); // Остановиться при достижении конца файла Подготовить буфер для чтения m B u f .rewind(); // Читать байты в буфер и отображать их // на экране как символы for(int i=0; i < count; i++) System.out.print((char)m B u f .g e t ()); } } while(count != -1); System.out.println(); } c a t c h (InvalidPathException e) { System.out.println("Path Error " + e ) ; } catch (IOException e) { Глава 20. Исследование N10 6 5 7 System.out.println("I/O Error " + e ) В этой версии нет необходимости в переменной f i l e p a t h , и оба исключения обрабатываются тем же оператором t r y . Поскольку этот подход компактнее, он используется в остальных примерах данной главы. Конечно, в собственном коде вы можете столкнуться с ситуацией, когда создание объекта интерфейса P a t h должно быть отделено от получения канала. В таких случаях применяется предыдущий подход. Другой способ чтения файла подразумевает соотнесение его с буфером. Преимущество заключается в том, что буфер автоматически получает содержимое файла. Никаких явных операций чтения ненужно. Сопоставление и чтение содержимого файла осуществляются входе следующей общей процедуры. Сначала получите объект интерфейса P a th , инкапсулирующий файл, как описано ранее. Затем получите канал к этому файлу, передав при вызове метода F i l e s . new B y- t e C h a n n e l () объект интерфейса P a t h и приведя тип возвращенного объекта к типу F i l e C h a n n e l . Как упоминалось, метод n e w B y te C h a n n e l () возвращает объект интерфейса S e e k a b le B y t e C h a n n e l . При использовании заданной по умолчанию файловой системы этот объект может быть приведен к типу класса F i l e C h a n n e l . Затем сопоставьте канал с буфером, вызвав метод та р () для канала. Метод та р () определен в классе F i l e C h a n n e l . Вот почему приведение к типу F i l e C h a n n e l необходимо. Вот синтаксис метода тартар как позиция long размер Метод map () сопоставляет данные в файле с буфером в памяти. Значение параметра как определяет, какие операции разрешены. Для него допустимы следующие значения a p M o d e .READ_ONLY M a p M o d e .READ_WRITE M a p M o d e Чтобы читать файл, используйте значение M apM ode. READ_0NLY. Чтобы читать и записывать файл, используйте значение MapMode . READ_WRITE. Значение M apM ode. PRIVATE приводит к созданию закрытой копии файла, чтобы внесенные в буфере изменения не повлияли на основной файл. Позиция начала сопоставления в пределах файла определяется параметром позиция, а количество сопоставляемых байтов — параметром размер Ссылка на этот буфер возвращается как объект класса M a p p e d B y te B u f f e r , который является производным от класса B y te B u f f e r . Как только файл сопоставлен с буфером, вы можете читать файл из этого буфера. Рассмотрим пример, иллюстрирующий этот подход Использование сопоставления для чтения файла. Требует JDK 7. import java.io.*; import java.nio.*; import java.nio.channels.*; import java.nio.file.*; public class MappedChannelRead { public static void main(String a r g s []) { // Получить канал для файла в пределах блока try-с-ресурсами. try ( FileChannel fChan = (FileChannel) Files.newByteChannel(Paths.g e t ("tes t .tx t ")) ) { // Получить размер файла 6 5 Часть II. Библиотека Java long fSize = fChan.s i z e (); // Теперь сопоставить файл с буфером mBuf = fChan.map(FileChannel.MapMode.READ-ONLY, 0, fSize); // Читать и отображать байты из буфера for(int i=0; i < fSize; i++) System.out.print((char)mBuf.get()); 1 System.out.println(); } c a tch(InvalidPathException e) { System.out.println("Path Error " + e) ; } catch (IOException e) { System.out.println("I/O Error " + e) В программе сначала создается путь к файлу, а затем открывается файл при помощи метода newByt eChanne 1 () . Канал приводится к типу FileChannel и сохраняется в объекте fChan. Затем, в результате вызова метода size () для канала, выясняется размер файла. Далее весь файл сопоставляется с областью в памяти при вызове метода шар () для объекта fChan, а ссылка на буфер сохраняется в объекте mBuf. Обратите внимание, что объект mBuf объявляется как ссылка на объект класса MappedByteBuf fer. Байты из объекта mBuf читаются непосредственно методом get (Запись в файл через канал Как ив случае чтения из файла, есть несколько способов записи данных в файл с использованием канала. Начнем с одного из наиболее распространенных. При этом подходе вы вручную резервируете буфер, записываете в него данные, а затем выполняете явную операцию записи этих данных в файл. Прежде чем вы сможете записать файл, его следует открыть. Для этого получите сначала объект интерфейса Path, описывающий файла затем используйте этот путь, чтобы открыть файл. В данном примере файл будет открыт для байтового вывода с помощью явных операций вывода. Поэтому данный пример откроет файл и установит канал к нему при вызове метода Files . newByt eChanne 1 () Как показано в предыдущем разделе, общая форма метода newByt eChanne 1 () такова SeekableByteChannel newByteChanne1 ( Path путь. как Метод возвращает объект интерфейса SeekableByteChannel, инкапсу лирующий канал для работы с файлом..Чтобы открыть файл для вывода, параметр как должен передать значение StandardOpenOpt ion .WRITE. Если файл еще не существует и его необходимо создать, то следует определить также значение StandardOpenOpt ion. CREATE. Другие доступные значения параметра перечислены в табл. 20.7.) Как описано в предыдущем разделе, интерфейс SeekableByteChannel описывает канал, применяемый для файловых операций. Его реализует класс FileChannel. Когда используется файловая система по умолчанию, возвращаемый объект может быть приведен к типу FileChannel. Завершив работу с каналом, его следует закрыть. Один из способов записи в файл через канал подразумевает использование явных вызовов метода write () . Сначала получите объект интерфейса Path для файла, а затем откройте его вызовом метода newByt eChanne 1 () , приводя ре Глава 20. Исследование N10 6 5 9 зультат к типу F i le C h a n n e l . Затем зарезервируйте байтовый буфер и запишите в него данные. Прежде чем данные будут записаны в файл, для буфера следует вызвать метод r e w in d (), чтобы обнулить его текущую позицию. (Каждая операция вывода в буфер увеличивает его текущую позицию. Поэтому перед записью в файл ее следует возвратить в исходное состояние) Далее вызовите для канала метод w r i t e ( ), передав ему буфер. Эту процедуру демонстрирует следующая программа. Она записывает алфавит в файл по имени t e s t . t x t . // Запись в файл с использованием N10. Требует JDK 7. import java.io.*; import java.nio.*; import java.n i o .channels.* ; import java.nio.file.*; public class ExplicitChannelWrite { public static void main(String a r g s []) { // Получить канал для файла в пределах блока try-с-ресурсами. try ( FileChannel fChan = (FileChannel) Files.newByteChannel(Paths.g e t ("tes t .txt"), StandardOpenOption.WRITE, StandardOpenOption.CREATE) ) { // Создать буфер mBuf = ByteBuffer.allocate(26); // Записать несколько байтов в буфер for(int i=0; i<26; i++) mBuf.put((byte) (1A ' + i)); // Подготовить буфер для записи B u f .rewind(); » // Запись буфера в выходной файл fChan.write(mBuf); } c a t c h (InvalidPathException e) { System.out.println("Path Error " + e ) ; } catch (IOException e) { System.out.println("I/O Error: " + e ) Имеет смысл подчеркнуть важный аспект этой программы. Как упоминалось, после записи данных в объект байтового буфера mBuf, однако прежде, чем они будут записаны в файл, для объекта m B u f осуществляется вызов метода r e w i n d (). Это необходимо для обнуления текущей позиции после записи данных в буфер mBuf. Помните каждый вызов метода p u t () объекта m B u f передвигает текущую позицию. Поэтому текущую позицию необходимо вернуть в начало буфера, прежде чем вызывать метод w r i t e () . Если это не будет сделано, то метод w r i t e () будет считать, что никаких данных в буфере нет. Еще один способ обнуления буфера между операциями ввода и вывода подразумевает вызов метода f l i p () вместо метода r e w in d (). Метод f l i p () устанавливает для текущей позиции нулевое значение, а для предела — значение предыдущей текущей позиции. В приведенном выше примере, поскольку емкость буфера 6 6 Часть II. Библиотека совпадала сего пределом, метод f lip () можно использовать вместо метода re wind () . Но эти два метода не являются взаимозаменяемыми во всех случаях. Как правило, следует обнулять буфер между любыми операциями чтения и записи. Например, с учетом приведенного выше примера, следующий цикл запишет алфавит в файл три раза. Обратите особое внимание на вызовы метода rewind (). for(int h=0; h < 3 ; h++) { // Записать несколько байтов в буфер for(int i=0; i<26; i++) mBuf.put((byte)('A' + i)); // Подготовить буфер для записи m B u f .rewind(); // Запись буфера в выходной файл fChan.write(mBuf); // Подготовить буфер для записи снова m B u f Обратите внимание метод rewind () вызывается между каждой операцией чтения и записи. Кроме того, когда буфер будет записываться в файл, первые 26 байт в файле будут содержать вывод. Если файл test.txt существовал ранее, то после выполнения программы первые 26 байт файла test. txt будут содержать алфавита остальная часть файла останется неизменной. Еще один способ записи в файл подразумевает его сопоставление с буфером. Преимущество этого подхода заключается в том, что занесенные в буфер данные будут автоматически записаны в файл. Никаких явных операций записи ненужно. Чтобы сопоставить и записать содержимое файла, мы будем использовать такую общую процедуру. Сначала получите объект интерфейса Path, который инкапсулирует файла затем создайте канал к этому файлу, вызвав метод Files. newByt eChanne 1 () и передав ему объект интерфейса Path. Приведите ссылку, возвращенную методом newByteChannel () , к типу FileChannel. Затем сопоставьте канал с буфером при вызове метода шар () для канала. Метод шар () был подробно описан в предыдущем разделе, а здесь он упомянут для вашего удобства. Вот его общая форма map(FileChannel.MapMode как long позиция long размер Метод map () сопоставляет данные в файле с буфером в памяти. Значение параметра как определяет разрешенные операции. Чтобы писать в файл, параметр как должен содержать значение MapMode. READ_WRITE. Положение начала сопоставления в пределах файла определяет параметр позиция, а количество сопоставляемых байтов — параметр размер. Возвращается ссылка на этот буфер. Как только файл сопоставлен с буфером, вы можете писать в буфер данные и они автоматически будут записываться в файл. Поэтому никаких явных операций записи в канал не нужно. Вот предыдущая программа, переделанная так, чтобы использовался сопоставленный файл. Обратите внимание на то, что в вызове метода newByt eChannel () в параметр добавлено значение StandardOpenOpt ion. READ. Дело в том, что сопоставленный буфер может использоваться или только для чтения, или для чтения и записи. Таким образом, для записи в сопоставленный буфер канал должен быть открыт и для чтения, и для записи Запись в сопоставленный файл. Требует JDK 7. |