Программы, которые мы пишем, так или иначе должны взаимодействовать с пользователем и внешней средой — операционной системой, устройствами компьютера, сетью Интернет. Простейшим способом взаимодействия является ввод с консоли и вывод на консоль, но сейчас такой способ применяется крайне редко. Более совершенным способом взаимодействия являются файлы — многие программы берут из них настроечную или входную информацию, и используют их для сохранения результатов своей работы или различных настроек. Например, всевозможные редакторы позволяют открывать файлы в определённом формате, просматривать и/или изменять их, сохранять файлы на диске компьютера или в сети Интернет.
В библиотеке Java внутри пакета java.io имеется ряд типов, обеспечивающих возможность работы с файлами, а библиотека Котлина дополняет их некоторыми удобными возможностями. Как обычно, рассмотрим часть этих возможностей на примере.
import java.io.File
/**
* Пример
*
* Во входном файле с именем inputName содержится некоторый текст.
* Вывести его в выходной файл с именем outputName, выровняв по левому краю,
* чтобы длина каждой строки не превосходила lineLength.
* Слова в слишком длинных строках следует переносить на следующую строку.
* Слишком короткие строки следует дополнять словами из следующей строки.
* Пустые строки во входном файле обозначают конец абзаца,
* их следует сохранить и в выходном файле
*/
fun alignFile(inputName: String, lineLength: Int, outputName: String) {
val writer = File(outputName).bufferedWriter()
var currentLineLength = 0
for (line in File(inputName).readLines()) {
if (line.isEmpty()) {
writer.newLine()
if (currentLineLength > 0) {
writer.newLine()
currentLineLength = 0
}
}
for (word in line.split(Regex("\\s+"))) {
if (currentLineLength > 0) {
if (word.length + currentLineLength >= lineLength) {
writer.newLine()
currentLineLength = 0
}
else {
writer.write(" ")
currentLineLength++
}
}
writer.write(word)
currentLineLength += word.length
}
}
writer.close()
}
Краеугольный тип, используемый для работы с файлами в Котлине — тип 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накапливается длина текущей строки ВЫХОДНОГО файла. Если в текущей строке достаточно места для очередного слова ВХОДНОГО файла, слово добавляется в текущую строку, в противном случае в файл добавляется перевод строки и слово добавляется в новую строку. Пустые строки входного файла, как и сказано в задании, переносятся в выходной файл без изменений.