Проверить существует ли файл kotlin

Обновлено: 04.07.2024

Мы можем проверить принадлежит ли объект к какому-либо типу во время исполнения с помощью оператора is или его отрицания !is :

Умные приведения

Во многих случаях в Kotlin вам не нужно использовать явные приведения, потому что компилятор следит за is -проверками для неизменяемых значений и вставляет приведения автоматически, там, где они нужны:

Компилятор достаточно умён для того, чтобы делать автоматические приведения в случаях, когда проверка на несоответствие типу ( !is ) приводит к выходу из функции:

или в случаях, когда приводимая переменная находится справа от оператора && или || :

Заметьте, что умные приведения не работают, когда компилятор не может гарантировать, что переменная не изменится между проверкой и использованием. Более конкретно, умные приведения будут работать:

  • с локальными val переменными - всегда;
  • с val свойствами - если поле имеет модификатор доступа private или internal , или проверка происходит в том же модуле, в котором объявлено это свойство. Умные приведения неприменимы к публичным свойствам или свойствам, которые имеют переопределённые getter'ы;
  • с локальными var переменными - если переменная не изменяется между проверкой и использованием и не захватывается лямбдой, которая её модифицирует;
  • с var свойствами - никогда (потому что переменная может быть изменена в любое время другим кодом).

Оператор "небезопасного" приведения

Обычно оператор приведения выбрасывает исключение, если приведение невозможно, поэтому мы называем его небезопасным. Небезопасное приведение в Kotlin выполняется с помощью инфиксного оператора as (см. приоритеты операторов):

Заметьте, что null не может быть приведен к String , так как String не является nullable, т.е. если y - null, код выше выбросит исключение. Чтобы соответствовать семантике приведений в Java, нам нужно указать nullable тип в правой части приведения:

Оператор "безопасного" (nullable) приведения

Чтобы избежать исключения, вы можете использовать оператор безопасного приведения as?, который возвращает null в случае неудачи:

Заметьте, что несмотря на то, что справа от as? стоит non-null тип String , результат приведения является nullable.

Стирание и проверка типов у Обобщений (Generics)

