Занятие №4

Работа с файловой системой. Потоки ввода/вывода. Обработка исключений

Соколов

Никита

Немного о себе

Соколов Никита Николаевич

Старший разработчик в компании

"Сибирские интеграционные системы"

Опыт работы:

  • 2008-2010 ФГУП "Почта России"
  • 2010-2010 ООО "Квазар" г.Норильск
  • 2010-2014 ОАО ЗФ "Норильский никель"
  • 2014 по н.в. работаю в ООО "Сибирские интеграционные системы"
Работа с файловой системой - Немного об основах
- Классы / API
- Потоки ввода/вывода
- Практика

Что вас ждет на этой лекции

Обработка исключений - Что это
- Зачем это
- Как применять
- Интерфейс "AutoCloseable"
- Типы исключений

Одним прекрасным днем вы понимаете ...

Мне Нужно поработать
с файлами

* Ввод информации
- Пользовательские данные
- Параметры приложения
* Вывод результата
- Результат обработки
- Промежуточная информация

Для чего ?

Где мои файлы ?

В классе                     !

Позволяет работать:
- С отдельными файлами
- С каталогами
boolean exists() Существует?
boolean isDirectory() Это директория?
boolean isFile() Это файл?
String getName() Название файла
String getPath() Путь к файлу
String getAbsolutePath() Абсолютный путь к файлу
String getCanonicalPath() Абсолютный канонический путь
boolean canRead() Доступ на чтение (атрибут)
boolean canWrite() Доступ на запись​ (атрибут)
boolean canExecute() Доступ на исполнение (атрибут)
File[] listFiles(FileFilter filter) список файлов по фильтру
boolean mkDir() создание каталога
boolean mkDirs() создание каталога с подкаталогами
int length() размер файла

Практика

Разберемся с некоторыми методами класса File

( Полное описание в ссылке в заголовке )

Java = кроссплатформенность

String dir = "C:/CON/PRN/CLOCK$/NUL/";

?

Как избавиться от сложности?

if (isWindows())
  return "path\to\ubuntu\distr\\";

else if (isLinux())
  return "keep/calm/and/use/linux/";

else
  return "who are you man ?!";

Что будет с нашими строковыми путями в другой операционной системе?

Будут ошибки :(

Используйте Path

Path resolve(String other) Добавить подкаталог
Path resolve(Path other) Добавить подкаталог
File toFile() Преобразовать в File
Path subPath(int beginIndex, int endIndex) Выбрать часть пути
Path getParent() Получить путь до родителя

Практика

Разберемся с некоторыми методами класса  Path

( Полное описание в ссылке в заголовке )

Paths.get("C:/temp1/temp2/someFile.txt");

Paths.get("C:/", "temp1", "temp2", "someFile.txt");

Используйте фабрику Paths:

Почему не просто
new SomePathImpl() ?

Path - это интерфейс. А как создать экземпляры ?

public final class Paths {
  private Paths() {
  }

  public static Path get(String first, String... more) {
    return Path.of(first, more);
  }

  public static Path get(URI uri) {
    return Path.of(uri);
  }
}

Под капотом метода get

public interface Path extends Comparable<Path>, Iterable<Path>, Watchable {
  
  ...
  
  static Path of(String first, String... more) {
    return FileSystems.getDefault().getPath(first, more);
  }

Платформо-зависимая имплементация!

Но для чего знать все эти  пути ?

А для того, чтобы считывать файлы и писать в них!

Хорошо, но как ?

Files.lines(Paths.get("some.file"))
  .forEach(System.out::println);
Files.write(Paths.get("some.file"),
  Arrays.asList("А", "и", "Б",
    "Сидели на трубе", "..."));

Читаем файл:

Пишем файл:

 Пример использования Files

Magic!

И всё ?

Конечно же, нет...

Потоки

Вывод
результата

Входные
данные

InputStream
FileInputStream
ByteArrayInputStream 
OutputStream
FileOutputStream
ByteArrayOutpuStream 

Диаграмма классов Stream

Path path = Paths.get(".")
  .resolve("inputAuthors.csv");

File inputFile = path.toFile();

InputStream inputStream =
  new FileInputStream(inputFile);

while (true) {
  int read = inputStream.read();
  if (read == -1) {
    break;
  }
  System.out.print((char) read);
}

inputStream.close();

Читаем файл

1. Получить путь к файлу

2. Получить объект File

3. Входящий поток

4. Читать поток в цикле

5. Пока результат чтения не -1

6. Вывести полученный символ

7. Закрыть поток

Используйте FileInputStream

Path inputPath = Paths.get("inputAuthors.csv");
File inputFile = inputPath.toFile();
InputStream fis = new FileInputStream(inputFile);

Path copyPath = Paths.get(".", "copyAuthors.csv");
File copyFile = copyPath.ToFile();
OutputStream fos = new FileOutputStream(copyFile);

byte[] buffer = new byte[4096];
while (true) {
  int read = fis.read(buffer);
  if (read == -1) {
    break;
  }
  fos.write(buffer, 0, read);
}

fis.close();
fos.close();

Пишем файл

1. Входящий поток

2. Исходящий поток копии

3. Читать входящий поток в переменную read

5. Запись в исходящий поток

6. Закрыть потоки

Используйте FileOutputStream

byte[] buffer = new byte[4096];
while (true) {
  int read = fis.read(buffer);
  if (read == -1) {
    break;
  }
  fos.write(buffer, 0, read);
}

Буфер

Чтение определенного размера данных за раз

Jav

a_C

ool

a

J

v

a

_

C

o

o

l

!

Поток

while (true) {
  int read = inputStream.read();
  if (read == -1) {
    break;
  }
  System.out.print((char) read);
}

Побайтовое чтение

Из примера с чтением:

Из примера с копированием:

!

А как работать с текстовыми данными ?

Символьные потоки

Reader / Writer

Path path = Paths.get("inputAuthors.csv");
File file = path.toFile();
InputStream fis = new FileInputStream(file);

Reader reader = new InputStreamReader(
  fis, StandardCharsets.UTF_8);

BufferedReader lineReader = 
  new BufferedReader(reader);

while (true) {
  String readLine = lineReader.readLine();
  if (Objects.isNull(readLine)) {
    break;
  }
  System.out.println(readLine);
}

lineReader.close();

Читаем текстовый файл

Создать BufferedReader - декоратор Reader, позволяющий работать с буфером.

Для чтения строки вызвать специальный метод readLine.

Закрывать объект Reader входящего потока не нужно. Достаточно закрыть BufferedReader.

А можно проще ?

Path path = Paths.get("inputAuthors.csv");
String charset = StandardCharsets.UTF_8.name;

Scanner scanner = new Scanner(path, charset);

while (scanner.hasNext()) {
  System.out.println(scanner.nextLine());
}

scanner.close();

Можно проще!

Используйте Scanner

VS

Path path = Paths.get("inputAuthors.csv");
File file = path.toFile();
InputStream fis = new FileInputStream(file);

Reader reader = new InputStreamReader(
  fis, StandardCharsets.UTF_8);

BufferedReader lineReader = 
  new BufferedReader(reader);

while (true) {
  String readLine = lineReader.readLine();
  if (Objects.isNull(readLine)) {
    break;
  }
  System.out.println(readLine);
}

lineReader.close();
Path path = Paths.get(
  "/","tmp", "test-file.log");

Writer writer = new FileWriter(
  path.toFile(),
  StandardCharsets.UTF_8);

Writer buffer = new BufferedWriter(writer);

List<String> list = Arrays.asList(
  "Hello", " ", "world", "!");

for (String o : list) {
  buffer.write(o);
}

buffer.close();

Пишем текстовый файл

1. Создать BufferedWriter

2. Записать данные

3. Закрыть BufferedWriter

А если на write произойдет ошибка?

for (String o : list) {
  buffer.write(o);
}

buffer.close();

Буфер не будет закрыт!

Чем грозит?

1. Утечка памяти

2. Файл останется заблокированным

3. Может закончиться лимит на открытые файлы OS

Обработчик

Как избежать?

Программа

Обрабатывать Исключения

Делает что-то недопустимое ....

Пытается устранить проблему

Stacktrace

Метод НачатьИгру()

Метод БроситьМяч()

Метод ОтбитьМяч()

Метод ПойматьМяч()

Мяч в ловушке

Присудить очко команде нападения

Пример стэка

try {
  BufferedWriter buffWriter =
    Files.newBufferedWriter(path);
      
  //Блок кода бросающий исключение IOException
  //Например кончилось место на диске
  buffWriter.write("someText");

  buffWriter.close();
}
catch (IOException e) {
  buffWriter.close();
}

Как поймать?

Используйте Try - Catch

1. Обернуть текст в блок Try

2. Вынести обработку исключения в блок Catch

А если другое исключение?

try {
  BufferedWriter buffWriter =
    Files.newBufferedWriter(path);
      
  //Блок кода бросающий исключение IOException
  //Например кончилось место на диске
  buffWriter.write("someText");

  //Блок кода, бросающий
  //непредвиденное исключение
  throw new IllegalArgumentException();

  buffWriter.close();
}
catch (IOException e) {
  buffWriter.close();
} finally {
  buffWriter.close();
}

Используйте Finally

Вынести обработку любого непредвиденного исключения в блок Finally

Что с этим кодом все еще не так?

try {
  BufferedWriter buffWriter =
    Files.newBufferedWriter(path);
      
  //Блок кода бросающий исключение IOException
  //Например кончилось место на диске
  buffWriter.write("someText");

  //Блок кода, бросающий
  //непредвиденное исключение
  throw new IllegalArgumentException();

  buffWriter.close();
}
catch (IOException e) {
  buffWriter.close();
} finally {
  buffWriter.close();
}

Если Writer не был создан из-за ошибки

При закрытии будет вызвано другое исключение!

А можно проще ?

Используйте try-with-resources

try (BufferedReader lineReader = new BufferedReader(reader)) {

  while (true) {
    String line = readLine = lineReader.readLine();
    if (line == null) {
      break;
    }
    System.out.println(readLine);
  }
}

Любой наследник AutoCloseable может быть использован как ресурс.

Вернемся к примеру с чтением файла

  • Имплементируем
  • Переопределяем метод close()
  • Используем в блоке try {}

 Интерфейс AutoCloseable

Типы исключений

Throwable

Error

Exception

RuntimeException

Checked

Unchecked

- OutOfMemoryError
- StackOverFlowError
- LinkageError
- IOException
- FileNotFoundException
- ArithmeticException
- IndexOutOfBoundsException
- NullPointerException

Полезные сведения

Полезные библиотеки

IOAdapter<Author> authorAdapter = 
  new CSVAdapterImpl<>(Author.class,authorFileReader,authorFileWriter);

Author author = authorCsvAdapter.read(0);
assertEquals("Лев Николаевич Толстой",author.getName());
assertEquals("Ясная Поляна",author.getBirthPlace());

Author authorNew = new Author("Некоторый Автор", "Некоторый Город");

int rowIndex = authorCsvAdapter.append(authorNew);
Author authorNewOpened = authorCsvAdapter.read(rowIndex);

assertEquals("Некоторый Автор",author.getName());
assertEquals("Некоторый Город",author.getBirthPlace());

IOAdapter<Book> bookAdapter = 
  new CSVAdapterImpl<>(Book.class,bookFileReader,bookFileWriter);

...

Домашнее задание

1

1

2

IOAdapter<Author> authorAdapter = 
  new CSVAdapterImpl<>(Author.class,authorFileReader,authorFileWriter);
...
IOAdapter<Book> bookAdapter = 
  new CSVAdapterImpl<>(Book.class,bookFileReader,bookFileWriter);

1. Описать интерфейс и имплементацию для работы с CSV-файлами любых сущностей.

В примере используются Book и Author.
Вы также можете придумать и свои типы.

int rowIndex = authorCsvAdapter.append(authorNew);
Author authorNewOpened = authorCsvAdapter.read(rowIndex);

Обратите внимание на конструкцию методов.

2. Проверить запись и чтение файлов при помощи полученного адаптера.

  • Проверить чтение из определенной строки
  • Проверить запись. Новая строка в файле должна соответствовать поданной в метод записи сущности
Author author = authorCsvAdapter.read(0);
assertEquals("Лев Николаевич Толстой",author.getName());
assertEquals("Ясная Поляна",author.getBirthPlace());

Author authorNew = new Author("Некоторый Автор", "Некоторый Город");
int rowIndex = authorCsvAdapter.append(authorNew);
Author authorNewOpened = authorCsvAdapter.read(rowIndex);
assertEquals("Некоторый Автор",author.getName());
assertEquals("Некоторый Город",author.getBirthPlace());
Made with Slides.com