Программы, которые мы пишем, так или иначе должны взаимодействовать с пользователем и внешней средой — операционной системой, устройствами компьютера, сетью Интернет. Простейшим способом взаимодействия является ввод с консоли и вывод на консоль, но сейчас такой способ применяется крайне редко. Более совершенным способом взаимодействия являются файлы — многие программы берут из них настроечную или входную информацию, и используют их для сохранения результатов своей работы или различных настроек. Например, всевозможные редакторы позволяют открывать файлы в определённом формате, просматривать и/или изменять их, сохранять файлы на диске компьютера или в сети Интернет.
В библиотеке 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
накапливается длина текущей строки ВЫХОДНОГО файла. Если в текущей строке достаточно места для очередного слова ВХОДНОГО файла, слово добавляется в текущую строку, в противном случае в файл добавляется перевод строки и слово добавляется в новую строку. Пустые строки входного файла, как и сказано в задании, переносятся в выходной файл без изменений.