` is erased to just `List `. In general, there is no way to check whether an instance belongs to a generic type with certain type arguments at runtime. -->

Котлин обеспечивает типобезопасность операций, связанных с обобщениями на этапе компиляции(compile time), в то время как информация о типе аргумента обобщения недоступна во время выполнения программы. Например для List<Foo> происходит стирание типа, что превращает его в List<*> . В связи с чем, нет способа проверить, принадлежит ли объект конкретному типу во время выполнения программы.

Учитывая это, компилятор запрещает is-проверки, которые не могут быть выполнены во время выполнения программы из-за стирания типов, например ints is List<Int> или list is T (параметризированный тип). Однако у вас есть возможность произвести проверку со "Звёздными" проекциями:

Таким же образом, когда у вас есть статически определенный тип аргумента, вы можете произвести is-проверку или приведение с не-обобщенной частью типа. Заметье, что в данном случае угловые скобки пропущены:

Аналогичный синтаксис, с пропущенным типом аргумента может использоваться для приведений, которые не принимают типы аргументы: list as ArrayList

Встроенные (inline) функции с параметрами вещественного типа имеют свои аргументы типа, встроенные на каждый момент вызова, что позволяет arg is T проверку параметризованного типа, но если arg является объектом обобщенного типа, его аргумент типа по-прежнему стирается. Пример:

Непроверяемые (Unchecked) приведения

Как упоминалось выше, стирание типов делает невозможным проверку типа аргумента обобщения на этапе выполнения, и обобщенные типы в коде могут быть недостаточно связаны друг с другом, чтобы компилятор обеспечил типобезопасность.

Тем не менее, иногда мы имеем программную логику высокого уровня, которая вместо этого подразумевает типобезопасность. Например:

Компилятор выдает предупреждение для приведения в последней строке. Приведение не может быть полностью проверено во время выполнения и нет дает гарантии, что значения в словаре (map) являются Int .

Чтобы избежать непроверяемые приведения, вы можете изменить структуру программы: в примере выше, возможно объявить интерфейсы DictionaryReader<T> и DictionaryWriter<T> с типобезопасными имплементациями для типизированного типа. Правильно использование вариативности обобщений также может помочь.

Для обобщенных функций, используемых встроенные (inline) функции с параметрами вещественного типа приведение типа arg as T является проверяемым, до тех пор, если тип arg не имеет свои аргументы типа, которые были стерты.

Предупреждение о непроверяемом приведении можно убрать используя аннотации

В JVM, массивы сохраняют информацию о стираемом типе их элементов, а также приведение типов к массиву частично проверяется: nullability и фактические аргументы для параметризированных элементов массива все еще стираются. Например, приведение foo as Array <List <String>?> будет успешным, если foo является массивом, содержащим какой-либо List <*> , null или нет.

Программы, которые мы пишем, так или иначе должны взаимодействовать с пользователем и внешней средой — операционной системой, устройствами компьютера, сетью Интернет. Простейшим способом взаимодействия является ввод с консоли и вывод на консоль, но сейчас такой способ применяется крайне редко. Более совершенным способом взаимодействия являются файлы — многие программы берут из них настроечную или входную информацию, и используют их для сохранения результатов своей работы или различных настроек. Например, всевозможные редакторы позволяют открывать файлы в определённом формате, просматривать и/или изменять их, сохранять файлы на диске компьютера или в сети Интернет.

В библиотеке Java внутри пакета java.io имеется ряд типов, обеспечивающих возможность работы с файлами, а библиотека Котлина дополняет их некоторыми удобными возможностями. Как обычно, рассмотрим часть этих возможностей на примере.

* Во входном файле с именем inputName содержится некоторый текст. * Вывести его в выходной файл с именем outputName, выровняв по левому краю, * чтобы длина каждой строки не превосходила lineLength. * Слова в слишком длинных строках следует переносить на следующую строку. * Слишком короткие строки следует дополнять словами из следующей строки. * Пустые строки во входном файле обозначают конец абзаца, fun alignFile ( inputName : String , lineLength : Int , outputName : String ) <

Краеугольный тип, используемый для работы с файлами в Котлине — тип java.io.File . В соответствии с названием, он предназначен для различных операций с файлами; объект этого типа соответствует какому-либо реальному файлу, чаще всего находящемуся на жёстком диске. Для создания файла используется специальная функция-конструктор: File(inputName) или File(outputName) в примере. Если в аргументе конструктора указано только имя файла — поиск файла происходит в текущей директории, а если аргумент содержим также путь к файлу — то в директории, указанной этим путём. Специфика конструктора заключается в том, что его имя совпадает с типом объекта, которую он создаёт, и он имеет результат соответствующего типа. Более подробно мы поговорим о конструкторах в следующем уроке.

В режиме записи информации, заданное имя может не соответствовать существующему файлу — в этом случае он будет создан. Для записи информации, необходимо создать один из объектов, обеспечивающих такую возможность. В примере, таким объектом является val writer = File(outputName).bufferedWriter() — то есть необходимо вызвать функцию bufferedWriter() на получателе, соответствующем исходному файлу. Как видно из текста примера, writer (писатель) имеет функции writer.newLine() (добавление в файл новой строки), writer.write(string) (добавление в файл заданной строки) и writer.close() (закрытие писателя, выполняется строго ПОСЛЕ выполнения всех остальных действий и фиксирует итоговое состояние файла).

Мы перечислили все файловые операции, присутствующие в исходном примере. Внутри цикла for , каждая из строк файла разбивается по пробелам на слова, с этой целью используется Regex("\\s+") . В currentLineLength накапливается длина текущей строки ВЫХОДНОГО файла. Если в текущей строке достаточно места для очередного слова ВХОДНОГО файла, слово добавляется в текущую строку, в противном случае в файл добавляется перевод строки и слово добавляется в новую строку. Пустые строки входного файла, как и сказано в задании, переносятся в выходной файл без изменений.

Глава 9. Операции ввода-вывода файлов, регулярные выражения и многопоточность

Мы уже ввели характеристики расширений классов в Kotlin в главе 6 «Функции и свойства расширений». Используя расширенную функцию Kotlin, мы можем напрямую реализовать функцию inc () для класса String, которая добавляет 1 к каждому символьному значению в строке.

Эта функция расширения реализована следующим образом

Благодаря мощным функциям расширения мы можем расширить множество «на первый взгляд нативных методов в классах Java» на основе библиотеки классов Java. Фактически, большое количество API в стандартной библиотеке Kotlin kotlin-stdlib реализовано путем расширения классов Java.

Операции файлового ввода-вывода, регулярные выражения и многопоточность, которые мы представим в этой главе, реализованы Kotlin путем расширения существующих классов Java. Сначала давайте введем чтение и запись файлов.

9.1 Файловые операции ввода-вывода

API операций ввода-вывода Kotlin находится в пакете kotlin.io. Принцип Котлина состоит в том, чтобы использовать Java напрямую, если он уже имеет хорошее применение, и расширять его на основе исходного класса, если он плохой или нет. Например, Котлин написал функции расширения для класса File.

Kotlin расширяет класс java.io.File множеством полезных функций расширения, которые находятся в файле исходного кода kotlin / io / FileReadWrite.kt. Мы рассмотрим это ниже.

В то же время, Kotlin также сделал простые расширения для InputStream, OutputStream и Reader. В основном они находятся в следующих двух исходных файлах:

kotlin/io/IOStreams.kt
kotlin/io/ReadWrite.kt

Сериализация Колтина напрямую использует псевдоним типа класса сериализации Java:

Давайте кратко представим операции чтения и записи файла Kotlin. Общие API чтения и записи файлов в Kotlin показаны в следующей таблице

Подпись функции Описание функции
File.readText(charset: Charset = Charsets.UTF_8): String Прочитать все содержимое файла в виде строки
File.readLines(charset: Charset = Charsets.UTF_8): List<String> Прочитайте каждую строку файла, сохраните ее в List <String> и верните
File.readBytes(): ByteArray Прочитать все содержимое файла и вернуть его как ByteArray
File.writeText(text: String, charset: Charset = Charsets.UTF_8): Unit Перезаписать текстовую строку в файл
File.writeBytes(array: ByteArray): Unit Перезаписать массив байтов потоком ByteArray
File.appendText(text: String, charset: Charset = Charsets.UTF_8): Unit Добавить текстовую строку в конец файла
File.appendBytes(array: ByteArray): Unit Добавить ByteArray в конец файла

9.1.1 Чтение файлов

readText: получить всю строку содержимого файла

Если мы просто читаем файл, мы можем использовать метод readText (), который напрямую возвращает все содержимое файла. Пример кода выглядит следующим образом

Мы напрямую используем объект File для вызова функции readText, чтобы получить все содержимое файла, которое возвращает строку. Если вы указываете кодировку символов, вы можете указать ее, передав параметр Charset.

readLines: получить содержимое каждой строки файла

Если мы хотим получить содержимое каждой строки файла, мы можем просто использовать split ("\ n"), чтобы получить массив каждой строки. Мы также можем напрямую вызвать инкапсулированную функцию readLines Kotlin, чтобы получить содержимое каждой строки файла. Функция readLines возвращает список строк, содержащих содержимое каждой строки.

readBytes: читать массив байтов

Если мы хотим напрямую манипулировать байтовым массивом файла, мы можем использовать функцию readBytes

bufferedReader

Получить BufferedReader для файла

9.1.2 Запись файлов

Используя расширенные функции Kotlin, запись в файл также довольно проста. Это похоже на чтение файла. Мы можем записать строку или поток байтов. Мы также можем напрямую вызвать класс Java Writer или OutputStream. Запись файлов обычно делится на два типа: перезапись (однократная запись) и добавление записи.

writeText: перезаписать файл записи

Мы используем функцию writeText для записи содержимого строкового текста непосредственно в файл

Где параметр destFile - это имя целевого файла (с каталогом).

appendFile: добавить запись файла в конце

Добавить запись в конец файла, используя текст функции appendFile

appendBytes

Добавить байтовый массив в файл

bufferedWriter

Получите BufferedWriter для файла

9.1.3. Обход файлового дерева

Kotlin предоставляет удобные функции для обхода дерева файлов.

функция ходьбы: пройтись по дереву файлов

В следующем примере выполняется перебор всех файлов в указанной папке.

Приведенный выше тестовый код выведет все подкаталоги и их файлы в текущем каталоге.

Мы также можем перебрать все файлы подкаталогов ниже текущего файла и сохранить его в Итераторе <File>

Мы перебираем все файлы подкаталогов в текущем файле, а также можем фильтровать по условиям и сохранять результаты в Sequence <File>

Обход дерева файлов требует вызова метода расширения walk (). Он вернет объект FileTreeWalk, и у него есть несколько методов для установки направления и глубины обхода, подробности см. В документации по FileTreeWalk API.

Копировать файлы рекурсивно

Скопируйте файл или рекурсивно скопируйте каталог и все его вложенные файлы по указанному пути. Если файл по указанному пути не существует, он будет создан автоматически.

copyRecursively подпись функции:

9.2 Сетевой ввод-вывод

Ниже мы просто напишем несколько примеров функций.

Получить ответ URL в соответствии с HTML-функцией URL

Получить функцию массива битов ответа URL в соответствии с URL

Записать байтовый массив ответа URL в файл

Следующий пример просто возвращает исходный код домашней страницы Baidu.

В следующем примере выполняется получение потока битов изображения на основе URL-адреса, а затем вызывается метод readBytes () для чтения потока байтов и записи его в файл.

Мы можем увидеть скачанный файл «picture.jpg» в соответствующей папке проекта.

9.3 Выполнение команд оболочки

Мы используем файловые операции ввода-вывода Groovy, которые кажутся очень полезными, такие как

Файловый IO и сетевой IO-операции в Kotlin так же просты, как Groovy.

Кроме того, из приведенного выше кода мы видим, что выполнить команды терминала с помощью Groovy очень просто:

В Kotlin такие функции, как класс String и Process, еще не расширены. На самом деле, расширение такой функции очень просто. Мы можем расширить себя.

Во-первых, давайте расширим функцию execute () строки.

Затем давайте расширим текстовую функцию до класса Process.

После выполнения двух простых функций расширения, описанных выше, мы можем выполнить команды терминала, такие как Groovy, в следующем тестовом коде:

Фактически, благодаря изучению многих предыдущих примеров, мы можем видеть, что функции расширения Котлина весьма практичны. Язык API Kotlin сам по себе широко использует расширения.

9.4 Регулярные выражения

В Kotlin, в дополнение к прежнему использованию Java Pattern, Matcher и других классов, Kotlin также предоставляет класс регулярного выражения kotlin / text / regex / Regex.kt. Мы используем конструктор Regex для создания регулярного выражения ,

9.4.1. Построение выражения регулярного выражения

Используйте конструктор Regex

Параметр сопоставления RegexOption является обычным параметром сопоставления в шаблоне класса Java, который используется напрямую.

Функция расширения ToRegex с использованием String

9.4.2 Функция регулярного выражения

Regex предоставляет множество простых и практичных функций, как показано в следующей таблице

Имя функции Описание функции
matches(input: CharSequence): Boolean Все входные строки совпадают
containsMatchIn(input: CharSequence): Boolean У входной строки есть хотя бы одно совпадение
matchEntire(input: CharSequence): MatchResult? Все входные строки совпадают, возвращают соответствующий объект результата
replace(input: CharSequence, replacement: String): String Заменить совпадающую часть входной строки содержимым замены
replace(input: CharSequence, transform: (MatchResult) -> CharSequence): String Замените совпадающее значение во входной строке новым значением после преобразования преобразования функции
find(input: CharSequence, startIndex: Int = 0): MatchResult? Возвращает первое совпадающее значение во входной строке
findAll(input: CharSequence, startIndex: Int = 0): Sequence<MatchResult> Возвращает последовательность всех совпадающих значений во входной строке

Ниже мы приведем простые примеры вышеуказанных функций соответственно.

matches

Все входные строки соответствуют регулярному выражению и возвращают true, в противном случае возвращается false.

containsMatchIn

Возвращает true, если во входной строке есть хотя бы одно совпадение, и false, если совпадения нет.

matchEntire

Все входные строки соответствуют регулярному выражению и возвращают объект MatcherMatchResult, в противном случае он возвращает ноль.

Мы можем получить доступ к значению MatcherMatchResult, чтобы получить соответствующее значение.

Поскольку возвращением функции matchEntire является объект MatchResult? Nullable, здесь мы используем символ безопасного вызова ?. 。

replace(input: CharSequence, replacement: String): String

Замените соответствующую часть входной строки содержимым замены.

Мы можем видеть это в "12345XYZ" 12345 Соответствует регулярному выражению 2+ Содержание было заменено abcd 。

replace функция

Функция замены подписи выглядит следующим образом

Его функция заключается в замене сопоставленного значения во входной строке новым значением после преобразования преобразования функции.

Мы можем видеть, что 9XYZ8 Числа 9 и 8 соответствуют регулярным выражениям 2+ Содержимое, они соответственно отображаются с помощью функции преобразования (it.value.toInt() * it.value.toInt()).toString() Замените новые значения 81 и 64.

find функция

Возвращает первый соответствующий объект MatcherMatchResult во входной строке.

findAll

Возвращает последовательность MatchResults для всех совпадающих значений во входной строке.

Мы можем перебрать все совпадающие значения через цикл forEach

9.4.3. Классы регулярных выражений с использованием Java

В дополнение к функциям, предоставляемым Kotlin выше, мы все еще можем использовать API регулярных выражений Java в Kotlin.

Приведенный выше код запускает вывод:

9.5 Многопоточное программирование

У Kotlin нет синхронизированных и изменчивых ключевых слов. Kotlin Any похож на Java Object, но не имеет методов wait (), notify () и notifyAll ().

Так как же работает параллелизм в Котлине? Будьте уверены, поскольку Kotlin стоит на плечах Java, конечно, поддержка многопоточного программирования неизбежна - Kotlin упрощает наше кодирование, инкапсулируя классы потоков в Java. В то же время мы также можем использовать некоторые конкретные аннотации и напрямую использовать ключевые слова синхронизации в Java. Давайте кратко представим соответствующий контент многопоточного программирования с Kotlin.

9.5.1 Создать тему

У нас обычно есть два метода в Java для создания потоков в Java:

  • Расширение класса Thread
  • Или создайте его экземпляр и передайте Runnable через конструктор

Поскольку мы легко можем использовать классы Java в Kotlin, можно использовать оба метода.

Создание с использованием выражений объекта

Этот код использует объектное выражение Kotlin для создания анонимного класса и переопределяет метод run ().

Использование лямбда-выражений

Вот как передать Runnable во вновь созданный экземпляр Thread:

Мы не видим здесь Runnable. В Kotlin мы можем легко использовать вышеприведенное лямбда-выражение для его выражения.

Есть ли более простой способ? Посмотрите на объяснение ниже.

Использование Kotlin-обернутых потоковых функций

Например, мы написали следующий код потока

Следующие четыре строки можно назвать стандартным кодом. Инкапсуляция таких операций упрощена в Kotlin.

Этот код выглядит более упорядоченным и аккуратным. Фактически функция thread () является абстрактной инкапсуляцией стандартного кода, часто используемого в нашей практике программирования.

Это просто очень удобная функция обертки, простая и практичная. Из приведенного выше примера мы видим, что Kotlin упростил шаблонный код, расширив API-интерфейс потоков Java.

9.5.2. Методы и блоки синхронизации

синхронизированный - это не ключевое слово в Kotlin, оно заменяется аннотацией @Synchronized. Объявление синхронного метода в Kotlin будет выглядеть так:

Аннотация @Synchronized имеет тот же эффект, что и синхронизация в Java: она помечает методы JVM как синхронизированные. Для синхронизированных блоков мы используем функцию synchronized (), которая принимает блокировку в качестве параметра:

В основном так же, как Java.

9.5.3 Переменное поле

Аналогично, у Kotlin нет ключевого слова volatile, но есть аннотация @Volatile.

@Volatile помечает поля резервного копирования JVM как изменяемые.

Конечно, у нас есть лучшие параллельные библиотеки сопрограмм в Котлине. В практике разработки кода мы можем свободно выбирать в соответствии с реальной ситуацией.

Краткое содержание этой главы

Kotlin - очень практичный инженерный язык: из файлового ввода-вывода, регулярных выражений и многопоточности, представленных в этой главе, мы можем понять основные принципы Kotlin: в полной мере использовать существующую экологическую библиотеку Java и опираться на На нем есть более простые и практичные расширения, которые значительно повышают производительность труда программистов. Из этого мы также поняли минималистскую концепцию программирования Kotlin - постоянно абстрагируя, инкапсулируя и расширяя, делая ее более простой и практичной

  • Open with Desktop
  • View raw
  • Copy raw contents Copy raw contents Loading

Copy raw contents

Copy raw contents

7. Файловые операции

Программы, которые мы пишем, так или иначе должны взаимодействовать с пользователем и внешней средой — операционной системой, устройствами компьютера, сетью Интернет. Простейшим способом взаимодействия является ввод с консоли и вывод на консоль, но сейчас такой способ применяется крайне редко. Более совершенным способом взаимодействия являются файлы — многие программы берут из них настроечную или входную информацию, и используют их для сохранения результатов своей работы или различных настроек. Например, всевозможные редакторы позволяют открывать файлы в определённом формате, просматривать и/или изменять их, сохранять файлы на диске компьютера или в сети Интернет.

В библиотеке Java внутри пакета java.io имеется ряд типов, обеспечивающих возможность работы с файлами, а библиотека Котлина дополняет их некоторыми удобными возможностями. Как обычно, рассмотрим часть этих возможностей на примере.

Краеугольный тип, используемый для работы с файлами в Котлине — тип java.io.File . В соответствии с названием, он предназначен для различных операций с файлами; объект этого типа соответствует какому-либо реальному файлу, чаще всего находящемуся на жёстком диске. Для создания файла используется специальная функция-конструктор: File(inputName) или File(outputName) в примере. Если в аргументе конструктора указано только имя файла — поиск файла происходит в текущей директории, а если аргумент содержим также путь к файлу — то в директории, указанной этим путём. Специфика конструктора заключается в том, что его имя совпадает с типом объекта, которую он создаёт, и он имеет результат соответствующего типа. Более подробно мы поговорим о конструкторах в следующем уроке.

Обмен данными с файлом может происходить в режиме чтения либо в режиме записи. В режиме чтения информации, заданное имя должно соответствовать уже существующему файлу. Один из способов получения информации из файла — вызов функции file.readLines() . Результат вызова — список строк, из которых состоит файл. Каждый String в этом списке соответствует одной строке файла, строки файла разделяются символом "возврат каретки" и / или "новая строка".

В режиме записи информации, заданное имя может не соответствовать существующему файлу — в этом случае он будет создан. Для записи информации, необходимо создать один из объектов, обеспечивающих такую возможность. В примере, таким объектом является val writer = File(outputName).bufferedWriter() — то есть необходимо вызвать функцию bufferedWriter() на получателе, соответствующем исходному файлу. Как видно из текста примера, writer (писатель) имеет функции writer.newLine() (добавление в файл новой строки), writer.write(string) (добавление в файл заданной строки) и writer.close() (закрытие писателя, выполняется строго ПОСЛЕ выполнения всех остальных действий и фиксирует итоговое состояние файла).

Мы перечислили все файловые операции, присутствующие в исходном примере. Внутри цикла for , каждая из строк файла разбивается по пробелам на слова, с этой целью используется Regex("\\s+") . В currentLineLength накапливается длина текущей строки ВЫХОДНОГО файла. Если в текущей строке достаточно места для очередного слова ВХОДНОГО файла, слово добавляется в текущую строку, в противном случае в файл добавляется перевод строки и слово добавляется в новую строку. Пустые строки входного файла, как и сказано в задании, переносятся в выходной файл без изменений.

За занавесом: чтение из файла

Пакет java.io позволяет работать с файлами на трёх разных уровнях:

Уровень отдельных байт. В этом случае файл воспринимается как массив или, точнее, как поток байт. Поток, в отличие от массива, можно только перебирать, с сильно ограниченными возможностями по возвращению назад. Для этой цели имеется тип java.io.InputStream .

Уровень символов. В этом случае файл воспринимается уже как поток символов типа Char , то есть каждые несколько байт файла превращаются в определённый символ — с учётом заданной кодировки файла. Для этой цели имеется тип java.io.InputStreamReader , который внутри себя использует InputStream для чтения байт.

Уровень строк. На этом уровне файл воспринимается как набор строк String , составленных из символов по определённым правилам — чаще всего используется разделение по отдельным строкам файла. Эту роль выполняет тип java.io.BufferedReader , использующий внутри себя InputStreamReader для чтения символов.

При программировании на Java каждый из этих объектов приходится создавать отдельно — вначале InputStream , потом InputStreamReader и, наконец, BufferedReader . Библиотека Котлина позволяет создать любой из этих объектов сразу, используя файл-получатель:

file.inputStream() создаёт байтовый поток.

file.reader() создаёт читатель символов, используя кодировку по умолчанию. file.reader(Charset.forName("CP1251")) создаёт писатель с заданной кодировкой (в данном случае CP1251).

Наконец, file.bufferedReader() создаёт буферизованный читатель строк. Опять-таки, может быть задана нужная кодировка, иначе используется кодировка по умолчанию.

Набор функций у данных трёх объектов различается. У всех у них есть функция close() , закрывающая исходный файл в конце работы с потоком. Также, у них имеется функция высшего порядка use < …​ >, выполняющая описанные в лямбде действия и закрывающая файл в конце своей работы автоматически. Скажем, исходный пример можно было бы переписать с помощью use так:

Здесь исходный BufferedWriter в лямбде становится параметром it . Заметим, что при использовании use исходный файл будет закрыт как при корректном завершении функции, так и при возникновении исключения.

Кроме этого, каждый объект обладает своими методами для чтения информации:

inputStream.read() читает из InputStream очередной байт, возвращая его в виде результата типа Int . Если файл закончен, результат этой функции будет -1. inputStream.read(byteArray) читает сразу несколько байт, записывая их в массив байт (число прочитанных байт равно размеру массива). inputStream.read(byteArray, offset, length) записывает в byteArray length байт, начиная с индекса offset .

reader.read() читает из InputStreamReader очередной символ, возвращая его в виде результата типа Int . Здесь используется именно Int , а не Char , так как, во-первых, символ в общем случае может не поместиться в двухбайтовые тип и, во-вторых, чтобы вернуть -1 в случае неудачи. Есть аналогичные методы для чтения символьного массива (НЕ строки) с возможным указанием смещения и числа символов — см. выше про байтовый массив.

bufferedReader.readLine() читает из BufferedReader очередную строку (до перевода строки). bufferedReader.readLines() читает сразу же все строки. Есть ряд других методов для работы со строками по отдельности.

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

В примере, мы вообще не создавали bufferedReader , а использовали функцию file.readLines() . Она создаёт bufferedReader внутри себя и обращается к его функции readLines() . После чтения последней строки файл закрывается.

За занавесом: запись в файл

Запись в файл использует те же три уровня: байты OutputStream , символы OutputStreamWriter и строки BufferedWriter . Для записи байт либо символов используются функции write , аргументом которых может являться целое число (в котором хранится байт или код символа) или массив (опять-таки байт или символов). Эти функции не имеют результата и бросают IOException , если файл недоступен для записи. BufferedWriter может использовать функцию write также для записи строк. Как и все три вида потоков чтения, потоки записи необходимо закрывать после использования с помощью close() или use < …​ >.

Сверх этого, для записи часто используется так называемый поток печати PrintStream . В Котлине его можно создать из файла, используя функцию file.printStream() . Поток печати расширяет обычный байтовый поток рядом дополнительных возможностей:

printStream.println(…​) — вывод заданной строки или строкового представления с последующим переходом на новую строку.

printStream.print(…​) — то же, но без перехода на новую строку.

printStream.format(formatString, …​) — форматированный вывод (происходит по принципу, описанном в разделе 6).

Откройте файл srс/lesson7/task1/Files.kt в проекте KotlinAsFirst . Он содержит ряд задач, каждая из которых предполагает наличие входного и/или выходного файла. Решите хотя бы одну-две из имеющихся задач, используя описанные в этом разделе приёмы. Обратите внимание на задачи, помеченные как "Сложная" или "Очень сложная", попробуйте решить одну из них.

Протестируйте свою реализацию, используя тесты из test/lesson7/task1/Tests.kt . Обратите внимание, что тесты используют готовые входные файлы, расположенные в директории input нашего проекта. Убедитесь, что тесты успешно проходят, обязательно создайте два-три дополнительных теста. Постарайтесь внутри этих тестов проверить какие-либо необычные ситуации, которые могут возникнуть в выбранной вами задаче.

Поздравляем! Выполнив упражнения по этому разделу, вы успешно завершили базовую часть нашего курса. Если вас интересует получение сертификата, прочитайте следующий параграф.

Получение сертификата Coursera / экзамен

Если вы проходили данный курс на Coursera, после загрузки последнего задания вам придёт письмо от Coursera со ссылкой "Получить сертификат о прохождении курса". Нажмите на данную ссылку и следуйтё указаниям сайта Coursera.

Если вы проходили данный курс на кафедре КСПТ СПбПУ, вы также можете получить сертификат Coursera, если задания курса выполнены до окончания зачётной недели. За информацией о регистрации на курс обратитесь к лектору курса. После этого вам необходимо загрузить на Coursera все сделанные задания, и дальнейшая процедура будет аналогичной.

Экзамен по данному курсу на кафедре КСПТ СПбПУ заключается в решении экзаменационной задачи (напоминающей задачи данного урока средней сложности). Для получения положительной оценки необходимо продемонстрировать экзаменатору работающее на компьютере решение и проверяющие его тесты.

В каждом билете отдельно приведены требования, необходимые для получения оценок "удовлетворительно", "хорошо" и "отлично". Студент должен быть в состоянии объяснить экзаменатору своё решение, использованные в нём элементы языка и стандартной библиотеки. От претендентов на высокую оценку ожидается также способность обосновать, почему задача решена именно так и провести сравнение с другими возможными вариантами решения.

На экзамене разрешается пользоваться конспектом, Интернетом, любыми книгами. Запрещается общаться с кем-либо, кроме экзаменатора, пользоваться электронной почтой, социальными сетями и другими средствами общения.

Максимальная длительность экзамена 2 часа 45 минут. Из них 2 часа отводится на решение задачи и 30 минут на исправление замечаний экзаменатора, 15 минут остаются в резерве.

Читайте также: