使用gcsfs下载CSV时如何转换字节为字符串?Python3下UTF-16LE解码报错
问题根因排查
异常现象本质
- 读取结果开头出现的
ÿþD,是UTF-16 LE编码文件的标准BOM头(字节序列0xFF 0xFE),没有被正确跳过,直接被当成普通文本读入了DataFrame的列名位置。 - Python3.7下报的
UnicodeDecodeError: 'utf-16-le' codec can't decode byte 0x74 in position 0: truncated data,是两层行为差异叠加导致的,和操作系统本身无关:- Python2和Python3的gcsfs文件流默认打开模式不一致:Python2中gcsfs返回的流默认是二进制模式,全流程由pandas完成字节到字符串的解码;Python3中gcsfs的open默认走文本模式,会提前用系统默认编码(通常是utf-8)对流内容做一轮解码,等把这个已经被错误解码过的文本流传给pandas时,原始字节顺序、内容长度已经完全失真,自然无法正常按照utf-16-le规则解码。
- 指定的
UTF-16 LE编码本身不具备自动识别BOM头的能力:只有用不带后缀的utf-16编码参数时,pandas才会自动检测并跳过文件开头的BOM字节,不会把BOM内容当成数据/列名读取。
可直接落地的修复方案
方案1:最小改动兼容Python3环境(推荐)
调整两个参数即可完全对齐Python2下的运行行为:
- 给
fs.open()方法显式指定mode='rb',强制以二进制模式返回文件流,禁用gcsfs的提前文本解码逻辑 - 把
pd.read_csv的encoding参数从UTF-16 LE替换为utf-16,让pandas自动识别并跳过UTF-16 LE的BOM头
修改后的代码参考:
fs = gcsfs.GCSFileSystem(project='<project ID>', token='<path to service account json>') with fs.open('gs://some_path_to_csv', mode='rb') as f: stability_data = pd.read_csv(f, delimiter=',', encoding='utf-16') return stability_data
方案2:必须使用utf-16-le编码时的处理
如果业务上必须严格用utf-16-le解码(比如需要完全避开pandas的自动编码检测逻辑),可以在读取前手动跳过流开头2个字节的BOM内容:
fs = gcsfs.GCSFileSystem(project='<project ID>', token='<path to service account json>') with fs.open('gs://some_path_to_csv', mode='rb') as f: # 跳过开头2字节的UTF-16 LE BOM f.seek(2) stability_data = pd.read_csv(f, delimiter=',', encoding='utf-16-le') return stability_data
注:之前观察到的Mac能跑、Windows跑不通的差异,本质是测试时Mac环境搭载的是Python2.7,Windows环境搭载的是Python3,和操作系统本身的编码规则没有关系。
内容的提问来源于stack exchange,提问作者xertzer
相关产品推荐
相关产品推荐

