如何配置CSV组件以映射字符串列表(含Eurovision数据示例)
解决Tototoshi CSV库实现国家到年份列表的映射问题
首先,你当前的代码有两个核心问题需要调整:一是CSVFormat的配置不完整,二是Map[String, String]只能存储单个年份,无法处理同一国家多次获奖的情况(比如瑞典在2012和2015年都获胜)。下面是完整的解决方案:
1. 补全CSVFormat配置
你的CSV文件没有使用引号包裹字段,所以需要把quoteChar设置为None(表示不使用引号),同时补全其他必要的格式参数:
implicit object CodesFormat extends CSVFormat { val delimiter: Char = ',' val quoteChar: Option[Char] = None // 你的CSV无引号字段,设为None适配 val escapeChar: Option[Char] = Some('\\') // 默认转义符,可按需调整 val lineTerminator: String = "\n" val header: Boolean = true // 标记CSV第一行为表头 }
2. 修改映射类型与解析逻辑
把countrEurovisionYearMap的类型从Map[String, String]改为Map[String, List[Int]](年份用整数更贴合业务逻辑),然后通过分组聚合的方式收集每个国家对应的所有年份:
import java.io.File import com.github.tototoshi.csv._ object CountryEurovision { def countryEurovisionYearFile: File = new File("conf/countryEurovision.csv") // 调整映射类型为「国家 -> 获奖年份列表」 lazy val countryEurovisionYearMap: Map[String, List[Int]] = getConvertData private def getConvertData: Map[String, List[Int]] = { implicit object CodesFormat extends CSVFormat { val delimiter: Char = ',' val quoteChar: Option[Char] = None val escapeChar: Option[Char] = Some('\\') val lineTerminator: String = "\n" val header: Boolean = true } // 读取CSV并处理数据 CSVReader.open(countryEurovisionYearFile).withFilter(_.nonEmpty) { reader => reader.all().tail // 跳过表头行 .map { case List(country, yearStr) => (country, yearStr.toInt) } // 年份转整数 .groupBy(_._1) // 按国家分组 .mapValues(_.map(_._2).sorted) // 提取年份列表并排序(可选) } } }
关键改动说明
- 格式适配:
quoteChar = None匹配你无引号的CSV格式,header = true让阅读器自动识别表头行。 - 映射升级:用
Map[String, List[Int]]替代单值映射,完美支持一个国家对应多个获奖年份的场景。 - 数据聚合:通过
groupBy按国家分组,再提取每个分组的年份列表,还可以按需对年份排序优化展示。 - 类型优化:把年份字符串转为整数,避免后续业务逻辑中出现字符串转数字的重复操作。
如果需要处理文件不存在或格式错误的情况,可以在代码中加入try-catch捕获异常,增强鲁棒性。
内容的提问来源于stack exchange,提问作者MNY




