【问题标题】:Read CSV line-by-line in Kotlin在 Kotlin 中逐行读取 CSV
【发布时间】:2017-10-19 01:23:57
【问题描述】:

我正在编写一个简单的导入应用程序,需要读取一个 CSV 文件,在一个网格中显示结果并在另一个网格中显示 CSV 文件的损坏行。

是否有任何内置库或任何简单的类似 pythonic 的方式?

我在安卓上做。

【问题讨论】:

标签: android csv kotlin


【解决方案1】:

[2019 年 10 月编辑]在我写完这个答案几个月后,Koyama Kenta wrote a Kotlin targeted library 可以在 https://github.com/doyaaaaaken/kotlin-csv 找到,对我来说它看起来比 opencsv 好得多。

使用示例:(有关更多信息,请参阅提到的 github 页面)

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]
        }
    }
}

有关此示例的完整最小项目,请参阅https://github.com/PHPirates/kotlin-csv-reader-example

使用 opencsv 的旧答案:

按照建议,使用opencsv 很方便。下面是一个小例子:

// 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()
}

docs 中所见,当您不需要单独处理每一行时,您可以得到 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"])
}

Gradle 的依赖项:compile 'com.opencsv:opencsv:4.6' 或 Gradle Kotlin DSL:compile("com.opencsv:opencsv:4.6")(与往常一样,请检查 docs 中的最新版本)。

【讨论】:

  • 最后一个建议不太正确 - 调用readMap() 只会从文件中读取一行。
  • @DavidSoroko 谢谢你是对的,我已经修复了这个例子。
【解决方案2】:

在易用性方面,kotlin写的csv库更好。

例如,您可以使用我创建的以下库的方式在 DSL 中编写代码:

https://github.com/doyaaaaaken/kotlin-csv

csvReader().open("test.csv") {
    readAllAsSequence().forEach { row ->
        //Do something with the data
        println(row)
    }
}

【讨论】:

  • 这个readAllAsSequence 是否逐行读取?我在这里有点困惑。
【解决方案3】:

使用opencsv

这就像读取 CSV 文件的魅力一样。

就记录损坏的行而言,您可以使用此逻辑来完成。

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
    }
}

希望这会有所帮助。

【讨论】:

  • 你的例子中输入了什么类?
【解决方案4】:

我使用 net.sourceforge.javacsv 和我的 Kotlin 代码来解析 CSV 文件。它是一个“java”库,但在 kotlin 中使用起来相当简单,就像

val reader = CsvReader("/path/to/file.csv").apply {
  trimWhitespace = true
  skipEmptyRecords = true
  readHeaders()
}

while (reader.readRecord()) {
  // do whatever
}

【讨论】:

    【解决方案5】:

    坦率地说,使用现代 Java 功能在 Kotlin 中制作一个简单的阅读器是很容易的,检查一下(记得处理 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)
        }
    }
    

    您可以按如下方式使用它(取决于您的文件结构):

    processLineByLine(File("./my-file.csv")) { row ->
        println("UserId: ${row["userId"]}")
        println("Email: ${row["email"]}")
    }
    

    【讨论】:

    • 我认为这是一个很好的答案,对于 kotlin dsl 开发人员的新手,例如 processLineByLine(file!!) { // 在这里对地图做一些事情 }跨度>
    • 能否详细说明什么是BOM,我们为什么要处理它?
    • BOM 是一个 Unicode 字符,可以出现在文件的开头。它很神奇,所以它不会被显示,但如果它在那里,你将无法匹配第一列标题。
    • 看起来不符合 rfc,在字段中允许换行 (tools.ietf.org/html/rfc4180)
    • 另一个非 rfc 的事情是 rfc 允许双引号在值中包含逗号,而此解决方案将忽略转义并在 "," 上拆分。
    【解决方案6】:

    如果您更喜欢对每一行使用自己的数据类,您应该看看我的解决方案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
    

    【讨论】:

      【解决方案7】:

      我知道我来晚了,但我最近在解析 CSV 时遇到了问题,而且似乎没有足够好的库来满足我的需求,所以我创建了自己的名为 Kotlin CSV stream 的库。

      这个库很特别,因为它不会对无效输入抛出异常,而是在结果中返回,这在某些情况下可能很有用。

      这是一个简单易用的例子

      val reader = CsvReader()
          .readerForType<CsvPerson>()
      val people = reader.read(csv).map { it.getResultOrThrow() }.toList()
      

      【讨论】:

      • 感谢您的回答,我认为如果您也将一个最小示例复制到此处会很有帮助 - 我认为它更容易显示与其他方法的区别。
      • 好点。我编辑了原始评论以添加它。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-12-05
      • 2018-12-08
      • 1970-01-01
      • 2019-03-22
      相关资源
      最近更新 更多