Ruby导入CSV经纬度首条值转Float返回0问题
问题原因
导入的CSV文件为带BOM的UTF-8编码格式,文件开头的3字节UTF-8 BOM标记(字节序列[239, 187, 191],对应字符U+FEFF)被读取为第一条纬度字符串的开头字符。虽然字符串编码检测结果显示为UTF-8,但Ruby的String#to_f、BigDecimal方法从字符串起始位置解析数字时,遇到不可见的BOM非数字字符会直接终止解析,因此to_f返回0,BigDecimal直接抛出无效值错误。
解决方案
1. 导入环节从源头规避(推荐)
修改CSV文件读取逻辑,打开文件时指定bom|UTF-8编码,Ruby会自动识别并剥离开头的BOM标记,同时兼容无BOM的普通UTF-8文件,不会引入额外兼容问题:
# 修改File.foreach的编码参数即可 File.foreach("#{APP_ROOT}/tmp/geofences/#{filename}", encoding: 'bom|UTF-8') do |line| csv_data = CSV.parse(line, col_sep: ',', headers: false) row = csv_data[0] lat = row[0] lon = row[1] interest.geofence << [lat, lon] end
2. 单值清洗兼容
如果暂时无法修改文件读取逻辑,可以在处理经纬度字符串前主动剥离BOM字符:
UTF8_BOM = "\xEF\xBB\xBF".freeze # 读取字段后清洗BOM lat = row[0].delete(UTF8_BOM) lon = row[1].delete(UTF8_BOM)
注意:delete(UTF8_BOM)仅会删除字符串内存在的BOM字符,对正常无BOM的字符串不会产生任何修改,可放心用于全量数据处理。
清洗后转换类型即可得到正确结果,验证如下:
# 模拟带BOM的异常字符串 bad_lat = [239, 187, 191, 51, 52, 46, 49, 55, 54, 48, 50, 49, 50].pack('C*').force_encoding('UTF-8') bad_lat.to_f # => 0.0 bad_lat.delete(UTF8_BOM).to_f # => 34.1760212 BigDecimal(bad_lat.delete(UTF8_BOM)) # => 0.341760212e2
3. 修复已入库存量数据
如果已有大量异常数据存入MongoDB,可遍历全量数据批量清洗修复:
UTF8_BOM = "\xEF\xBB\xBF".freeze FR::Interest.all.each do |interest| fixed_geofence = interest.geofence.map do |point| point.map { |coord| coord.delete(UTF8_BOM) } end interest.update!(geofence: fixed_geofence) end
内容的提问来源于stack exchange,提问作者alilland
相关产品推荐
相关产品推荐

