Рубрики
Без рубрики

Чтение и запись CSV-файлов в Kotlin с помощью Apache Commons

В этом уроке мы рассмотрим, как читать и записывать CSV-файлы в Kotlin, используя библиотеку Apache Commons, с примерами чтения и записи пользовательских объектов.

Автор оригинала: 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.