资讯动态

docling EBCDIC 后端:把大型机定宽数据文件解码成 Markdown 表格 —— 以多伦多 311 工单 Groundtruth 为例

发布时间:2026/9/7 23:39:03 来源:尧图企业网站定制
docling EBCDIC 后端把大型机定宽数据文件解码成 Markdown 表格 —— 以多伦多 311 工单 Groundtruth 为例【免费下载链接】doclingGet your documents ready for gen AI项目地址: https://gitcode.com/GitHub_Trending/do/docling本文以 docling 测试数据集中的tests/data/ebcdic/groundtruth/311_calls_for_service.ebc.md为核心样本讲解 docling 的 EBCDIC 后端如何把一份没有自描述结构的大型机定宽数据文件依据 COBOL 布局layout逐字节解码最终产出这份 Markdown 表格文中会给出该表格完整的 17 列字段模式、配套的layout.json全文、后端解码调用链与端到端测试的验证方式读完你可以独立处理自己的.ebc定宽文件。1. 这份 groundtruth 文件是什么tests/data/ebcdic/groundtruth/311_calls_for_service.ebc.md是 docling 端到端测试的基准输出groundtruth对应源数据tests/data/ebcdic/sources/311_calls_for_service.ebc—— 一份采用 EBCDIC 编码测试中固定使用cp037码表、由多伦多 311 市民服务热线导出的服务请求定宽数据文件。整个文件只有 44 行结构极其规整第 1 行是布局文件layout里的description字段即311 service requests, single schema of character fields第 34 行是一张 17 列表格的表头与分隔行第 544 行是 40 条记录每行一条解码后的服务请求。这 40 条记录来自上游ebcdic-parser项目样本的前 40 条记录上游样本有 2067 MB仓库按 tests/data/ebcdic/README.md 的说明裁剪每条记录都保持上游数据的字节级原样解码值曾与参考项目的validate/、validation/输出逐字段核对过。表格表头就是布局文件中声明的 17 个字段名| service_request_id | status | status_notes | service_name | service_code | description | agency_responsible | service_notice | requested_datetime | updated_datetime | expected_datetime | address | address_id | zipcode | long | lat | media_url |以第 1 条记录groundtruth 第 5 行为例各列解码后的典型取值是service_request_id10100555934412 字节字符字段statusopenstatus_notesIn progress - The request has been scheduled.service_nameRoad - Pot holeservice_codeCSROWR-12agency_responsible311 Torontorequested_datetime2018-10-19T23:05:00-04:00ISO 8601 带时区偏移addressWoodmount Ave / Glebeholme Blvd, former Torontoaddress_id13460182long-79.31627311lat43.687585761description、zipcode、media_url在该行为空值得注意的是数据本身的细节大多数记录的时区偏移是-04:00EDT而少数跨越到 1 月的expected_datetime变成了-05:00EST例如 groundtruth 第 17 行的2019-01-17T12:04:00-05:00——这些值都原样保留在定宽字节里解码后端不做任何格式修正这正是“字节级忠实”的体现。部分记录的media_url列带有 seeclickfix 附件图片 URL如 groundtruth 第 27、28 行其余列为空对应源记录中该字段被空格填充。2. 源数据与布局文件905 字节定宽记录EBCDIC 文件自身不携带结构信息字段含义必须来自产生它的 COBOL copybook。docling 把 copybook 抽象成EbcdicLayout而这份样本对应的布局就是 tests/data/ebcdic/sources/311_calls_for_service.layout.json全文如下{ description: 311 service requests, single schema of character fields, records: [ { name: main, fields: [ { name: service_request_id, size: 12 }, { name: status, size: 6 }, { name: status_notes, size: 126 }, { name: service_name, size: 30 }, { name: service_code, size: 10 }, { name: description, size: 344 }, { name: agency_responsible, size: 11 }, { name: service_notice, size: 1 }, { name: requested_datetime, size: 25 }, { name: updated_datetime, size: 25 }, { name: expected_datetime, size: 25 }, { name: address, size: 130 }, { name: address_id, size: 8 }, { name: zipcode, size: 6 }, { name: long, size: 14 }, { name: lat, size: 14 }, { name: media_url, size: 118 } ] } ] }要点所有字段都没有显式写type因此全部按默认类型string即 COBOLUSAGE DISPLAY字符数据解码这也与description里 “single schema of character fields” 的表述一致单一records条目意味着单模式single schema文件不需要record_type_field前缀也不存在表头/表尾字节header_size、footer_size默认 017 个字段宽度求和1261263010344111252525130861414118 905 字节即每条记录固定 905 字节。这一点可以直接用源文件验证311_calls_for_service.ebc实测为36 200 字节恰好等于 905 × 40。用十六进制查看文件开头也能对上解码结果00000000 f1 f0 f1 f0 f0 f5 f5 f5 f9 f3 f4 f4 96 ...在 cp037 码表中f1、f0分别是字符1、0于是前 12 个字节解码为101005559344正是 groundtruth 表格里第 1 条记录的service_request_id96是 cp037 的空格对应字段之间的填充。3. 解码管线从字节流到 DoclingDocument 表格后端实现在 docling/backend/ebcdic_backend.py类EbcdicDocumentBackend继承自DeclarativeDocumentBackend声明自己支持InputFormat.EBCDICsupported_formats()返回{InputFormat.EBCDIC}且不支持分页supports_pagination()返回False——定宽数据文件没有“页”的概念。convert()的完整调用链是解析布局必须_resolve_layout()优先取EbcdicBackendOptions.layout内联的EbcdicLayout否则读取layout_file指向的 JSON两者都没提供时抛出DocumentLoadError提示语为 “The EBCDIC backend needs a layout: set either EbcdicBackendOptions.layout or EbcdicBackendOptions.layout_file.”。测试test_layout_is_required专门验证了这个报错路径。切记录、解字段_RecordParser.parse()从header_size偏移开始循环到len(data) - footer_size为止。对于本样本这种无record_length_field的定长记录每条记录直接按record.size即 905 字节切块_decode_record()再按字段宽度依次取字节交给_FieldDecoder解码。type为skip的字段只消耗字节、不产生列。字符解码_FieldDecoder._string()用 Python 标准库的 EBCDIC codec默认cp037解码然后在strip_control_characters为 True 时去掉 C0/C1 控制字符最后strip()去除首尾空格。这就是为什么description、zipcode等整段空格填充的字段在 groundtruth 表里呈现为空单元格。组装表格_build_table()为每个记录模式构建一张TableData第一行是字段名表头并给首行单元格打上column_header标记。若布局带description先向DoclingDocument追加一段TEXT文本这就是 groundtruth 第 1 行的来源只有多模式multi-schema布局才会为每个模式额外插入标题add_heading本样本是单模式所以 Markdown 里只有描述行加一张表。4. 字段模式详解与空值语义结合布局文件与 groundtruth 表格17 个字段的宽度与含义如下字段字节宽度说明结合样本数据service_request_id12请求编号如101005559344status6open/closedstatus_notes126状态说明如 “In progress - The request has been scheduled.”service_name30服务类别如Road - Pot hole、Graffitiservice_code10类别代码如CSROWR-12、30102description344描述本样本中绝大多数记录为空agency_responsible11责任机构样本中均为311 Torontoservice_notice1单字节标志位样本中全部为空requested_datetime/updated_datetime/expected_datetime各 25ISO 8601 时间戳含时区偏移address130地址含Ward: ...选区标注address_id8地址编号zipcode6邮编样本中为空long/lat各 14十进制经纬度字符串media_url118附件图片 URL仅少数记录非空从源码结构看string只是六种字段类型之一。EbcdicFieldType定义于 docling/datamodel/backend_options.py完整枚举为类型值COBOL 对应解码方式见_FieldDecoderstringUSAGE DISPLAY字符数据EBCDIC codec 控制字符清理integer/unsigned_integerCOMP/BINARY大端二进制定点整数int.from_bytespacked_decimalCOMP-3每字节两位数字末半字节为符号0xB/0xD为负zoned_decimal带符号USAGE DISPLAY数值每字节低四位是数字末字节高四位是符号skipfiller消耗字节、不产出列数值类型支持scale参数对应 COBOL picture 子句V后的位数解码后按Decimal定点缩放。测试test_single_schema_decodes_cobol_field_types演示了同一记录内混合使用这几种类型的解码结果1234.56、-987.65等说明本样本虽然全部是string字段但同一套后端机制完全覆盖数值型主框架数据。5. 测试如何消费这份 groundtruthtests/test_backend_ebcdic.py 中的端到端用例按参数化方式遍历tests/data/ebcdic/sources/下全部.ebc样本311_calls_for_service是其中之一pytest.mark.parametrize(source, sorted(SOURCES.glob(*.ebc)), idslambda p: p.stem) def test_e2e_ebcdic_conversions(source: Path): gt_path source.parent.parent / groundtruth / source.name result _converter(source.with_suffix(.layout.json)).convert(source) doc: DoclingDocument result.document pred_md doc.export_to_markdown(escape_htmlFalse, compact_tablesTrue) assert verify_export(pred_md, str(gt_path) .md, generateGENERATE), ( export to md )其中_converter构造的转换器为DocumentConverter(format_options{InputFormat.EBCDIC: EbcdicFormatOption(backend_optionsEbcdicBackendOptions(layout_file...))})。校验逻辑在 tests/verify_utils.py 的verify_export()先把换行归一化为 LF再与 groundtruth 做逐字符严格相等比较该用例不启用 fuzzy 模式。GENERATE由环境变量开关DOCLING_GEN_TEST_DATA控制见 tests/test_data_gen_flag.py开启时测试会把新导出的 Markdown 直接写回 groundtruth 文件因此官方再生成命令是DOCLING_GEN_TEST_DATA1 uv run pytest tests/test_backend_ebcdic.py同目录的另外两个样本覆盖了更复杂的场景见 tests/data/ebcdic/README.md样本布局特征311_calls_for_service单模式、17 个字符字段gas_disposition单模式、26 个字符字段、200 字节记录ola013k记录类型前缀后挂 4 种模式、含 483 个 packed-decimal 字段其中ola013k在转换后会扇出为 4 张表每模式一张各 6 行1 表头 5 记录列数分别为 209、307、360、489——测试test_ola013k_splits_records_across_schemas对此有断言。而参考项目的第 4 个样本service_segment_data因依赖OCCURS DEPENDING ON重复组layoutvariable未被纳入当前后端未实现该特性这属于明确声明的限制。README 还解释了为什么 groundtruth 只保留 Markdown 而不同时保存序列化的DoclingDocumentJSON这类文档“就只是一张解码值表格”再存 JSON 会增加几 MB 的单元格脚手架而不覆盖表格之外的任何信息文档结构断言直接写在测试里。6. 实操转换你自己的 .ebc 文件按 docs/usage/supported_formats.mdEBCDIC 格式支持的扩展名为.ebc、.ebcdic且必须通过EbcdicBackendOptions提供 COBOL 记录布局。参照测试中的用法最小可用脚本如下from docling.datamodel.backend_options import ( EbcdicBackendOptions, EbcdicField, EbcdicFieldType, EbcdicLayout, EbcdicRecordLayout, ) from docling.datamodel.base_models import InputFormat from docling.document_converter import DocumentConverter, EbcdicFormatOption converter DocumentConverter( format_options{ InputFormat.EBCDIC: EbcdicFormatOption( backend_optionsEbcdicBackendOptions( layout_filepath/to/layout.json, # 与内联 layout 二选一 encodingcp037, # 默认值亦可 cp500、cp1140 max_records40, # 可选只读前 N 条记录 ) ) } ) result converter.convert(path/to/file.ebc) print(result.document.export_to_markdown(escape_htmlFalse, compact_tablesTrue))也可以不写 JSON 文件直接内联布局与测试employee_layoutfixture 相同的方式layout EbcdicLayout( descriptionemployee master, records[ EbcdicRecordLayout( nameemployee, fields[ EbcdicField(namename, size10), EbcdicField(namefiller, size2, typeEbcdicFieldType.SKIP), EbcdicField(namewages, size5, typeEbcdicFieldType.PACKED_DECIMAL, scale2), EbcdicField(nameid, size4, typeEbcdicFieldType.INTEGER), ], ) ], ) options EbcdicBackendOptions(layoutlayout)EbcdicBackendOptions的完整参数定义与默认值见 docling/datamodel/backend_options.py参数类型默认值说明layoutEbcdicLayoutNone内联布局与layout_file互斥同时设置会触发ValidationError测试test_layout_sources_are_mutually_exclusivelayout_filePathNoneJSON 布局文件路径与layout二选一二者皆无则DocumentLoadErrorencodingstrcp037Python EBCDIC codec文档注明可选cp037US/Canada、cp500international、cp1140euromax_recordsPositiveIntNone解码 N 条记录后停止测试test_max_records_stops_early验证 2 条记录时表格恰好 3 行表头 2 行strip_control_charactersboolTrue是否从解码后的字符数据中剔除控制字符EbcdicLayout层面还支持本样本未用到的选项header_size/footer_size跳过文件首尾字节、record_length_field变长记录的长度前缀声明后记录体长度 前缀值 −prefix_size、record_type_field记录类型前缀多模式文件必填缺失会抛record_type_field is required校验错误测试test_multi_schema_layout_requires_a_record_type_field有覆盖。变长 多模式的完整示例可参考test_multi_schema_variable_length_records。错误处理同样值得注意文件在记录中间被截断时抛EbcdicDecodeError消息形如 “Input ends inside employee: N of M bytes left.”test_truncated_record_is_reported记录类型值没有匹配的模式时抛EbcdicDecodeError消息为 “No record layout matches record type ...”test_unknown_record_type_is_reported单个字段无法按声明类型解码时同样抛EbcdicDecodeError并携带字段的十六进制字节便于排查见EbcdicDecodeError与_FieldDecoder.decode()的实现。7. 适用边界与小结适用前提与限制EBCDIC 后端是纯 Python 解码字符走标准库 codecCOBOL 数值用法按半字节拆包不依赖任何外部库记录必须是固定长度或声明record_length_field的变长且必须提供布局——没有 copybook 就没有可解释的列OCCURS DEPENDING ON重复组尚未实现这是 tests/data/ebcdic/README.md 明确记录的未覆盖场景定宽文件无分页概念supports_pagination()返回False转换结果是“每个记录模式一张表”的扁平结构。小结311_calls_for_service.ebc.md这份看似普通的表格实际上是一条完整证据链的终点——905 字节 × 40 条 cp037 定宽记录经EbcdicDocumentBackend依据 17 字段布局逐字节解码_build_table()排成带列头表格再由export_to_markdown(escape_htmlFalse, compact_tablesTrue)落盘最终被test_e2e_ebcdic_conversions以逐字符严格比对的方式守护。对于手头有主框架导出的.ebc/.ebcdic定宽文件以及配套 copybook的开发者这条“layout JSON EbcdicFormatOption”的管线可以直接复用把无法被常规工具打开的主框架数据变成下游 RAG 与 LLM 可直接消费的 Markdown 表格。【免费下载链接】doclingGet your documents ready for gen AI项目地址: https://gitcode.com/GitHub_Trending/do/docling创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价