You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Scala使用Encoder读取CSV至DataFrame时丢失内容的问题

解决CSV解析时记录内容丢失的问题

看起来你遇到的问题是CSV里的嵌套引号导致解析器误判字段边界,把后面的内容弄丢了!我来帮你拆解原因和解决办法:

问题根源

你给出的这条记录里,URL字段的格式是"http://svnbook.red-bean.com/"">Version Control with Subversion..."——这里的问题在于不规范的引号嵌套:字段开头的"在URL末尾的"处被错误地当成了字段结束标记,后面的">Version Control...部分要么被解析成无效字段,要么直接被丢弃,导致你看不到第二段内容。

而且你用case class定义Schema,一旦解析器拆分字段的数量和case class的属性数量不匹配,就会出现内容丢失或错位的情况。

具体解决方案

1. 调整Spark CSV解析参数(推荐)

如果是用Spark处理的话,可以通过设置解析选项来兼容这种不规范的引号:

import org.apache.spark.sql.SparkSession
import your.package.yourCaseClass

val spark = SparkSession.builder().appName("CSVParser").getOrCreate()

val df = spark.read
  .option("quote", "\"") // 指定字段包裹符为双引号
  .option("escape", "\"") // 用双引号转义字段内部的双引号
  .option("quoteMode", "NON_NUMERIC") // 只对非数字字段用引号包裹,避免干扰
  .schema(spark.implicits.newProductEncoder[yourCaseClass].schema) // 用case class生成Schema
  .csv("your-file.csv")

这里的escape参数是关键——它告诉解析器:字段内部的双引号需要用另一个双引号转义,这样就能正确识别"http://svnbook.red-bean.com/"里的引号是字段内容的一部分,而不是字段结束标记。

2. 预处理CSV文件修复格式

如果数据格式太不规范,解析参数也搞不定,可以先手动修复文件里的引号问题:
比如用命令行工具sed把字段内部的单个双引号替换成两个(CSV标准的转义方式):

sed -E 's/([^,])"([^,])/\1""\2/g' input.csv > corrected.csv

这个正则会匹配不在逗号前后的单个双引号,把它变成两个,这样解析器就能正确识别了。

3. 手动解析每行数据(兜底方案)

如果上面两种方法都不行,你可以直接读取文本行,自己实现简单的CSV解析逻辑:

import org.apache.spark.sql.functions._

// 先读取原始文本行
val rawLines = spark.read.text("your-file.csv")

// 自定义解析逻辑,处理嵌套引号
def parseCsvLine(line: String): yourCaseClass = {
  val fields = collection.mutable.ArrayBuffer[String]()
  val currentField = StringBuilder.newBuilder
  var inQuotes = false

  for (c <- line) {
    c match {
      case '"' => inQuotes = !inQuotes
      case ',' if !inQuotes =>
        fields += currentField.toString().trim
        currentField.clear()
      case _ => currentField.append(c)
    }
  }
  // 加入最后一个字段
  fields += currentField.toString().trim

  // 把解析后的字段映射到你的case class
  yourCaseClass(
    fields(0).toInt,
    fields(1).toInt,
    fields(2),
    fields(3).toInt,
    fields(4).toInt,
    fields(5)
  )
}

// 转换为DataFrame
val parsedDF = rawLines.map(r => parseCsvLine(r.getString(0))).toDF()

这种方式完全由你控制解析逻辑,能应对各种奇葩的CSV格式。

内容的提问来源于stack exchange,提问作者methefish

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.25 08:35:37