Kotlin读写CSV存储游戏状态及二维MutableList初始化问题
问题解答
1. CSV扩展函数调用正确性与优化方案
你当前的readAsCSV()、writeAsCSV()基础调用逻辑是正确的,简单场景下可以正常运行,但存在几个容易踩坑的问题,优化后稳定性会高很多:
- 格式兼容性问题:当前读函数按
,(逗号+空格)分割字段,你附的chk_Q.csv第一行是0,0(逗号后无空格),会直接分割失败,后续访问列索引时会崩溃。建议改为按逗号分割后,对每个字段做首尾去空白处理,兼容各种不规范的CSV格式。 - 异常兜底不足:读取时没有做行数、列数校验,一旦CSV文件被篡改、损坏,很容易触发越界崩溃;字符串转Int时如果遇到非数字内容,也会直接抛异常。
- 编码不统一:读写时没有明确指定编码,不同安卓设备系统默认编码不一致时,可能出现中文乱码。
优化后的扩展函数代码如下,你原来的调用方式不需要改动,直接替换函数实现即可:
@Throws(IOException::class) fun File.readAsCSV(): List<List<String>> { val splitLines = mutableListOf<List<String>>() // 明确指定UTF-8编码读取,避免乱码 forEachLine(Charsets.UTF_8) { line -> // 按逗号分割后清除每个字段首尾空白,兼容无空格、多空格的格式 splitLines += line.split(",").map { it.trim() } } return splitLines } @Throws(IOException::class) fun File.writeAsCSV(values: List<List<String>>) { val csv = values.joinToString("\n") { line -> line.joinToString(", ") } // 明确指定UTF-8编码写入 writeText(csv, Charsets.UTF_8) }
2. 二维MutableList初始化报错解决
你之前写的循环初始化代码报IndexOutOfBoundsException的核心原因:
你只初始化了外层列表,且初始状态下外层列表只有1个空的内层列表元素。Kotlin的列表和数组不同,不能直接对不存在的索引位置赋值——你循环取csvOUT[i]时,i=0对应的内层列表是空的,访问[0][0]会触发空列表越界;i>=1时外层列表根本没有对应索引的元素,必然崩溃。
给你两种适合初学者的简洁初始化写法,完全可以替代逐行手动赋值的冗余代码:
写法1:循环add元素(逻辑最直观,不容易出错)
val csvOUT: MutableList<MutableList<String>> = mutableListOf() // 0..10共11个元素,循环11次 for (i in 0..10) { // 每轮先创建内层列表,添加完元素后再塞进外层列表 val innerList = mutableListOf<String>() innerList.add("$i") // 默认值可以根据自己的业务逻辑改 innerList.add(if (i <= 2) "OK" else "Not yet") csvOUT.add(innerList) }
写法2:用Kotlin标准库函数初始化(代码最简洁)
// 传入列表总大小11,尾Lambda定义每个索引位置的元素生成规则 val csvOUT = MutableList(11) { i -> mutableListOf("$i", if (i <= 2) "OK" else "Not yet") }
两种写法效果完全一致。
优化后的完整读写逻辑参考
替换原有onCreate里的读写代码即可,稳定性更高、代码更简洁:
// 读CSV部分增加边界校验 if (isFileExists(file)) { val csvIN = file.readAsCSV() // 只取CSV中实际存在的行,避免行数不足崩溃 val validRowCount = minOf(csvIN.size, 11) for (i in 0 until validRowCount) { // 校验列数足够再取值,转Int失败用默认值兜底 if (csvIN[i].size >= 2) { chk_Q_Num[i] = csvIN[i][0].toIntOrNull() ?: i chk_Q_State[i] = csvIN[i][1] } } } // 一行初始化二维列表,不需要逐行手动写 val csvOUT = MutableList(11) { i -> mutableListOf( chk_Q_Num[i].toString(), if (i <= 2) "OK" else "Not yet" ) } // 拼接展示文本 val tempString = csvOUT.joinToString("\n") { "${it[0]}-->${it[1]}" } binding.readFile.text = tempString // 写入CSV file.writeAsCSV(csvOUT)
额外提示:你目前把
chk_Q_Num和chk_Q_State定义成了全局变量,简单场景下可以正常使用,但安卓页面销毁重建时容易丢失数据,后续功能复杂后可以考虑把状态存在ViewModel中。
内容的提问来源于stack exchange,提问作者Surlofia
相关产品推荐
相关产品推荐

