如何用SuperCSV与Kotlin读取CSV时将空值替换为空白并添加列
解决SuperCSV读取CSV空值返回null导致拼接不符合预期的问题
问题场景
在不修改原CSV内容的前提下,通过逐行读取拼接字符串的方式为CSV末尾添加新列时,原CSV中的空字段被SuperCSV解析为null,导致最终生成的CSV中出现字符串"null",而非预期的空值格式(连续逗号)。即使使用Map.getOrDefault(header, "")也无法解决该问题。
原因分析
Map.getOrDefault()方法仅在键不存在于Map中时才会返回默认值;如果键存在但对应的值是null,该方法会直接返回null。而SuperCSV对CSV中的空字段处理逻辑是:将空字段以null值存入Map(键存在,值为null),因此getOrDefault无法覆盖这种场景。
解决方案
改用Kotlin的Elvis运算符?:,它会在值为null(无论键是否存在)时返回指定的默认值。同时优化字符串拼接方式,使用StringBuilder提升性能,并通过use块自动关闭资源,避免手动关闭可能的遗漏。
修改后的完整代码:
fun addColumnToCSV(csvData: ByteArray, columnName: String, columnValue: String): ByteArray { val prefs = CsvPreference.Builder(CsvPreference.STANDARD_PREFERENCE) .useQuoteMode(NormalQuoteMode()).build() return BufferedReader(InputStreamReader(csvData.inputStream())).use { reader -> CsvMapReader(reader, prefs).use { mapReader -> val readHeader = mapReader.getHeader(true) val csvBuilder = StringBuilder() // 写入表头,追加新列名 csvBuilder.append(readHeader.joinToString(",", postfix = ",$columnName\n")) var row = mapReader.read(*readHeader) while (row != null) { // 用Elvis运算符处理null值,替换为空字符串 val rowValue = readHeader.map { header -> row[header] ?: "" } csvBuilder.append(rowValue.joinToString(",", postfix = ",$columnValue\n")) row = mapReader.read(*readHeader) } // 仅去除末尾的多余换行/空格,保留CSV格式完整性 csvBuilder.trimEnd().toString().toByteArray() } } }
关键修改点
- 将
row.getOrDefault(header, "")替换为row[header] ?: "",确保所有null值(无论键是否存在)都被替换为空字符串。 - 使用
StringBuilder代替循环中的字符串拼接,避免频繁创建临时字符串对象,提升性能。 - 使用
use块自动管理BufferedReader和CsvMapReader的资源,无需手动调用close()。 - 用
trimEnd()代替trim(),仅去除末尾的多余空白字符,避免破坏CSV内容的格式。
运行原测试用例即可通过,生成的CSV中空值将以连续逗号的形式呈现,符合预期格式。
内容的提问来源于stack exchange,提问作者wick3d
相关产品推荐
相关产品推荐

