CSV订单号的前导零去哪了:把dtype和缺失值规则一起固定

举报
yd_234095983 发表于 2026/11/06 10:42:13 2026/11/06
【摘要】 用合成订单复现前导零丢失与默认NA识别,结合字符串dtype、按列缺失规则及严格字段契约验证读取边界。Python3.12.3、pandas3.0.3,本地47项检查通过,含AI辅助和非生产说明。

作者:lgbisha

说明:本文由 AI 辅助起草并执行本地代码验证。全部订单、地区与备注均为合成数据,不含真实客户或公司资料。验证环境为 Python 3.12.3、pandas 3.0.3;完整本地脚本实际通过 47 项检查、0 失败。文中的三个代码块按顺序运行,可复现主要现象与读取函数,不等于完整列出全部 47 项检查。没有使用云服务器部署,也不声称完成线上数据导入或生产安全验证。

一、读入成功,不等于原始含义被保留

有些 CSV 字段看起来像数字,实际是标识符:订单号 0007 与 7 是否相同,取决于业务契约,不能由解析器代为决定。地区字段也有类似问题:示例里的 NA 和 NULL 是允许出现的代码文本,并不表示缺失。

这次实验关注两类彼此独立的转换:一是类型推断移除了前导零,二是缺失值识别把特定文本转成了缺失。只修改其中一项,另一项仍可能改变数据。

import csv
import io
import pandas as pd

CSV_TEXT = "order_id,region,note\n0007,NA,ready\n0012,NULL,\n0420,CN,NA\n"

inferred = pd.read_csv(io.StringIO(CSV_TEXT))
assert inferred["order_id"].tolist() == [7, 12, 420]
assert inferred["region"].isna().tolist() == [True, True, False]

string_only = pd.read_csv(io.StringIO(CSV_TEXT), dtype="string")
assert string_only["order_id"].tolist() == ["0007", "0012", "0420"]
assert string_only["region"].isna().sum() == 2

第一组结果说明默认推断把订单号当成了整数;第二组说明设置字符串类型可以保留前导零,却不会自动关闭默认缺失值词表。这里的 NA、NULL 仍被识别为缺失。

在错误解析之后再使用 astype("string"),只能得到 "7",不会自动恢复 "0007"。使用 zfill 也不是通用恢复方法:除非业务已经规定宽度且原始信息足够,否则它是在补充假设,不是在还原证据。

二、先声明每个字段的契约

本例规定:

  • 列名与顺序固定为 order_id、region、note,每条逻辑记录恰好三个字段。
  • 订单号是四位 ASCII 数字文本,必须非空且唯一。
  • 地区是二至四位大写英文字母,NA 与 NULL 都是合法文本。本例不是全球地区编码标准。
  • 备注允许缺失,且只有备注里的空字符串代表缺失;备注 NA 必须保留为文本。
  • 不静默去掉字段两端空格,不忽略数据中的空行,不替用户纠正列名。

这是一份教学用的小规模数据契约,不是适用于任意企业文件的 CSV 导入规范。

三、把类型与缺失值策略一起固定

def read_orders(csv_text):
    if not isinstance(csv_text, str):
        raise TypeError("输入必须是CSV文本")
    expected_columns = ["order_id", "region", "note"]
    try:
        records = list(csv.reader(io.StringIO(csv_text), strict=True))
    except csv.Error as error:
        raise ValueError("CSV引号结构无效") from error
    if not records or records[0] != expected_columns:
        raise ValueError("列名或顺序不符合示例契约")
    if len(records) < 2 or any(len(record) != 3 for record in records[1:]):
        raise ValueError("需要数据行且每行必须有三个字段")
    orders = pd.read_csv(
        io.StringIO(csv_text),
        dtype={column: "string" for column in expected_columns},
        keep_default_na=False,
        na_values={"note": [""]},
        skip_blank_lines=False,
    )
    if not orders["order_id"].str.fullmatch(r"[0-9]{4}", na=False).all():
        raise ValueError("订单号必须是四位ASCII数字文本")
    if not orders["order_id"].is_unique:
        raise ValueError("订单号不能重复")
    if not orders["region"].str.fullmatch(r"[A-Z]{2,4}", na=False).all():
        raise ValueError("地区代码必须是二至四位大写字母")
    return orders

其中,dtype 固定三列为 pandas 扩展字符串类型;keep_default_na=False 不再套用默认缺失值词表;按列指定的 na_values 只把空备注识别为缺失。没有设置 na_filter=False,因为关闭这个开关后,前面的缺失值策略不会按这里的预期生效。

先用标准库解析逻辑记录,是为了在这个固定三列的例子里拒绝字段数量变化与重复表头等问题,不使用 splitlines 或按逗号简单切分。引号内逗号和换行可以是一个字段的一部分。不过,strict=True 也不是“覆盖所有 CSV 方言和攻击方式”的安全认证。

四、验证保留下来的究竟是什么

orders = read_orders(CSV_TEXT)
assert orders["order_id"].tolist() == ["0007", "0012", "0420"]
assert orders["region"].tolist() == ["NA", "NULL", "CN"]
assert pd.isna(orders.loc[1, "note"])
assert orders.loc[2, "note"] == "NA"
assert orders.isna().sum().to_dict() == {
    "order_id": 0, "region": 0, "note": 1
}
assert orders.equals(read_orders(orders.to_csv(index=False, na_rep="")))

最后一个断言只说明本例按相同契约导出、重读后,DataFrame 的值和类型保持一致,不意味着原文件的引号风格、换行或字节序列保持不变,也不保证使用其他软件打开时显示一致。

完整本地脚本的 47 项检查还包括:仅表头、缺列、多列、重复表头、列顺序变化、数据行字段不足或过多、空数据行、未闭合引号、重复订单号、非四位或非 ASCII 订单号、非法地区代码,以及合法的引号内逗号、换行和备注空格。所有输入都由实验脚本构造,没有据此宣称覆盖所有异常格式。

五、上线前仍然缺少哪些东西

这个函数把整个 CSV 文本放进内存并进行两次解析,只适合解释小文件的语义边界。本文没有实现文件大小限制、编码探测、大文件分块、权限控制、审计、敏感信息脱敏或面向电子表格的公式注入防护。也没有在 HarmonyOS 设备、云端工作区或生产业务中运行这些 Python 例子。

更重要的是,某个字符串究竟代表代码还是缺失,必须由数据提供方和使用方共同确定。如果业务规定 NULL 就是缺失,应修改对应字段的契约,而不是照抄本文把它强行当成有效值。

参考:pandas 官方 API 文档 pandas.read_csv 的 dtype、keep_default_na、na_values、na_filter 与 skip_blank_lines 参数说明,以及 Python 标准库 csv.reader 文档。本文按实际安装版本另做运行复核;升级依赖后应重跑,而不是仅依赖截图判断正确。

结论:可靠导入的起点不是“把所有列都读成字符串”,而是把字段类型、缺失语义和业务约束一起写清楚,再用反例验证它们没有被悄悄改变。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。