Автор оригинала: Guest Contributor.
Вступление
В этой статье мы рассмотрим как читать и записывать CSV-файлы в Kotlin , в частности, с помощью Apache Commons.
Зависимость от Apache Commons
Поскольку мы работаем с внешней библиотекой, давайте продолжим и импортируем ее в наш проект Kotlin. Если вы используете Maven, просто включите commons-csv
зависимость:
org.apache.commons commons-csv 1.5
Или, если вы используете Gradle:
implementation 'org.apache.commons:commons-csv:1.5'
Наконец, с библиотекой, добавленной в наш проект, давайте определим CSV – файл, который мы будем читать – students.csv
:
101,John,Smith,90 203,Mary,Jane,88 309,John,Wayne,96
Он будет расположен в разделе /ресурсы/students.csv
.
Кроме того, поскольку мы будем считывать эти записи в пользовательские объекты, давайте создадим класс данных:
data class Student ( val studentId: Int, val firstName: String, val lastName: String, val score: Int )
Чтение CSV-файла в Kotlin
Давайте сначала прочитаем этот файл с помощью BufferedReader
, который принимает Путь
к ресурсу, который мы хотели бы прочитать:
val bufferedReader = new BufferedReader(Paths.get("/resources/students.csv"));
Затем, как только мы прочитаем файл в буфер, мы сможем использовать сам буфер для инициализации экземпляра CSVParser
:
val csvParser = CSVParser(bufferedReader, CSVFormat.DEFAULT);
Учитывая, насколько изменчивым может быть формат CSV – чтобы избавиться от догадок, вам придется указать формат CSV
при инициализации анализатора. Этот синтаксический анализатор, инициализированный таким образом, может быть использован только для этого формата CSV.
Поскольку мы следуем учебному примеру формата CSV и используем разделитель по умолчанию, запятую ( ,
), мы передадим CSVFormat.ПО УМОЛЧАНИЮ
в качестве второго аргумента.
Теперь CSVParser
является итерируемым
, который содержит CSVRecord
экземпляры. Каждая строка представляет собой запись CSV. Естественно, затем мы можем выполнить итерацию по экземпляру csvParser
и извлечь из него записи:
for (csvRecord in csvParser) { val studentId = csvRecord.get(0); val studentName = csvRecord.get(1); val studentLastName = csvRecord.get(2); var studentScore = csvRecord.get(3); println(Student(studentId, studentName, studentLastName, studentScore)); }
Для каждой записи CSV
вы можете получить соответствующие ячейки с помощью метода get()
и ввести индекс ячейки , начиная с 0
. Затем мы можем просто использовать их в конструкторе нашего класса Student
data.
Этот код приводит к:
Student(studentId=101, firstName=John, lastName=Smith, score=90) Student(studentId=203, firstName=Mary, lastName=Jane, score=88) Student(studentId=309, firstName=John, lastName=Wayne, score=96)
Насквозь, этот подход не очень хорош. Нам нужно знать порядок столбцов, а также количество столбцов, чтобы использовать метод get ()
, и изменение чего-либо в структуре CSV-файла полностью нарушает наш код.
Чтение CSV-файла с заголовками в Kotlin
Разумно знать , какие столбцы существуют , но немного меньше знать, в каком порядке они расположены.
Обычно CSV-файлы имеют строку заголовка, в которой указываются имена столбцов, такие как StudentID
, Имя
и т.д. При создании экземпляра CSVParser
, следуя шаблону проектирования Builder , мы можем указать, имеет ли файл, который мы читаем, строку заголовка или нет, в CSVFormat
.
По умолчанию формат CSV
предполагает, что файл не имеет заголовок. Давайте сначала добавим строку заголовка в наш CSV-файл:
Git Essentials
Ознакомьтесь с этим практическим руководством по изучению Git, содержащим лучшие практики и принятые в отрасли стандарты. Прекратите гуглить команды Git и на самом деле изучите это!
StudentID,FirstName,LastName,Score 101,John,Smith,90 203,Mary,Jane,88 309,John,Wayne,96
Теперь давайте инициализируем экземпляр CSVParser
и по пути зададим пару дополнительных параметров в формате CSV
:
val bufferedReader = new BufferedReader(Paths.get("/resources/students.csv")); val csvParser = CSVParser(bufferedReader, CSVFormat.DEFAULT .withFirstRecordAsHeader() .withIgnoreHeaderCase() .withTrim());
Таким образом, первая запись (строка) в файле будет рассматриваться как строка заголовка, а значения в этой строке будут использоваться в качестве имен столбцов.
Мы также указали, что регистр заголовка для нас мало что значит, превратив формат в формат без учета регистра.
Наконец, мы также сказали синтаксическому анализатору обрезать записи, что удаляет избыточные пробелы из начала и конца значений, если таковые имеются. Некоторые из других вариантов, с которыми вы можете повозиться, – это такие варианты, как:
CSVFormat.DEFAULT .withDelimiter(',') .withQuote('"') .withRecordSeparator("\r\n")
Они используются, если вы хотите изменить поведение по умолчанию, например, установить новый разделитель, указать, как обрабатывать кавычки, поскольку они часто могут нарушать логику синтаксического анализа, и указать разделитель записей, присутствующий в конце каждой записи.
Наконец, как только мы загрузим файл и проанализируем его с помощью этих настроек, вы сможете получить CSVRecord
s, как показано ранее:
for (csvRecord in csvParser) { val studentId = csvRecord.get("StudentId"); val studentName = csvRecord.get("FirstName); val studentLastName = csvRecord.get("LastName); var studentScore = csvRecord.get("Score); println(Student(studentId, studentName, studentLastName, studentScore)); }
Это гораздо более снисходительный подход, поскольку нам не нужно знать порядок самих столбцов. Даже если они будут изменены в любой момент времени, CSVParser
нас прикроет.
Выполнение этого кода также приводит к:
Student(studentId=101, firstName=John, lastName=Smith, score=90) Student(studentId=203, firstName=Mary, lastName=Jane, score=88) Student(studentId=309, firstName=John, lastName=Wayne, score=96)
Написание CSV-файла в Kotlin
Подобно чтению файлов, мы также можем записывать CSV-файлы с помощью Apache Commons. На этот раз мы будем использовать CSVPrinter
.
Так же , как CSV-считыватель
принимает BufferedReader
, CSVPrinter
принимает BufferedWriter
и формат CSV
, который мы хотели бы использовать при записи файла.
Давайте создадим BufferedWriter
и создадим экземпляр CSVPrinter
:
val writer = new BufferedWriter(Paths.get("/resources/students.csv")); val csvPrinter = CSVPrinter(writer, CSVFormat.DEFAULT .withHeader("StudentID", "FirstName", "LastName", "Score"));
Метод print Record()
экземпляра CSVPrinter
используется для записи записей. Он принимает все значения для этой записи и выводит ее в новой строке. Вызов метода снова и снова позволяет нам записывать много записей. Вы можете либо указать каждое значение в списке, либо просто передать список данных.
Нет необходимости использовать метод print Record()
для самой строки заголовка, так как мы уже указали его с помощью withHeader()
метода CSVFormat
. Без указания заголовка там нам пришлось бы распечатать первую строку вручную.
В общем, вы можете использовать csvPrinter
вот так:
csvPrinter.printRecord("123", "Jane Maggie", "100"); csvPrinter.flush(); csvPrinter.close();
Не забудьте промыть()
и закрыть()
принтер после использования.
Поскольку мы работаем здесь со списком студентов и не можем просто распечатать запись таким образом, мы пройдемся по списку студентов, внесем их информацию в новый список и распечатаем этот список данных с помощью метода printRecord()
:
val students = listOf( Student(101, "John", "Smith", 90), Student(203, "Mary", "Jane", 88), Student(309, "John", "Wayne", 96) ); for (student in students) { val studentData = Arrays.asList( student.studentId, student.firstName, student.lastName, student.score) csvPrinter.printRecord(studentData); } csvPrinter.flush(); csvPrinter.close();
В результате получается CSV-файл, содержащий:
StudentID,FirstName,LastName,Score 101,John,Smith,90 203,Mary,Jane,88 309,John,Wayne,96
Вывод
В этом уроке мы рассмотрели, как читать и записывать CSV-файлы в Kotlin, используя библиотеку Apache Commons.