I'm writing a simple import application and need to read a CSV file, show result in a grid and show corrupted lines of the CSV file in another grid.
Is there any built-in lib for it or any easy pythonic-like way?
I'm doing it on android.
I'm writing a simple import application and need to read a CSV file, show result in a grid and show corrupted lines of the CSV file in another grid.
Is there any built-in lib for it or any easy pythonic-like way?
I'm doing it on android.
[Edit October 2019] A couple of months after I wrote this answer, Koyama Kenta wrote a Kotlin targeted library which can be found at https://github.com/doyaaaaaken/kotlin-csv and which looks much better to me than opencsv.
Example usage: (for more info see the github page mentioned)
import com.github.doyaaaaaken.kotlincsv.dsl.csvReader
fun main() {
csvReader().open("src/main/resources/test.csv") {
readAllAsSequence().forEach { row ->
//Do something
println(row) //[a, b, c]
}
}
}
For a complete minimal project with this example, see https://github.com/PHPirates/kotlin-csv-reader-example
Old answer using opencsv:
As suggested, it is convenient to use opencsv. Here is a somewhat minimal example:
// You can of course remove the .withCSVParser part if you use the default separator instead of ;
val csvReader = CSVReaderBuilder(FileReader("filename.csv"))
.withCSVParser(CSVParserBuilder().withSeparator(';').build())
.build()
// Maybe do something with the header if there is one
val header = csvReader.readNext()
// Read the rest
var line: Array<String>? = csvReader.readNext()
while (line != null) {
// Do something with the data
println(line[0])
line = csvReader.readNext()
}
As seen in the docs when you do not need to process every line separately you can get the result in the form of a Map:
import com.opencsv.CSVReaderHeaderAware
import java.io.FileReader
fun main() {
val reader = CSVReaderHeaderAware(FileReader("test.csv"))
val resultList = mutableListOf<Map<String, String>>()
var line = reader.readMap()
while (line != null) {
resultList.add(line)
line = reader.readMap()
}
println(resultList)
// Line 2, by column name
println(resultList[1]["my column name"])
}
Dependency for Gradle: compile 'com.opencsv:opencsv:4.6'
or for Gradle Kotlin DSL: compile("com.opencsv:opencsv:4.6")
(as always, check for latest version in docs).
In terms of easiness, kotlin written csv library is better.
For example, you can write code in DSL like way with below library that I created:
https://github.com/doyaaaaaken/kotlin-csv
csvReader().open("test.csv") {
readAllAsSequence().forEach { row ->
//Do something with the data
println(row)
}
}
Use opencsv.
This is gonna work like a charm for reading a CSV file.
As far as logging the corrupted lines is concerned you can do it using this logic.
while(input.hasNextLine())
{
try
{
//execute commands by reading them using input.nextLine()
}
catch (ex: UserDefinedException)
{
//catch/log the exceptions you're throwing
// log the corrupted line the continue to next iteration
}
}
Hope this helps.
Frankly speaking, it is quite easy to make a simple reader in Kotlin using modern Java features, check this (REMEMBER to handle BOM :-)):
fun processLineByLine(csv: File, processor: (Map<String, String>) -> Unit) {
val BOM = "\uFEFF"
val header = csv.useLines { it.firstOrNull()?.replace(BOM, "")?.split(",") }
?: throw Exception("This file does not contain a valid header")
csv.useLines { linesSequence ->
linesSequence
.drop(1)
.map { it.split(",") }
.map { header.zip(it).toMap() }
.forEach(processor)
}
}
Than you can use it as follows (depends on your file structure):
processLineByLine(File("./my-file.csv")) { row ->
println("UserId: ${row["userId"]}")
println("Email: ${row["email"]}")
}
I used net.sourceforge.javacsv with my Kotlin code for parsing CSV files. It is a "java" library but within kotlin it is fairly straightforward to work with it like
val reader = CsvReader("/path/to/file.csv").apply {
trimWhitespace = true
skipEmptyRecords = true
readHeaders()
}
while (reader.readRecord()) {
// do whatever
}
If you prefer to use your own data class for each row you should have a look at my solution https://github.com/gmuth/ipp-client-kotlin/blob/master/src/main/kotlin/de/gmuth/csv/CSVTable.kt
data class User(
val name: String,
val phone: String,
val email: String
) {
constructor(columns: List<String>) : this(
name = columns[0],
phone = columns[1],
email = columns[2]
)
}
CSVTable.print(FileInputStream("users.csv"))
val userList = CSVTable(FileInputStream("users.csv"), ::User).rows
I know i'm a bit late, but I recently had problems with parsing CSV and there seemed to be no library good enough for what I was looking for, so I created my own called Kotlin CSV stream.
This library is special because it doesn't throw exceptions on an invalid input, but returns in the result instead, which might be useful in some cases.
Here is an example of how easy it is to use
val reader = CsvReader()
.readerForType<CsvPerson>()
val people = reader.read(csv).map { it.getResultOrThrow() }.toList()