Scala Spark读取S3存储桶CSV字符间空格/损坏修复方案
问题根因
该现象不是文件损坏,本质是CSV文件编码与Spark默认读取编码不匹配:这类文件大多是Windows端工具、旧版Excel导出的UTF-16LE(带BOM)格式,每个普通ASCII字符占2字节,高8位为0x00空字节。Spark默认按UTF-8编码读取时,0x00字节会被识别为空控制字符,视觉上呈现为每个字符间夹了多余空格,同时文件开头的BOM头会导致第一行表头无法被正常解析。
你之前用Excel打开后重新上传能临时解决问题,本质是Excel保存时自动将文件转成了系统兼容的UTF-8/ANSI编码,只是该方法无法批量处理,效率极低。
修复方案
方案1:Spark读取时指定正确编码(推荐,零额外文件操作)
Spark 2.4及以上版本内置了带BOM的UTF-16编码支持,读取时直接指定编码参数即可,Scala代码示例:
val df = spark.read .option("header", "true") .option("encoding", "UTF-16") // 核心配置,自动识别BOM适配大小端 // 若明确文件为UTF-16LE无BOM,可将编码值替换为UTF-16LE .option("multiline", "true") // 存在带换行的单元格时开启,无此类场景可删除 .csv("s3a://<你的桶名>/<文件路径>/*.csv")
如果使用2.3及更早版本的Spark,内置CSV数据源无法自动识别BOM,可通过RDD层预处理去除BOM后再转DataFrame:
import org.apache.hadoop.io.LongWritable import org.apache.hadoop.io.Text import org.apache.hadoop.mapreduce.lib.input.TextInputFormat // 以UTF-16LE编码读文件,移除开头BOM val cleanedRdd = sc.newAPIHadoopFile( "s3a://<你的桶名>/<文件路径>/*.csv", classOf[TextInputFormat], classOf[LongWritable], classOf[Text] ).map { case (_, line) => new String(line.getBytes, "UTF-16LE").replaceAll("^\uFEFF", "") } // 转成DataSet后按正常CSV逻辑解析 val df = spark.read .option("header", "true") .csv(cleanedRdd.toDS())
方案2:批量预处理S3存量文件(适合多任务共享数据的场景)
如果异常存量文件多、不想逐个修改Spark任务配置,可以跑一次性的Spark/Glue批处理任务,统一将S3上的UTF-16编码CSV转为无BOM的UTF-8格式存回S3,后续所有任务直接按默认配置读取即可,核心处理逻辑:
- 遍历目标S3路径下所有CSV文件,读取文件头2个字节判断编码:头字节为
0xFF 0xFE即为带BOM的UTF-16LE文件 - 按对应编码读取文件全量内容,转为UTF-8编码写回目标路径
- 写回时移除BOM头,避免后续解析异常
避坑提示
- 不要尝试通过配置
ignoreLeadingWhiteSpace、ignoreTrailingWhiteSpace参数解决问题,这类配置仅能处理正常的半角/全角空格,无法识别编码不匹配产生的空控制字符 - 读取S3文件时优先使用
S3A连接器,老版本S3N连接器对特殊编码、大文件的读取兼容性较差
内容的提问来源于stack exchange,提问作者Dylan Sanderson
相关产品推荐
相关产品推荐

