为何Java用File构造Scanner读CSV抛NoSuchElementException,换Paths.get就好
两种Scanner构造的核心差异
两个构造的本质区别是默认采用的文件解码字符集完全不同,这点是JDK实现里硬编码的,很容易被忽略:
new Scanner(new File(filename)):使用运行JVM的操作系统平台默认字符集解码文件内容,比如中文Windows环境下默认是GBK编码new Scanner(Paths.get(filename)):固定使用UTF-8字符集解码文件内容,不受操作系统默认字符集影响
报错的触发逻辑
你遇到的NoSuchElementException是字符集解码错位直接导致的,完整链路如下:
- 你的CSV文件本身是UTF-8编码存储的,文件中出现的西语重音字符(比如
Sánchez里的é、á)在UTF-8编码中占2个字节长度 - 当用
new Scanner(File)构造读取时,JVM按本机GBK规则解码UTF-8的字节流,每个2字节的重音字符会被拆成2个独立的GBK单字节字符,直接打破文件原本的字节边界 - 错位的字节被转成字符串后,Scanner基于字符内容识别的分隔符、行结束符会完全偏移:本该被引号包裹在作者字段内部的逗号、换行符,会被错误识别成CSV的字段分隔边界
- 这种边界错位会让Scanner提前把读取指针滑到流末尾,当代码执行到循环里读取第4个整数(也就是362行内容处理完的对应位置)时,已经找不到可匹配的int元素,直接抛出
NoSuchElementException - 换成
Paths.get()的构造方法后,因为固定用UTF-8解码,和文件本身的存储编码完全匹配,不会出现字节拆分错位的问题,错误自然消失
现存的代码风险
哪怕解决了字符集的问题,当前的CSV解析逻辑依然存在硬bug:
- 你直接将
,设置为Scanner的全局分隔符,完全没有处理CSV规范中双引号包裹字段内部的逗号的场景——你给出的示例CSV里,作者列表本身就是被双引号包裹、内部带逗号的结构,后续只要字段内部出现逗号,Scanner依然会错误切分,触发读取异常
这个字符集差异是JDK的官方定义行为,不是代码实现bug。
内容的提问来源于stack exchange,提问作者jruan
相关产品推荐
相关产品推荐

