用Python标准库给JSONL做导入前体检:BOM、重复字段与行号定位

举报
yd_243677465 发表于 2026/10/29 00:15:52 2026/10/29
【摘要】 用Python标准库实现只读JSONL预检,兼容文件开头BOM,定位重复字段、NaN和样本结构问题,并用8行自建数据核验计数与错误行号。附完整代码及明确的适用范围。

在把 JSONL 文件交给训练平台之前,可以先在本地做一次只读检查。本文给出一个不依赖第三方库的小工具,检查范围包括逐行 JSON 解析、顶层对象类型、重复字段、非标准常量,以及一组自定义的 Alpaca 风格字段要求。

它是导入前的辅助工具,不是任何云平台的完整数据规范实现。字段要求需要按实际任务调整。本文使用自建测试数据运行验证,不涉及真实训练样本。

1. 先约定检查范围

本例采用以下规则:

  • 使用 UTF-8 读取文件,并兼容文件开头的 UTF-8 BOM。
  • 逐行处理;空白行单独计数,不算有效样本。
  • 每条非空记录的顶层必须是 JSON 对象。
  • instruction 和 output 必须是非空字符串;input 可省略,存在时必须是字符串。
  • 拒绝重复字段,以及 NaN、Infinity 等非标准 JSON 常量。
  • 不修改原文件,只返回计数及前10条错误示例。

这里的“有效”仅表示满足上述局部规则,并不代表样本语义正确、回答质量合格或一定能通过平台全量校验。

2. 为什么不只调用一次 json.loads

JSONL 由多条 JSON 记录组成,按行检查能保留原文件中的行号。有一行失败时,也可以继续统计后面的数据。

Python 的默认 JSON 解码会接受一些不适合严格数据交换的输入。例如,重复字段通常会留下后一个值,NaN 也会被默认解析器接受。因此,本例使用 object_pairs_hook 检查重复字段,并通过 parse_constant 显式拒绝非标准常量。

utf-8-sig 用于兼容文件开头的 BOM,不会自动处理任意位置插入的 BOM;这类异常仍应回到原文件排查。

3. 可直接保存运行的代码

把下面代码保存为 jsonl_precheck.py:

"""只读检查 Alpaca 风格 JSONL 样例,不修改文件。"""
import json
from pathlib import Path


def unique_object(pairs):
    result = {}
    for key, value in pairs:
        if key in result:
            raise ValueError(f"重复字段:{key}")
        result[key] = value
    return result


def reject_constant(name):
    raise ValueError(f"不接受非标准 JSON 常量:{name}")


def validate_jsonl(path):
    valid = invalid = blank = 0
    examples = []
    with Path(path).open(encoding="utf-8-sig") as stream:
        for line_number, line in enumerate(stream, 1):
            if not line.strip():
                blank += 1
                continue
            try:
                item = json.loads(line, object_pairs_hook=unique_object,
                                  parse_constant=reject_constant)
                if not isinstance(item, dict):
                    raise ValueError("记录必须是 JSON 对象")
                for key in ("instruction", "output"):
                    if not isinstance(item.get(key), str) or not item[key].strip():
                        raise ValueError(f"{key} 必须是非空字符串")
                if "input" in item and not isinstance(item["input"], str):
                    raise ValueError("input 必须是字符串")
            except (ValueError, TypeError) as error:
                invalid += 1
                if len(examples) < 10:
                    examples.append({"line": line_number, "reason": str(error)})
            else:
                valid += 1
    return {"valid": valid, "invalid": invalid, "blank": blank,
            "error_examples": examples}


if __name__ == "__main__":
    import argparse
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument("path")
    args = parser.parse_args()
    print(json.dumps(validate_jsonl(args.path), ensure_ascii=False, indent=2))

运行方式:

python jsonl_precheck.py sample.jsonl

工具只读取文件。错误原因里记录行号、字段名或解析错误,不会主动打印整条样本。不过,如果数据包含敏感字段名,日志仍需要按自己的数据管理要求保存。

4. 用8行自建数据核对结果

本次在 Python 3.11 中创建了一份带 UTF-8 BOM 的临时文件,8个物理行依次为:

行号 内容类型 本例预期
1 instruction/output 均为字符串的合法对象 有效
2 空行 计入 blank
3 不完整 JSON:{broken 语法错误
4 顶层数组:[] 类型错误
5 只有 instruction,缺少 output 字段错误
6 output 为数字7 字段类型错误
7 同一对象重复写了两次 output 重复字段错误
8 output 为 NaN 非标准常量错误

本地实际返回的计数是:

{"valid":1,"invalid":6,"blank":1}

错误示例中的行号依次是3、4、5、6、7、8。程序没有因为第3行失败而终止,后续各类问题都被单独记录。演示结束后临时文件被清理,原有文件未被修改。

5. 把规则和解析分开,后续更好维护

如果业务允许空 output,或者字段名称并非 instruction/input/output,就应该调整字段检查部分。不要为了让统计数字变好看而自动填入空字符串、删除报错样本或改写原文件。修正前应先明确任务的数据约定。

重复字段检查会作用于嵌套对象,而不只检查顶层。若需要输出完整的嵌套字段路径,可以在后续版本中单独设计路径跟踪;当前示例仅报告重复的字段名。

本工具还没有检查重复样本、token长度、训练集与验证集泄漏、个人信息残留或样本质量,也没有限制单行大小、嵌套深度。逐行读取减少了整文件一次载入的需求,但不能据此认为它适合处理任意大小的不可信输入。文件编码或磁盘读取异常也会直接抛出,需由调用方处理。

对于一个小型训练数据准备流程,这种“先解析、再校验字段、保留行号、最后汇总”的组织方式,能让问题定位更具体,也方便把后续规则逐项加入,而无需一开始就引入完整的数据处理框架。

本文示例代码为本次编写并验证,文章使用 AI 辅助整理。

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

评论(0)

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

全部回复

上滑加载中

设置昵称

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

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

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