记得以前帮运营同事合并一批订单数据,三百多行。我用 pandas 一读,好家伙,身份证号全变科学计数法了,手机号也变浮点数了。
我当时还纳闷,这数据源没问题啊,怎么一进 Excel 就变样了?
后来才发现,这不是 Excel 的 bug,是 Excel 的特性。今天把这类坑一次性讲清楚。
坑 1:Excel 打开 CSV,身份证号直接变科学计数法
出问题的场景:
CSV 里一行:张三,420111199901011234Excel 打开后:张三,4.20111E+17
Excel 打开 CSV 时,超过 11 位的纯数字默认当成数值处理,自动转成科学计数法。身份证号 18 位,直接变 4.20111E+17,精度还丢了,后面几位全变 0。
这不是 Python 的问题,是 Excel 的锅。但你的数据是从 Python 脚本导出的,用户第一反应就是:你代码写错了。
修复方法:
# 导出 CSV 时,给数字列加个前缀让 Excel 当文本处理import csvwith open('output.csv', 'w', newline='', encoding='utf-8-sig') as f:writer = csv.writer(f)writer.writerow(['姓名', '身份证号'])writer.writerow(['张三', '\t' + '420111199901011234']) # 加 \t 前缀
或者在 pandas 里:
df['身份证号'] = df['身份证号'].astype(str)df.to_csv('output.csv', index=False, encoding='utf-8-sig')
翻车现场:有一次给财务导数据,身份证号变科学计数法之后,他们直接拿去用了,批量比对全部失败。后来排查了两小时,才发现是导出的时候没处理格式问题。
坑 2:pandas 读进来就是 float,转不回去了
出问题的代码:
import pandas as pddf = pd.read_excel('订单.xlsx')print(df['手机号'].dtype) # float64print(df['手机号'].iloc[0]) # 1.380012e+10
Excel 里的手机号本来是文本格式的数字,但 pandas 读取时自动推断为数值类型,转成了 float64。
读进来就已经是科学计数法了,你想转回字符串都费劲,因为精度已经丢了。
修复方法:
# 读取时指定该列为字符串df = pd.read_excel('订单.xlsx', dtype={'手机号': str, '身份证号': str})# 或者读取后转换(注意要在丢精度之前)df = pd.read_excel('订单.xlsx', converters={'手机号': str, '身份证号': str})
翻车现场:这个坑最阴险的地方在于,dtype 是 float64,以为astype(str) 转回来就行了。但转完变成 '1.3800123456e+10' 这种字符串,原始号码已经回不来了,必须在读取的时候就指定 dtype。
坑 3:Excel 自己也会偷偷改你的数字
出问题的场景:
在 Excel A1 输入:420111199901011234按回车A1 变成:4.20111E+17
你没用 Python,纯 Excel 操作也会中招。
Excel 单元格默认格式是"常规",超过 11 位的数字自动显示为科学计数法。更狠的是,如果你直接保存,原始精度就真的丢了,后面几位变 0。
修复方法:
# 用 openpyxl 写入时,强制设为文本格式from openpyxl import Workbookfrom openpyxl.styles import numberswb = Workbook()ws = wb.activecell = ws['A1']cell.value = '420111199901011234'cell.number_format = '@' # 文本格式wb.save('output.xlsx')
翻车现场:我见过最离谱的情况是,有人用 Excel 手动录入了一列身份证号,保存之后发给同事,同事打开一看全是科学计数法,后面三位全变 0。
想找回来?还不如直接下楼买彩票吧。
坑 4:从 MySQL 查出来的数字也变味了
出问题的代码:
import pymysqlconn = pymysql.connect(...)df = pd.read_sql('SELECT * FROM users', conn)print(df['id_card'].dtype) # object (字符串)print(df['id_card'].iloc[0]) # 420111199901011234 ✓
好消息:从数据库读出来的字符串字段,pandas 会保留为 object 类型,不会变成 float。
坏消息:很多人不知道这一点,非要把读出来的数据再做一次 pd.to_numeric(),结果又变回 float 了。
翻车现场:有同事查数据库拿到正确的身份证号,然后用 pd.to_numeric(df['id_card']) 想规范化一下数据,结果全变科学计数法了。
坑 5:合并 Excel 后数字格式全乱了
出问题的代码:
import pandas as pdfiles = ['1月.xlsx', '2月.xlsx', '3月.xlsx']all_data = pd.concat([pd.read_excel(f) for f in files])all_data.to_excel('Q1合并.xlsx', index=False)
合并前每个文件的身份证号都是正常的,合并后再打开,全变科学计数法了。
原因是 pd.concat 之后 dtype 可能发生变化,尤其是当某些文件的列有空值时,pandas 会把整列升级为 float64。
修复方法:
# 合并时强制保持字符串类型all_data = pd.concat([pd.read_excel(f, dtype={'身份证号': str, '手机号': str})for f in files])all_data.to_excel('Q1合并.xlsx', index=False)
翻车现场:季度末合并销售数据,合并完发现客户手机号全变了。因为其中一个文件有几行空数据,pandas 把整列从 object 升级成了 float64,精度丢失不可逆。
以上 5 个坑,核心就一句话:Excel 和 pandas 都默认把数字当数值处理,超过 11 位就自动变科学计数法,而且是不可逆的。
记住一个原则:身份证号、手机号、订单号这类长数字,从读取的第一步就要指定为字符串,否则后面怎么补救都来不及。
你还遇到过类似的情况吗?评论区说说。
夜雨聆风