Вступление
В этой статье мы рассмотрим, как читать и записывать файлы CSV в Kotlin , в частности, с помощью Apache Commons.
Зависимость Apache Commons
Поскольку мы работаем с внешней библиотекой, давайте продолжим и
импортируем ее в наш проект Kotlin. Если вы используете Maven, просто
включите зависимость commons-csv
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-csv</artifactId>
<version>1.5</version>
</dependency>
Или, если вы используете Gradle:
implementation 'org.apache.commons:commons-csv:1.5'
Наконец, добавив библиотеку в наш проект, давайте определим CSV-файл,
который мы собираемся прочитать - students.csv
:
101,John,Smith,90
203,Mary,Jane,88
309,John,Wayne,96
Он будет расположен в /resources/students.csv
.
Кроме того, поскольку мы будем считывать эти записи в пользовательские объекты, давайте создадим класс данных:
data class Student (
val studentId: Int,
val firstName: String,
val lastName: String,
val score: Int
)
Чтение файла CSV в Kotlin
Давайте сначала прочитаем этот файл с помощью BufferedReader
, который
принимает Path
к ресурсу, который мы хотим прочитать:
val bufferedReader = new BufferedReader(Paths.get("/resources/students.csv"));
Затем, как только мы прочитали файл в буфер, мы можем использовать сам
буфер для инициализации экземпляра CSVParser
val csvParser = CSVParser(bufferedReader, CSVFormat.DEFAULT);
Учитывая, насколько изменчивым может быть формат CSV - чтобы CSVFormat
догадок, вам нужно будет указать CSVFormat при инициализации
синтаксического анализатора. Этот синтаксический анализатор,
инициализированный таким образом, может использоваться только для этого
формата CSV.
CSVFormat.DEFAULT
примеру формата CSV и используем разделитель по
умолчанию, запятую ( ,
) - мы передадим CSVFormat.DEFAULT в качестве
второго аргумента.
Теперь CSVParser
- это Iterable
, содержащий экземпляры CSVRecord
Каждая строка - это запись CSV. Естественно, затем мы можем csvParser
экземпляр 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));
}
Для каждой CSVRecord
вы можете получить соответствующие ячейки,
используя метод get()
и передав индекс ячейки, начиная с 0
. Затем
мы можем просто использовать их в конструкторе нашего класса данных
Student
Этот код приводит к:
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
, FirstName
и т. Д. При CSVParser
экземпляра CSVParser, следуя шаблону проектирования
Builder , мы можем указать, имеет
ли файл, который мы читаем, строку заголовка. или нет, в CSVFormat
.
По умолчанию CSVFormat
предполагает, что файл не имеет заголовка.
Давайте сначала добавим строку заголовка в наш CSV-файл:
StudentID,FirstName,LastName,Score
101,John,Smith,90
203,Mary,Jane,88
309,John,Wayne,96
Теперь давайте инициализируем CSVParser
и попутно установим несколько
дополнительных параметров в CSVFormat
:
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
как было показано ранее:
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
.
Как CSVReader
принимает BufferedReader
, CSVPrinter
принимает
BufferedWriter
и CSVFormat
мы хотели бы использовать при записи
файла.
Давайте создадим BufferedWriter
и CSVPrinter
экземпляр CSVPrinter:
val writer = new BufferedWriter(Paths.get("/resources/students.csv"));
val csvPrinter = CSVPrinter(writer, CSVFormat.DEFAULT
.withHeader("StudentID", "FirstName", "LastName", "Score"));
Метод printRecord()
CSVPrinter
используется для записи записей. Он
принимает все значения для этой записи и распечатывает их в новой
строке. Вызов метода снова и снова позволяет нам писать много записей.
Вы можете указать каждое значение в списке или просто передать список
данных.
Нет необходимости использовать метод printRecord()
для самой строки
заголовка, поскольку мы уже указали его с помощью withHeader()
CSVFormat
. Без указания заголовка нам пришлось бы распечатать первую
строку вручную.
В общем, csvPrinter
можно использовать следующим образом:
csvPrinter.printRecord("123", "Jane Maggie", "100");
csvPrinter.flush();
csvPrinter.close();
Не забудьте flush()
и 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.