Dart中io.File读取split后List.length值异常问题排查
Dart List遍历异常问题分析
问题背景
使用Dart的io.File.readAsString()读取文件内容,按\n分割为List<String>后,用for循环结合List.length遍历时,length值经常偏低甚至为0。临时改用while循环加异常捕获的方案可正常运行,现分析原因:
不稳定版本代码
static Future<int> open() async { io.File handle = await ModelHandle.alias; String string = await handle.readAsString(); List<String> lines = string.split('\n'); for(var i = 0; i < lines.length; i++){ // length often too low or even zero // do some stuff } }
临时方案代码
static Future<bool> open() async { io.File handle = await ModelHandle.alias; String string = await handle.readAsString(); List<String> lines = string.split('\n'); int i = -1; String l; while (true) { // works fine try { l = lines[++i]; } catch (e) { break; } // do some stuff } }
文件句柄源码
static Future<io.File> fileHandle(String filename) async { io.Directory appDir = await pathProvider.getApplicationDocumentsDirectory(); List<String> parts = [appDir.path, ...localPath, filename]; io.File file = await io.File(parts.join(path.separator)).create(recursive: true); return file; }
原版本问题根源
核心问题出在循环体内部的// do some stuff代码修改了lines列表本身:
- Dart的for循环会在每次迭代时重新计算
lines.length。如果循环内执行了lines.remove()、lines.clear()等缩短列表的操作,lines.length会动态减小,导致循环提前终止,表现为“length值偏低甚至为0”。 - 举个典型场景:初始
lines.length为5,循环到i=2时执行lines.removeAt(2),列表长度变为4;i递增到3后,3 <4成立继续循环,但原索引3的元素已变为索引2,会被跳过;若后续执行lines.clear(),lines.length直接变为0,下一次循环判断i <0不成立,循环直接终止,剩余元素完全未处理。 - 换行符问题(如
\r\n分割出空元素)概率极低,不会导致length频繁偏低。
临时方案可行的原因
while循环通过索引直接访问+异常捕获的方式,彻底绕开了列表长度变化的影响:
- 每次循环通过
lines[++i]尝试访问元素,直到触发索引越界的RangeError才终止循环。不管循环体内部如何修改lines,只要当前索引对应的元素存在,就会执行逻辑;当索引超出有效范围时,自动终止循环,确保所有可访问的元素都能被处理。 - 这种方式不依赖
List.length属性,完全靠索引访问的有效性控制循环,因此不受列表动态修改的干扰,运行稳定。
内容的提问来源于stack exchange,提问作者Stefan Brinkmann
相关产品推荐
相关产品推荐

