一、简介
FFReader 提供完整的命令行模式(CLI),面向开发、运维与数据分析人员:一条命令即可将 OFD / CSV / FIXED / DBF 接口文件批量转换为 CSV / Excel / JSON / SQL,或对文件与目录做批量比对(diff),输出 Excel / JSON / HTML 差异报告。配合 --stdout 管道输出与 JSON 导出的 AI 解读元信息,可无缝衔接脚本、流水线与 AI 工具。
如需可视化的解析、阅读与编辑操作,请参阅FFReader使用手册。
| 能力 | 说明 |
|---|---|
| 文件解析 | 自动识别 OFD / CSV / FIXED / DBF,一般情况下无需手动指定类型 |
| 导出格式 | csv、xlsx(Excel)、json(含 AI 解析元信息)、sql(INSERT 或者REPLACE等语句) |
| 文件比对 | 单文件或目录级批量 diff,输出差异报告为 Excel / JSON / HTML |
| 批量处理 | --batch 目录级批量转换与批量比对,支持 glob 过滤、排除、大小限制 |
| 标准输出 | --stdout 将转换结果或 diffjson 直接输出到 stdout(便于管道/AI 分析) |
| 配置匹配 | 自动按版本/字段数/行长匹配 INI 配置,支持 -p 手动指定。注意:CLI 暂不支持无配置的自动解析 CSV 文件(该能力仅 GUI 提供)——所有 CSV 配置均未命中时报 No CSV config matches this file,建议补全 config/ 下对应解析配置后使用 |
| 流式处理 | 大部分场景下csv/json/sql 导出逐块写出、内存占用量较低;对于不支持流式处理的xlsx 导出以及数据接口比对全量载入内存,此时可用 --max-size 限制文件大小 |
二、运行环境与可执行程序
各平台可执行程序
| 平台 | CLI 模式使用的程序 | 说明 |
|---|---|---|
| Windows | FFReaderCli-x64.exe / FFReaderCli-x86.exe / FFReaderCli-ARM64.exe |
专用命令行程序(原生控制台程序,stdout/stderr 分离,管道与重定向直接可用)。按系统架构选择对应文件 |
| Linux | FFReader-x86_64.AppImage / FFReader-aarch64.AppImage |
单一程序同时支持 GUI 与 CLI(-c)模式 |
| macOS | FFReader.app/Contents/MacOS/FFReader |
单一程序同时支持 GUI 与 CLI(-c)模式 |
注意:Windows 下的
FFReader-x64.exe/FFReader-x86.exe/FFReader-ARM64.exe为 GUI 程序,不再支持 CLI 模式(传入-c等命令行参数时仅弹窗提示改用 FFReaderCli)。
Linux AppImage 启动失败说明:在部分 Linux 发行版(如麒麟 Kylin 等 FUSE 挂载不可用或不稳定的环境)上,直接运行 AppImage 可能启动失败,终端打印
execv error: Socket not connected(errno 107 ENOTCONN)。这是 AppImage type2 运行器(runtime)的报错——运行时先用 FUSE 把内部 squashfs 挂载到/tmp/.mount_XXX再execv()启动内部 AppRun,挂载通道失效时execv()失败即打印该错误,与 FFReader 程序本体、Qt 库均无关。遇到该错误时,请使用 AppImage 官方绕过 FUSE 挂载的参数--appimage-extract-and-run运行(解压到临时目录后直接运行,不依赖 FUSE):./FFReader-x86_64.AppImage --appimage-extract-and-run -c -f input.txt -t csv -q 2 # 等价环境变量方式 export APPIMAGE_EXTRACT_AND_RUN=1 && ./FFReader-x86_64.AppImage -c -f input.txt -t csv -q 2
环境要求与配置目录
- Windows 系统按架构匹配:常规 64 位系统找
FFReaderCli-x64.exe,ARM 设备找FFReaderCli-ARM64.exe。 - Windows 系统下运行时,需保证可执行文件同级目录存在
config/配置目录(缺失时无法解析任何文件)。 - Linux 系统下,配置会在程序首次运行时自动解压存储到用户home目录下的
.ffreader/config/目录(缺失时无法解析任何文件)。如从未修改过该配置,升级 FFReader 后请删除此目录,程序运行时会自动解压新版本配置,或者使用一次GUI模式(此时会提示更新配置),如本地有修改配置,建议做好配置备份,以免升级时被误覆盖 - Windows 未找到 CLI 程序时,可从 www.ffreader.cn 下载包含命令行程序的完整版本。
三、快速上手
一个最简单的转换
# Linux / macOS:使用通用可执行程序,必须加 -c 进入 CLI 模式
FFReader -c -f OFD_XXX_XXX_20181016_04.TXT -t csv -q 2
# Windows 使用专用CLI程序FFReaderCli 无需 -c
FFReaderCli-x64.exe -f OFD_XXX_XXX_20181016_04.TXT -t csv -q 2
执行后,会在源文件同目录生成 OFD_XXX_XXX_20181016_04.TXT.export.csv。
查看帮助与版本
FFReader -h # 显示帮助信息
FFReader -c -v # 显示版本和作者信息(Windows FFReaderCli 可直接 -v)
四、命令行基础与通用参数
命令格式
FFReader -c [必选参数] [可选参数]
通用参数
| 参数 | 说明 |
|---|---|
-c / --cli |
启用 CLI 模式(Linux/macOS 单一程序下必须传递该参数,否则启动 GUI;Windows 独立命令行程序 FFReaderCli 无需此参数,传入亦被忽略) |
-h / --help |
显示帮助信息(单独使用 -h 也会进入 CLI 模式,无需 -c) |
-v / --version |
显示程序版本和作者信息(Linux/macOS 需与 -c 搭配;Windows FFReaderCli 可直接使用) |
-p <配置> / --config |
指定解析配置,格式见第十一章 |
-o / --overwrite |
目标文件已存在时强制覆盖 |
-O <路径> / --output |
指定输出文件路径(可以是目录或具体文件路径;目录必须已存在,程序不会自动创建,指定具体文件路径时其父目录也必须已存在) |
--stdout |
结果输出到标准输出而非文件。支持:单文件转换 -t csv/json/sql、比对 -t diffjson(含批量比对);不支持 xlsx、diffxlsx 和 diffhtml(Excel/HTML 为文件格式,无法流式输出)。启用时 -o/-O 被忽略。注意:批量模式下 stdout 为逐文件 JSON 顺序拼接(多个 JSON 文档连排,非单一 JSON 文档),需流式解析;仅要单一汇总 JSON 需配合 --summary-only(仅批量比对支持);进度与错误信息始终走 stderr,stdout 仅承载转换数据,可安全用于管道 |
--batch |
批量模式:处理目录下所有文件(不递归子目录),详见第七章 |
--max-size <大小> |
文件大小限制:防止超大文件载入内存导致程序崩溃。支持 KB/MB/GB 单位(如 100MB),无单位按字节,-1 不限制(默认)。超限行为按模式区分(详见各模式章节):单文件导出报错退出(退出码 -1);批量导出跳过不导出;单文件/批量比对仅二进制比对、跳过接口比对,且仅 diffjson 生成报告(标记 sizeExceeded=true,无字段定义/主键信息),diffhtml/diffxlsx 超限时无明细数据、不生成报告。csv/json/sql 导出为流式处理,该限制主要针对 xlsx 导出(数据全量载入内存)与比对模式(新旧文件均需载入内存) |
五、文件转换(单文件导出)
基本用法
FFReader -c -f <文件路径> -t <csv|xlsx|json|sql> [可选参数]
| 参数 | 说明 |
|---|---|
-f <文件路径> / --file |
待解析的源文件路径(必选) |
-t <类型> / --type |
导出目标类型:csv / xlsx / json / sql(必选) |
--max-size <大小> |
文件大小限制(见通用参数):超限文件直接报错退出(退出码 -1),不执行导出 |
输出文件命名规则(未用 -O 指定时): 与源文件同目录,文件名为 <源文件名(含原扩展名)>.export.<新扩展名>,如 OFD_XXX_XXX_20181016_04.TXT 导出 CSV 得到 OFD_XXX_XXX_20181016_04.TXT.export.csv。
CSV 格式可选参数
| 参数 | 默认值 | 说明 |
|---|---|---|
-d <分隔符> / --delimiter |
, |
CSV 导出时的字段分隔符(1-10 个字符,不得包含换行、引号、反斜杠) |
-q <1|2|3> / --quote-strategy |
2(RFC4180) | CSV 引号策略:1=不加引号 2=RFC4180按需 3=全部加引号。单文件 CSV 导出时必填(不传报错);批量模式下可选,默认 2 |
SQL 格式可选参数
| 参数 | 默认值 | 说明 |
|---|---|---|
-s <模板> / --sql-template |
— | SQL 模板,-t sql 时必须提供 |
-n / --null-empty |
否 | 空字段输出裸 NULL(用于 SQL 导出) |
-C <N> / --commit |
0(不输出) | 每 N 行插入一条 COMMIT;(N 取值 1-100000) |
JSON 格式可选参数
| 参数 | 默认值 | 说明 |
|---|---|---|
--check-primary-key |
否 | 检查并报告主键冲突信息(JSON 导出输出 primaryKeyConflict 段),所有行仍导出(含重复行),检测到冲突时退出码为 5。仅 -t json 有效,其他导出格式(csv/xlsx/sql)忽略此参数 |
--ignore-warnings |
否 | 抑制警告输出到 stderr,退出码返回正常值而非 5 |
六、文件比对(单文件 diff)
比对模式用于对比两个同类型文件,输出差异数据。
基本用法
FFReader -c -R <原文件> -N <新文件> -t <diffxlsx|diffjson|diffhtml> [可选参数]
| 参数 | 说明 |
|---|---|
-R <文件路径> / --orifile |
原文件路径(必选) |
-N <文件路径> / --newfile |
新文件路径(必选) |
-t <类型> / --type |
导出类型(必选):diffxlsx(差异 Excel)/ diffjson(差异 JSON)/ diffhtml(差异 HTML 可视化报告) |
比对可选参数
| 参数 | 默认值 | 说明 |
|---|---|---|
--diff-single-side |
否 | 比对单边字段(一方存在另一方不存在的字段视为差异) |
--ignore-fields <字段列表> |
空 | 忽略比对的字段(逗号分隔,支持英文名或中文描述) |
--force-compare |
否 | 强制比对(即使存在主键冲突也继续比对) |
--max-size <大小> |
-1(不限制) | 文件大小限制(见通用参数):超限文件仅二进制比对、跳过接口比对,且仅 diffjson 生成报告(标记 sizeExceeded=true,无字段定义/主键信息);diffhtml/diffxlsx 在此场景不生成报告 |
--ignore-warnings |
否 | 抑制解析警告输出,退出码不再返回 5、而按比对结果返回 0-4(警告仍记录在报告中)。可容忍轻微警告的自动化场景建议启用 |
--exp-diff-column <1|2|3> |
1 | 导出列模式:1=仅差异列 2=差异列+忽略列(有差异时) 3=所有列 |
--exp-diff-line <1|2|3> |
1 | 导出行模式:1=仅差异行 2=差异行+忽略差异行 3=所有行。对 diffhtml 无效(HTML 固定仅导出差异行) |
比对输出文件命名规则
未用 -O 指定时:与原文件同目录,<原文件名>.diffresult.json / <原文件名>.diffresult.html / <原文件名>.diffresult.xlsx(均无时间戳)。
用 -O 指定目录时:json/html 为 <目录>/<原文件名>.diffresult.json/html;xlsx 为 <目录>/<原文件名>.diffresult_<时间戳>.xlsx。
比对输出类型差异(单文件比对,OFD/CSV/FIXED/OFD Index 文件)
| 类型 | 生成时机 |
|---|---|
diffxlsx |
仅当导出行模式下的结果表非空时生成:默认 --exp-diff-line 1(仅差异行)即接口存在差异行才生成,接口内容一致时不生成;--exp-diff-line 2 在忽略字段存在差异时即使接口内容一致也生成;--exp-diff-line 3 所有行均入报告(含一致行),接口内容一致也生成。二进制一致时无论行模式如何均不生成 |
diffjson |
总是生成 JSON 报告(含摘要信息;二进制一致时仅含 metadata/summary 无数据行;data 行内容同样受 --exp-diff-line 影响——默认模式 1 下接口内容一致时为空数组,模式 3 下含所有行) |
diffhtml |
生成人类可读的可视化 HTML 报告,单文件差异行上限 5000 行(超出部分不导出;HTML 固定跳过一致行,不受 --exp-diff-line 影响,接口内容一致时输出简化报告) |
比对注意事项
- 比对前提:两个文件必须使用相同的主键定义,否则无法比对。
- 主键冲突:默认情况下主键冲突会终止比对,使用
--force-compare可强制继续。 - 单边字段:默认不比对单边字段,使用
--diff-single-side可启用。 - 忽略字段:使用
--ignore-fields可指定不参与比对的字段。 - DBF 不支持接口比对:DBF 文件仅做二进制比对,不做接口数据比对(二进制一致退出码 0,不一致退出码 1,与批量"仅二进制不一致"语义一致),stderr 会输出 Note 提示该语义。该场景不生成任何报告文件(diffjson/diffhtml/diffxlsx 均不生成,
--stdout也无 JSON 输出),比对结果仅由退出码与 stderr 提示承载。 - ZIP 文件:仅二进制比对,不做接口数据比对(二进制一致退出码 0,不一致退出码 1,与批量"仅二进制不一致"语义一致),stderr 同样输出 Note 提示,与 DBF 分支措辞、行为完全一致(同样不生成任何报告文件)。
- 二进制一致时:无论
--exp-diff-line如何设置都不导出数据行,仅含 metadata/summary;所有列信息仍在 metadata 中完整导出。
七、批量处理
--batch 启用目录级批量处理,不递归处理子目录(批量导出时检测到子目录会输出警告;批量比对则静默忽略子目录)。单文件模式下传入目录路径而未加 --batch 时,程序会给出友好提示并退出。
批量转换(批量导出)
FFReader -c --batch -f <输入目录> -t <csv|sql|xlsx|json> [可选参数]
| 参数 | 说明 |
|---|---|
-f <目录> |
输入目录(必选) |
-t <类型> |
导出类型:csv / sql / xlsx / json(必选) |
-O <目录>/ |
输出目录(必须以 / 结尾且已存在)。未指定时在输入目录下自动创建 exportresult_<时间戳> 子目录 |
--filter <模式> |
glob 过滤模式(可多次指定),如 --filter "*.txt",支持 *、?、[abc];优先级高于系统内置排除规则(但不高于 --filter-exclude) |
--filter-exclude <模式> |
glob 排除模式(可多次指定),如 --filter-exclude "*.bak";优先级高于 --filter,同时命中时以排除为准 |
--max-size <大小> |
文件大小限制(见通用参数):超过限制的文件跳过不导出 |
--stdout |
仅支持 csv/json/sql(xlsx 不支持);逐文件内容顺序拼接写入 stdout、无文件边界标记(json 为多个 JSON 文档连排,需流式解析;csv 每文件含表头行;sql 语句顺序拼接);进度与摘要走 stderr,不污染数据流 |
-s / -n / -C / -d / -q / -p |
与单文件模式含义相同 |
输出文件命名:每个文件生成 <文件名>.export.<扩展名>,放在输出目录中。
批量比对
FFReader -c --batch -R <原目录> -N <新目录> -t <diffxlsx|diffjson|diffhtml> [可选参数]
| 参数 | 说明 |
|---|---|
-R <目录> / -N <目录> |
原目录和新目录(均必选,两个目录都必须存在) |
-t <类型> |
diffxlsx / diffjson / diffhtml(必选) |
-O <路径> |
两种形式:以 .json/.html/.xlsx 结尾的文件路径=输出汇总报告(仅 --summary-only 模式下允许,否则必须传目录路径,程序会报错);以 / 结尾的目录路径=输出全部结果到该目录。未指定时在原目录下自动创建 diffresult_<时间戳> 子目录。注意:-O 文件路径只对 diffjson/diffhtml 汇总生效,-t diffxlsx 汇总始终写入 diffresult[_<时间戳>].xlsx,忽略 -O 文件路径 |
--summary-only |
仅导出汇总报告(diffresult.xlsx/json/html),跳过单文件差异报告(*.diffresult.*) |
--batch-diff-summary-rows <N> |
汇总报告中每个文件的最大差异明细行数,取值 10-500,默认 50(仅对批量比对 diffjson/diffhtml 生效;更多明细见单文件报告) |
--force-compare |
存在主键冲突时仍执行接口比对,主键配置可自定义修改,修改后建议做好本地配置备份 |
--stdout |
仅支持 diffjson。--summary-only 时输出单一汇总 JSON;否则输出逐文件差异 JSON + 汇总 JSON 顺序拼接(多个 JSON 文档连排,需流式解析) |
--filter / --filter-exclude / --max-size |
--filter / --filter-exclude 同批量转换;--max-size 文件大小限制(见通用参数):超限文件仅二进制比对、跳过接口比对,汇总报告(json/xlsx/html 均生成)中标记 sizeExceeded=true,单文件明细报告仅 diffjson 生成(与单文件比对一致,不整体排除) |
--ignore-fields / --exp-diff-column / --exp-diff-line / --diff-single-side |
同单文件比对模式 |
-p <配置> |
指定解析配置,仅支持单个配置(应用到原、新两文件),不支持单文件比对模式的 | 双配置分隔 |
批量比对文件匹配规则: 按文件名在两个目录间匹配;仅存在于一方目录的文件标记为单边文件(“Only in original/new directory”),计入结果但不做接口比对。
多配置自动选择(批量比对): 批量比对时若某文件自动匹配到多个解析配置,程序会自动使用第一个匹配的配置继续处理(记录解析警告,不中断批量流程);这与单文件模式不同——单文件比对/导出命中多个配置会报错并列出全部候选配置,需用 -p 指定其一。因此批量比对遇到可疑结果时,建议对该文件单独执行单文件比对并用 -p 锁定配置复核。
批量比对输出:
- 汇总报告:
diffresult.json/diffresult.html(无时间戳);xlsx 为diffresult_<时间戳>.xlsx(仅自动创建输出目录时含时间戳,指定-O目录时为diffresult.xlsx) - 单文件差异报告(非
--summary-only时):json/html 为<文件名>.diffresult.json/html;xlsx 为<文件名>.diffresult_<时间戳>.xlsx
八、各文件类型解析说明
OFD 文件(Open Fund Data)
开放式基金交换协议标准格式或其体系内的其他协议,分两种子类型:
OFD Index 文件(索引文件)
- 文件结构:6 行固定文件头 + 若干索引行(每行一条文件名记录) +
OFDCFEND结束标志 - 支持导出:
csv、xlsx、json(JSON 含专用元信息结构,见第十章);SQL 导出未针对其单列结构做适配,不建议使用 - 支持比对模式(以 filename 为虚拟主键)
OFD Data 文件(数据文件)
- 文件结构:版本行、文件类型行、字段列表、记录数行,之后为定长数据行
- 配置文件:
config/OFD_<版本>.ini,如OFD_21.ini - 支持全部四种导出格式及比对模式
- 多配置碰撞时程序会输出所有候选配置供
-p指定(单文件模式报错;批量比对自动使用第一个匹配配置,见第七章)
CSV 文件(固定分隔符文件)
- 配置文件:
config/CSV_*.ini - 支持自定义分隔符、首行校验、版本校验、尾部忽略行
- AUTO 编码使用 LibUcd 自动检测编码(读取文件前 4096 字节);检测结果不在允许列表时回退为 UTF-8,windows-125x / koi8-r 等识别为 GB18030
- 支持全部四种导出格式及比对模式
FIXED 文件(定长字段文件)
- 配置文件:
config/FIXED_*.ini - 支持多种行长度变体(如 NAV 文件)
- 支持字节定长(
fieldlengthtype=0)和字符定长两种模式 - 增强校验:首行、尾行、版本行、字段数行、字段明细行
- 支持全部四种导出格式及比对模式
DBF 文件( dBase/FoxPro数据文件)
- 配置文件:
config/DBF_*.ini(提供字段描述翻译,非必须) - 自动按文件名通配 + 字段名匹配度(≥30%)选择配置
- DBF 导出 JSON 时
configSegment恒为空字符串(DBF 无配置段概念) - 支持全部四种导出格式(JSON 导出中所有字段
type恒为S、值恒为字符串) - 仅支持二进制比对(DBF 文件结构不适用于接口数据比对:二进制一致退出码 0,不一致退出码 1;比对时不生成任何报告文件,见第六章比对输出类型差异)
九、导出格式详解
CSV 导出
- 引号策略:1=不加引号 2=RFC4180(含分隔符/换行/引号时加引号,默认) 3=全部加引号
- 输出文件含标题行(字段描述 + 字段名,如
基金代码(FundCode)) - 0 行文件仍创建只含标题行的空 CSV
Excel 导出(xlsx)
- 最大行数限制:100 万行(超出报错退出,建议改用 CSV 或 SQL)
- 标题行:绿色背景、居中、加粗
- 数值型字段(
N类型)自动应用千分位格式 - 字段宽度自适应内容长度
JSON 导出
JSON 导出专为 AI 分析和程序解析设计,包含完整的元信息和数据结构,流式生成(超大文件不受内存限制),文件输出与 --stdout 输出内容一致。详细 Schema 见第十章。
SQL 导出
- 必须搭配
-s参数提供 SQL 模板 - 模板中用
%N(N 从 1 起)引用字段,如%1= 第 1 字段 '%N'模式(单引号包围):程序自动处理引号,空值 +-n时输出裸NULL而非''%N模式(无引号):值直接替换,适用于数值字段- 字段值中的单引号自动转义为
''(ANSI SQL 标准) - 模板中不允许出现
%0,字段编号超出实际字段数时报错
SQL 模板示例:
-- 字符串字段加引号,数值字段不加引号
INSERT INTO fund_nav VALUES('%1','%2',%3,%4,'%5');
-- 所有字段均为字符串
INSERT INTO t (code,name,amt) VALUES('%1','%2','%3');
-- 搭配 -n 参数处理可空字段(空值自动输出 NULL)
INSERT INTO t VALUES('%1','%2','%3');
-- 不加 -n:INSERT INTO t VALUES('A001','','')
-- 加 -n:INSERT INTO t VALUES('A001',NULL,NULL)
COMMIT 控制:
# 每 1000 行插入一条 COMMIT(-C 取值 1-100000)
FFReader -c -f OFD_XXX_XXX_20181016_04.TXT -t sql -s "INSERT INTO t VALUES('%1');" -C 1000
十、JSON 结构说明
程序共有三类 JSON 输出,schema 各不相同:
| JSON 类型 | 产生方式 |
|---|---|
| 数据导出 JSON | -t json(单文件 / --batch 批量 / --stdout) |
| 单文件比对 JSON | -t diffjson(含批量比对生成的 *.diffresult.json 单文件报告) |
| 批量比对汇总 JSON | --batch -t diffjson(diffresult.json 或 --stdout) |
数据导出 JSON Schema(-t json)
顶层结构(键按出现顺序):
{
"metadata": {
"ai_parse_hint": "...",
"fileType": "OFD",
"configFile": "OFD_21.ini",
"configSegment": "[04_001_FUND]",
"description": "开放式基金-交易类确认",
"fields": [
{"name": "AppSheetSerialNo", "describe": "申请单编号/申请编号", "type": "S"},
{"name": "LargeRedemptionFlag", "describe": "巨额赎回处理标志", "type": "S", "dict": {"0": "取消", "1": "顺延"}},
{"name": "ConfirmedVol", "describe": "确认份额", "type": "N"}
],
"primaryKey": ["TASerialNO"]
},
"data": [
{"rowNum": 1, "lineNum": 133, "fields": {"AppSheetSerialNo": "20181015002403", "LargeRedemptionFlag": "0", "ConfirmedVol": 90000000.0}}
],
"parseWarnings": ["Missing end marker: OFDCFEND not found"],
"primaryKeyConflict": { "hasConflict": true, "conflictKeys": [{"key": "20181015002403", "count": 2}], "totalConflictKeys": 1, "totalRows": 1066, "uniqueRows": 1065 }
}
metadata 对象字段:
| 字段 | 类型 | 说明 |
|---|---|---|
ai_parse_hint |
string | AI 解读指引全文,内嵌完整的结构说明与解读要求 |
fileType |
string | 文件类型标识:OFD / CSV / FIXED / DBF / OFDIndex |
header |
object | 仅 OFD Index 文件存在,6 行文件头的解析结果 |
totalFiles |
number | 仅 OFD Index 文件存在,实际索引记录数 |
configFile |
string | 解析使用的配置文件名(DBF 未匹配到配置时为 DBF_AUTO) |
configSegment |
string | 配置段名;DBF 文件恒为空字符串 |
description |
string | 接口文件描述(如"开放式基金-交易类确认") |
fields |
array | 字段定义数组 |
primaryKey |
array<string> | 可选键,OFD/CSV/FIXED 数据文件且配置定义了主键时输出;DBF 文件不输出此键 |
metadata.fields[] 元素:
| 字段 | 类型 | 说明 |
|---|---|---|
name |
string | 字段名,即 data[].fields 的键名;配置中字段名为空时回退使用中文描述 |
describe |
string | 字段中文描述,非空时输出 |
type |
string | N=数值,S=非数值(含字符串、字符、日期时间等)。DBF 文件所有字段恒为 S |
dict |
object | 可选,枚举值→含义映射,按键升序排列 |
primaryKeyConflict 对象(可选): 仅当使用 --check-primary-key 且检测到主键冲突时输出;位于 data 与 parseWarnings 之后(主键冲突需在全部数据行写入完成后统计)。仅对配置了主键(fieldprimarylist,字段索引从 0 开始,格式见第十一章配置系统与 -p 参数)的 OFD/CSV/FIXED 数据文件生效,DBF/OFD Index 文件无主键配置,永不输出该段:
| 字段 | 说明 |
|---|---|
hasConflict |
是否存在主键冲突 |
conflictKeys |
每个元素为 {"key": "主键值", "count": 出现次数} |
totalConflictKeys |
冲突主键总数 |
totalRows |
总数据行数 |
uniqueRows |
唯一主键数 |
data[] 元素(数据文件):
| 字段 | 说明 |
|---|---|
rowNum |
数据行号,从 1 开始 |
lineNum |
原始文件物理行号(含文件头),从 1 开始;如某交易类确认文件头 132 行时首条数据 lineNum=133 |
fields |
全部字段的键值对 |
data 取值规则:
type: "N"且值可转换为数值时输出 JSON 数字,否则(含空值、转换失败)输出字符串- DBF 文件所有字段值均为字符串(type 恒为 S);DBF 的
rowNum与lineNum相同(无文件头) parseWarnings数组始终输出,位于 JSON 尾部;无警告时为空数组[]
字段键名规则(重要):
data[].fields 的键名严格等于 metadata.fields[].name,而 name 的内容由所用配置文件决定,因此不同文件类型/配置的键名风格不同,不能假设统一为英文:
- 解析
data时不要硬编码字段名,应先读metadata.fields[].name再按名取值 - 同一个
name可能重复出现语义相同但风格不同的写法,交叉核对请以metadata.fields为准 metadata.fields[].describe为字段中文释义,可用于向用户展示可读名称
OFD Index 文件 JSON 专有结构
{
"metadata": {
"ai_parse_hint": "...",
"fileType": "OFDIndex",
"header": {
"identifier": "OFDCFIDX",
"version": "22",
"creator": "918A",
"receiver": "YZX",
"date": "20210318",
"fileCount": "5",
"actualFileCount": 5
},
"totalFiles": 5,
"configFile": "OFD_IndexFile.ini",
"configSegment": "[OFDINDEXFILE]",
"description": "OFD Index File",
"fields": [{"name": "filename", "describe": "文件名", "type": "S"}]
},
"data": [
{"rowNum": 1, "lineNum": 7, "fileName": "OFD_918A_YZX_20210318_01.TXT"}
],
"parseWarnings": ["File count mismatch: declared 4, actual 5"]
}
header 对象字段:
| 字段 | 说明 |
|---|---|
identifier |
文件标识,规范固定值 OFDCFIDX |
version |
版本号(如 22 表示 2.2 版本) |
creator |
文件创建人代码 |
receiver |
文件接收人代码 |
date |
日期,YYYYMMDD(与文件名中日期一致,非机器时间) |
fileCount |
声明的数据文件个数 |
actualFileCount |
实际解析的索引记录数 |
data[] 元素(OFD Index):
| 字段 | 说明 |
|---|---|
rowNum |
数据行号,从 1 开始 |
lineNum |
原始文件行号,从 7 开始(前 6 行为文件头) |
fileName |
数据文件名(注意:无 fields 对象,键名为 fileName) |
校验提示:header.fileCount 与 header.actualFileCount 不一致表示声明与实际不符;重复文件名、缺少 OFDCFEND 结束标志等会记入 parseWarnings。
单文件比对 JSON(diffjson)Schema
顶层为 metadata / summary / data 三个键(批量比对生成的 *.diffresult.json 单文件报告同此结构):
{
"metadata": {
"ai_parse_hint": "...",
"fileType": "FIXED",
"oriFilePath": "/data/oldpath/C103100259-Z700110227-001-20230725-01.txt",
"oriConfigFile": "FIXED_WDEP_理财产品中央数据交换协议_V1.0.ini",
"oriConfigSegment": "[??????????-??????????-001-20??????-*.txt*|1.0]",
"oriDescription": "001-01-账户申请",
"newFilePath": "/data/newpath/C103100259-Z700110227-001-20230725-01.txt",
"newConfigFile": "FIXED_WDEP_理财产品中央数据交换协议_V1.0.ini",
"newConfigSegment": "[??????????-??????????-001-20??????-*.txt*|1.0]",
"newDescription": "001-01-账户申请",
"fields": [{"name": "AppSheetSerialNo", "describe": "申请单编号", "type": "S"}, {"name": "IndividualOrInstitution", "describe": "个人/机构标志", "type": "S", "dict": {"0": "个人", "1": "机构", "2": "产品", "3": "私募产品", "9": "全国"}}],
"primaryKey": ["AppSheetSerialNo"],
"ignoredFields": ["Address"]
},
"summary": {
"success": true,
"binaryEqual": false,
"sizeExceeded": false,
"sizeExceedDetail": "",
"errorMessage": "",
"versionEqual": true,
"contentEqual": false,
"oriRows": 4,
"newRows": 4,
"oriPrimaryConflict": false,
"oriPrimaryConflictKeys": [],
"newPrimaryConflict": false,
"newPrimaryConflictKeys": [],
"forceCompare": false,
"totalDiffCount": 1,
"warnMessage": ""
},
"data": [
{
"primaryFields": {"AppSheetSerialNo": "998320230725000000984919"},
"diffFields": {"IndividualOrInstitution": {"oldValue": "1", "newValue": "0"}},
"result": "比对不一致"
}
]
}
summary 对象字段(全部键始终输出):
| 字段 | 说明 |
|---|---|
success |
比对流程是否成功执行 |
binaryEqual |
二进制是否一致 |
sizeExceeded |
是否因超过大小限制跳过接口比对 |
sizeExceedDetail |
超限详情,无超限时为空字符串 |
errorMessage |
错误信息,无错误时为空字符串 |
versionEqual |
接口版本是否一致;null=未进行接口解析(超限或解析失败) |
contentEqual |
接口数据内容是否一致 |
oriRows / newRows |
原/新文件数据行数 |
oriPrimaryConflict / newPrimaryConflict |
原/新文件是否存在主键冲突 |
oriPrimaryConflictKeys / newPrimaryConflictKeys |
冲突主键详情,元素为 {"key": "主键值", "count": 出现行数};联合主键各字段值以 - 连接 |
forceCompare |
是否强制比对 |
totalDiffCount |
总差异行数(不一致+新增+删除);-1 表示未比对/解析失败/无主键 |
warnMessage |
警告信息;存在主键冲突时输出"仅首条重复主键记录参与比对"提示 |
data[] 元素(每行一条差异记录):
| 字段 | 说明 |
|---|---|
primaryFields |
主键字段名->字段值(有主键配置时才存在该键) |
diffFields |
差异字段:双边差异为 {"oldValue": "原值", "newValue": "新值"} 对象;单边记录(新增/删除)直接为值字符串。模式 1/2 下单边记录此对象为空 |
ignoredDiffFields |
被忽略字段的差异(仅模式 2/3 输出) |
sameFields |
一致字段及其值(仅模式 3 输出) |
result |
行结果:比对一致 / 忽略指定列后一致 / 比对不一致 / 原文件中无此记录(新增) / 新文件中无此记录(删除) |
行结构随 --exp-diff-column 模式变化:模式 1=primaryFields+diffFields+result;模式 2=模式 1+ignoredDiffFields;模式 3=模式 2+sameFields。
行数由 --exp-diff-line 控制(1 仅差异行、2 差异+忽略差异行、3 所有行);二进制一致时 data 恒为空数组。
注:data 记录中不含 rowNum/lineNum 行号,记录定位以
primaryFields为准。
批量比对汇总 JSON Schema(–batch -t diffjson)
顶层为 metadata / summary / files 三个键:
{
"metadata": {
"ai_parse_hint": "...",
"batchMode": true,
"oriDirectory": "/data/oldpath/",
"newDirectory": "/data/newpath/",
"exportType": "diffjson"
},
"summary": {
"totalFiles": 8,
"binaryMatchCount": 5,
"contentMatchCount": 0,
"contentMismatchCount": 2,
"singleSideCount": 0,
"binaryMatchSkippedCount": 0,
"binaryMismatchSkippedCount": 1,
"unknownCount": 0,
"summaryOnly": true
},
"files": [ {"fileName": "C103100259-Z700110227-001-20230725-01.txt", "...": "..."} ]
}
summary 对象字段:
| 字段 | 说明 |
|---|---|
totalFiles |
参与比对的总文件数 |
binaryMatchCount |
二进制完全一致的文件数 |
contentMatchCount |
接口数据内容一致的文件数(不含二进制一致的) |
contentMismatchCount |
接口数据不一致的文件数 |
singleSideCount |
单边文件数(仅一方目录存在) |
binaryMatchSkippedCount |
无法接口比对(超限/无主键/主键冲突等)且二进制一致的文件数 |
binaryMismatchSkippedCount |
无法接口比对且二进制不一致的文件数 |
unknownCount |
未知状态的文件数(兜底归类) |
summaryOnly |
是否仅导出汇总 |
files[] 元素(每个文件一条记录):
| 字段 | 说明 |
|---|---|
fileName |
文件名(两目录间按文件名匹配) |
hasOriFile / hasNewFile |
原/新目录是否存在该文件 |
oriFilePath / newFilePath |
原/新文件完整路径 |
binaryEqual |
二进制是否一致 |
contentEqual |
内容是否一致 |
versionEqual |
接口版本是否一致;null=未进行接口解析 |
compareResult |
标准化结果:Binary identical / Content identical / Content mismatch / Single-side file / Binary identical (binary-only comparison) / Binary mismatch (binary-only comparison) / Unknown |
oriRows / newRows |
原/新文件行数;null=未解析 |
oriPrimaryConflict / newPrimaryConflict |
是否存在主键冲突 |
oriPrimaryConflictCount / newPrimaryConflictCount |
冲突主键数;null=未检查 |
resultFilePath |
单文件差异报告路径(生成了单文件报告时非空:含二进制一致、内容一致、内容不一致及超限 diffjson 报告;未生成报告时为空:DBF/ZIP 二进制兜底、单边文件、空文件、解析失败、diffxlsx 内容一致、--summary-only、stdout 模式) |
interfaceCompareResult |
接口比对结果描述(与 compareResult 的标准化英文描述一致) |
errorMessage |
错误信息(仅失败时有值) |
beginTime / endTime |
开始/结束比对时间 |
sizeExceeded |
是否超限跳过接口比对 |
sizeExceedDetail |
超限详情(无超限时为空字符串) |
fields |
字段定义(仅不一致且有已解析差异时非空,按 --exp-diff-column 模式筛选,主键列始终包含) |
primaryKey |
主键列表(无差异数据时为 []) |
hasParsedDiffData |
是否有已解析的差异明细 |
totalDiffCount |
差异总行数 |
topDiffRecords |
前 N 条差异摘要(N 由 --batch-diff-summary-rows 控制,默认 50,取值 10-500) |
topDiffRecords[] 元素:
| 字段 | 说明 |
|---|---|
primaryFields |
主键字段名->字段值 |
diffFields |
差异字段(格式同单文件 diffjson 的 diffFields) |
result |
比对不一致 / 原文件中无此记录(新增) / 新文件中无此记录(删除) |
十一、配置系统与 -p 参数
配置文件位于程序目录下的 config/ 子目录:
| 文件模式 | 用途 |
|---|---|
OFD_<版本>.ini |
OFD 数据文件解析规则(如 OFD_21.ini) |
CSV_*.ini |
CSV 文件解析规则 |
FIXED_*.ini |
定长字段文件解析规则 |
DBF_*.ini |
DBF 字段名翻译 |
OFD_IndexFile.ini |
OFD 索引文件定义 |
OFD_CodeInfo.ini |
发送方/接收方机构代码映射 |
OFD_Dictionary.ini |
OFD 字段枚举值翻译 |
配置文件均为 UTF-8 编码的 INI 格式,修改后无需重新编译,重启程序即生效。
接口段内的主键配置(fieldprimarylist)
OFD/CSV/FIXED 配置文件的每个接口段(如 [01_001_FUND])内,在字段定义之后可配置主键:
fieldprimarylist="8" # 单字段主键
fieldprimarylist="0,4,8" # 联合主键(多个索引,逗号分隔)
解读规则:
- 字段索引从 0 开始(
0= 段内第 1 个字段,4= 第 5 个字段),与字段定义行1=...、2=...的行号差 1,配置时务必注意 - 多个索引构成联合主键,程序加载时自动去重并按升序排列
- 索引超出字段总数(COUNT)时该索引被忽略(不参与主键功能,不报错)
- 该配置仅对 OFD/CSV/FIXED 数据文件生效;DBF、OFD Index 文件无主键配置
示例(OFD_22.ini 的 [02_001_FUND] 段,共 27 个字段):
fieldprimarylist="8"
含义:主键为 0 基索引 8,即第 9 个字段 TASerialNO(TA确认交易流水号)。同文件 [01_001_FUND] 段的 fieldprimarylist="4" 即第 5 个字段 AppSheetSerialNo(申请单编号)。
主键配置是以下功能的前提:
- 单文件/批量比对的接口比对(无主键配置时比对退出码
2) - JSON 导出的
metadata.primaryKey字段 --check-primary-key主键冲突检测(冲突时退出码5,明细见primaryKeyConflict节点)- 不同公司对主键的认定可能不同,可直接编辑配置段调整主键定义,修改后建议做好本地配置备份
接口段内的必填规则配置(fieldcheck_XXXX)
接口段内还支持以 fieldcheck_ 为前缀的必填规则,可配置多条(fieldcheck_0001、fieldcheck_0002…),格式为 "条件|必填字段列表":
fieldcheck_0001="11=1&18=001|5,6,7,8,9,10,11,17,18,32"
└── 条件部分 ──┘└──────── 必填字段列表 ────────┘
| 部分 | 语法 | 说明 |
|---|---|---|
| 条件 | 序号=值 |
序号从 1 开始(与 fieldprimarylist 的 0 基索引不同);多个条件用 & 连接(且关系);多组条件用 ; 连接(或关系);条件部分写 ALL 表示对所有行无条件生效 |
| 必填字段列表 | 序号1,序号2,... |
序号从 1 开始;命中条件的行中这些字段不能为空 |
解读规则:
- 条件值与解析后的字段原始值做精确相等比较(不做字典翻译)
- 必填序号超出字段总数(COUNT)时该序号被忽略;序号非法则整条规则不加载
- 该规则仅 GUI 的必填校验功能使用,CLI 导出/比对不执行必填校验
示例(OFD_22.ini 的 [01_001_FUND] 段):
fieldcheck_0001="11=1&18=001|5,6,7,8,9,10,11,17,18,32"
含义:当第 11 个字段(个人/机构标志 IndividualOrInstitution)为 1 且第 18 个字段(业务代码 BusinessCode)为 001 时,第 5、6、7、8、9、10、11、17、18、32 个字段必填。
易混淆点:
fieldprimarylist的索引从 0 开始,fieldcheck_的序号从 1 开始,两套编号体系不一致,编写配置时需分别对待。
-p 参数格式
# OFD / CSV / FIXED 文件:需指定配置文件名 + 配置段(双括号格式)
-p "[OFD_21.ini][04_001_FUND]"
# DBF 文件:只需指定配置文件名(单括号格式)
-p "[DBF_上海证券登记结算数据接口.ini]"
# 单文件比对模式:两个文件可用不同配置,用 | 分隔
-p "[OFD_21.ini][04_001_FUND]|[OFD_21.ini][04_001_FUND]"
# 或两个文件用相同配置时只需指定一个
-p "[OFD_21.ini][04_001_FUND]"
# FIXED 配置段可能含通配符(按配置文件中实际段名书写)
-p "[FIXED_WDEP_理财产品中央数据交换协议_V1.0.ini][??????????-??????????-001-20??????-*.txt*|1.0]"
注意:
|双配置分隔仅单文件比对模式支持;批量比对-p只接受单个配置,应用到原、新两文件。
校验规则:
- 第一个括号内容必须以
OFD_、CSV_、FIXED_或DBF_开头 - OFD/CSV/FIXED 必须提供双括号(文件名+配置段),DBF 仅需单括号
- 比对模式
-p用|分隔时必须是 1 个或 2 个配置,多于 2 个报错 - 指定的配置文件和配置段必须实际存在,否则报错
十二、退出码速查表
转换模式(单文件导出)
| 退出码 | 含义 |
|---|---|
0 |
导出成功,无解析警告 |
5 |
导出成功,但存在解析警告(如缺少文件结束标志、尾部内容不匹配),或 --check-primary-key 检测到主键冲突 |
-1 |
导出失败 |
比对模式(单文件 diff)
| 退出码 | 含义 |
|---|---|
0 |
比对一致:文件内容相同,无警告,无主键冲突 |
1 |
比对不一致:文件内容不同,无警告,无主键冲突(含 ZIP/DBF 仅二进制不一致,与批量比对该场景的退出码语义一致) |
2 |
无主键配置:原文件或新文件(或两者)未配置主键,无法进行接口比对 |
3 |
内容一致但存在主键冲突(–force-compare 强制比对完成,或二进制一致但检测到主键冲突) |
4 |
强制比对完成,内容不一致且有主键冲突 |
5 |
警告:比对完成但存在解析警告(如缺少结束标志)或配置类警告(如二进制一致但配置未定义主键) |
-1 |
比对失败:解析错误、配置错误、主键冲突未强制比对等 |
注:退出码 0-4 表示无警告;只要存在警告(解析警告或配置类警告,如"Config has no primary key defined"),无论比对结果如何退出码均为 5(警告状态优先于比对结果)。无主键配置的退出码与二进制一致性有关:二进制不一致时无主键返回 2(无法接口比对),二进制一致时无主键降级为警告返回 5。若可容忍轻微警告,可加
--ignore-warnings:退出码将直接反映比对结果本身(0-4)。需注意:解析警告与配置类警告不写入比对 JSON(diffjson),--ignore-warnings后也不会输出到 stderr;比对 JSON 中唯一保留的警告是主键冲突(经summary.warnMessage字段记录)。
批量模式(批量导出和diff)
| 退出码 | 含义 |
|---|---|
0 |
全部文件处理成功(批量比对:全部一致) |
1 |
仅批量比对:存在差异(数据不一致/单边文件/仅二进制不一致),比对正常完成,非执行错误(与单文件比对退出码 1 语义一致) |
-1 |
参数错误或无效参数 |
6 |
批量导出:部分文件导出失败;批量比对:部分文件比对结果无法归类(执行异常) |
注:批量比对退出码不受解析警告影响(批量模式内部等效忽略警告,警告记录在 JSON 报告中),故批量比对无退出码 5。
退出码读取注意
注意:比对模式(单文件/批量)的退出码 1 表示"存在差异",属比对正常完成,不是失败
注意:因使用环境的环境不同,可能会出现错误码被转义的情况,编写脚本前建议做好验证
程序实际返回的失败退出码为 -1,不同 shell 读取到的值不同(判断请按"非 0 即失败",不要依赖具体数值):
| 执行环境 | -1 的实际表现 |
|---|---|
PowerShell($LASTEXITCODE) |
-1 |
cmd(%ERRORLEVEL%) |
-1 |
macOS / Linux shell(zsh / bash,echo $?) |
255(-1 的 8 位无符号表示) |
Git Bash / MSYS(echo $?) |
127(MSYS 层转换所致,非程序真实返回值) |
十三、错误信息与排查
| 错误信息 | 含义 | 处理建议 |
|---|---|---|
File not recognized. Supported formats: OFD, CSV, FIXED, DBF. Please provide a parse config (add to config/ directory or specify with -p) and try again |
文件类型无法识别(非文本/二进制文件、文本未被任何配置命中且自动引擎无法归类(无规整分隔符结构)、单行数据过长等场景均统一输出本条) | 补全解析配置(config/ 目录)或用 -p 指定已有配置后重试;若文件本身为二进制/损坏文件则无法解析 |
Unsupported file format: ... |
文件后缀属于不支持的类型(压缩包/音视频/图像/Office 等,CLI 中压缩文件统一提示先解压) | 压缩文件先解压再解析;其余类型不支持解析 |
This is an OK file of an OFD data file |
OFD 数据文件的 OK 回执文件 | 请解析对应的原始数据文件 |
This is a sqlldr bad file |
sqlldr 导入生成的 bad 文件 | 请解析对应的原始数据文件 |
This is a sqlldr control file |
sqlldr 导入使用的控制文件 | 请解析对应的原始数据文件 |
This is a file shortcut |
文件快捷方式 | 请解析快捷方式指向的原始文件 |
No CSV config matches this file |
所有 CSV 配置均未命中该文件 | CLI 暂不支持无配置的自动解析 CSV 文件(该能力仅 GUI 提供),建议在 config/ 下补全对应解析配置后重试,或用 -p 指定已有配置 |
Auto-detected CSV does not support interface comparison |
比对模式下自动识别的 CSV(疑似固定分隔符文本文件,无配置解析)无字段定义与主键,无法接口比对(退出码 -1) | 在 config/ 下补全含字段定义与主键的解析配置后重试,或用 -p 指定已有配置 |
File matches both CSV and fixed-length configs |
CSV 和 FIXED 配置同时命中 | 用 -p 指定正确配置 |
File matches multiple configs |
多个同类型配置均匹配 | 从输出的候选列表中选择 -p 参数 |
Field count mismatch |
配置字段数与文件不符 | 检查文件版本,更换对应配置 |
Row length mismatch |
行长度与配置不符 | 文件可能不是本配置对应的版本 |
Config [xxx] not found |
-p 指定的配置不存在 |
检查配置文件名和配置段名称拼写 |
Quote strategy (-q) is required for CSV export |
单文件 CSV 导出未传 -q |
补充 -q 1/2/3(推荐 -q 2) |
SQL template contains no field placeholders |
SQL 模板无 %N 占位符 |
在模板中加入 %1、%2 等占位符 |
SQL template contains %0 which is invalid |
占位符序号为 %0 或更小(字段编号必须 ≥ 1) |
占位符从 %1 开始,勿使用 %0 |
SQL template references field %N but file only has M |
占位符序号超出字段数 | 检查字段数量,占位符从 %1 开始 |
File already exists |
目标文件已存在 | 加 -o 参数覆盖,或指定不同的 -O 路径 |
File exceeds Excel export limit |
超过 100 万行限制 | 改用 CSV 或 SQL 导出 |
DBF file does not support interface comparison |
DBF 文件比对提示(.dbf 后缀预检查或 -p 指定 DBF 配置路径;stderr 同时输出 Note:DBF 仅支持二进制比对,不支持接口数据比对) |
属正常提示非报错:二进制一致退出码 0,不一致退出码 1(正常完成态,非执行错误) |
ZIP file does not support interface comparison |
ZIP 文件比对预检查提示(stderr 同时输出 Note:ZIP 仅支持二进制比对,不支持接口数据比对) | 属正常提示非报错:二进制一致退出码 0,不一致退出码 1(正常完成态,非执行错误) |
No common primary key, cannot diff |
两文件主键定义不匹配 | 确保两文件使用相同的主键配置 |
Primary key conflict in original/new file |
文件存在主键冲突 | 使用 --force-compare 强制比对 |
Directory path detected but --batch parameter not specified |
传入了目录但未加 --batch |
加 --batch,或改为具体文件路径 |
Directory path must end with / |
批量模式 -O 目录路径未以 / 结尾 |
改为 -O /path/to/dir/ 形式 |
Invalid config format |
-p 格式错误 |
使用 [OFD_21.ini][04_001_FUND] 或 [DBF_上海证券登记结算数据接口.ini] 格式 |
First bracket must start with DBF_, OFD_, CSV_, or FIXED_ |
配置文件名前缀不合法 | 检查配置文件名前缀 |
Commit value must be an integer between 1 and 100000 |
-C 超出取值范围 |
使用 1-100000 之间的整数 |
Delimiter must be between 1 and 10 characters |
分隔符长度不合法 | 使用 1-10 个字符,且不含换行/引号/反斜杠 |
Invalid --batch-diff-summary-rows parameter |
该参数超出范围 | 使用 10-500 之间的整数 |
Invalid --max-size parameter |
大小参数格式错误 | 使用如 100MB、1GB、500KB 或纯字节数 |
Failed to commit temp file: ... |
临时文件落地为目标文件失败(rename 与 copy 兜底重试均失败,多因目标文件被占用、目录权限不足或磁盘空间不足) | 检查输出目录权限与磁盘空间,关闭占用目标文件的程序(杀毒/索引/编辑器等)后重试 |
... failed to remove temp file ... |
临时文件清理失败(残留的 .ffreader_*.tmp 临时文件被占用无法删除) |
属非致命告警,不影响导出/比对结果,可手动删除残留的临时文件 |
排查通用建议:
- 优先让用户不带
-p运行,查看自动匹配结果和失败原因 - 若输出
Matched configs列表,让用户从中选择-p参数 - 若输出
Match failure reasons,逐条分析原因(字段数、行长、版本等)
运行时临时文件提示: 导出与比对写文件时采用「临时文件 → 原子落地」保护机制,临时文件命名为 <目标文件名>.ffreader_<时间戳>_<进程PID>.tmp(.ffreader 前缀标识本程序临时文件,避免与用户文件混淆)。若某次运行异常中断(如进程崩溃)残留了临时文件,下一次导出/比对开始前会自动清理,此时 stderr 输出 Cleaned up N stale temp file(s) in <目录>(仅在有残留时输出,属正常提示、非报错,不影响退出码)。
十四、常用示例汇总
文件转换示例
# 1. 自动识别 OFD 文件,导出 CSV(单文件 CSV 导出必须带 -q)
FFReader -c -f OFD_XXX_XXX_20181016_04.TXT -t csv -q 2
# 2. 导出 Excel,覆盖已有文件,指定路径
FFReader -c -f OFD_XXX_XXX_20181016_04.TXT -t xlsx -o -O output.xlsx
# 3. 指定配置解析(当自动匹配到多个配置时使用)--如果你的文件匹配到了多套配置,你可以选择删除不需要的配置,避免需要指定配置
FFReader -c -f OFD_XXX_XXX_20181016_04.TXT -t csv -q 2 -p "[OFD_21.ini][04_001_FUND]"
# 4. DBF 文件导出 JSON(自动按文件名+字段名匹配翻译配置)
FFReader -c -f TZXX.DBF -t json -O output.json
# 5. OFD 文件导出 JSON 并检查主键冲突
FFReader -c -f OFD_XXX_XXX_20181016_04.TXT -t json --check-primary-key -O output.json
# 6. SQL 导出,空字段输出 NULL,每 500 行 COMMIT
FFReader -c -f NAVREST20181012 -t sql \
-s "INSERT INTO fund_nav(code,date,nav) VALUES('%1','%2',%3);" \
-n -C 500
# 7. 使用管道分隔符导出 CSV(不加引号)
FFReader -c -f A054_XXX_20220429_007_1.txt -t csv -d "|" -q 1
# 8. JSON 导出忽略警告输出
FFReader -c -f OFD_XXX_XX_20111123_03.TXT -t json --ignore-warnings -O output.json
# 9. 转换结果直接输出到 stdout(不生成文件,适合管道给其他工具/AI)
FFReader -c -f OFD_XXX_XXX_20181016_04.TXT -t json --stdout
FFReader -c -f OFD_XXX_XXX_20181016_04.TXT -t csv -q 2 --stdout
文件比对示例
# 1. 比对两个定长格式文件,输出差异 Excel
FFReader -c -t diffxlsx -R /data/oldpath/C103100259-Z700110227-001-20230725-01.txt -N /data/newpath/C103100259-Z700110227-001-20230725-01.txt
# 2. 比对并输出 JSON 格式差异报告
FFReader -c -t diffjson -R /data/oldpath/C103100259-Z700110227-001-20230725-01.txt -N /data/newpath/C103100259-Z700110227-001-20230725-01.txt
# 3. 比对并输出 HTML 可视化差异报告
FFReader -c -t diffhtml -R /data/oldpath/C103100259-Z700110227-001-20230725-01.txt -N /data/newpath/C103100259-Z700110227-001-20230725-01.txt
# 4. 指定配置进行比对(FIXED 配置段含通配符时按实际段名书写)
FFReader -c -t diffjson -R /data/oldpath/C103100259-Z700110227-001-20230725-01.txt -N /data/newpath/C103100259-Z700110227-001-20230725-01.txt -p "[FIXED_WDEP_理财产品中央数据交换协议_V1.0.ini][??????????-??????????-001-20??????-*.txt*|1.0]"
# 5. 比对时忽略某些字段,导出所有行
FFReader -c -t diffjson -R /data/oldpath/C103100259-Z700110227-001-20230725-01.txt -N /data/newpath/C103100259-Z700110227-001-20230725-01.txt \
--ignore-fields "Address,RegionCode" --exp-diff-line 3
# 6. 强制比对(存在主键冲突时继续)
FFReader -c -t diffxlsx -R /data/oldpath/C103100259-Z700110227-001-20230725-01.txt -N /data/newpath/C103100259-Z700110227-001-20230725-01.txt --force-compare
# 7. 比对单边字段,导出差异列+忽略列
FFReader -c -t diffxlsx -R /data/oldpath/C103100259-Z700110227-001-20230725-01.txt -N /data/newpath/C103100259-Z700110227-001-20230725-01.txt \
--diff-single-side --exp-diff-column 2
# 8. 指定输出路径并覆盖
FFReader -c -t diffxlsx -R /data/oldpath/C103100259-Z700110227-001-20230725-01.txt -N /data/newpath/C103100259-Z700110227-001-20230725-01.txt -O result.xlsx -o
# 9. 差异 JSON 直接输出到 stdout
FFReader -c -t diffjson -R /data/oldpath/C103100259-Z700110227-001-20230725-01.txt -N /data/newpath/C103100259-Z700110227-001-20230725-01.txt --stdout
注:比对示例中
/data/oldpath/、/data/newpath/为目录占位符,请替换为实际的原/新文件路径。
批量转换示例
# 1. 目录下全部文件导出 JSON(输出到 exportresult_<时间戳> 目录)
FFReader -c --batch -f /data/datafile/ -t json
# 2. 仅处理 *.txt 文件,排除备份文件
FFReader -c --batch -f /data/datafile/ -t csv -q 2 --filter "*.txt" --filter-exclude "*.bak"
# 3. 指定输出目录(必须以 / 结尾且已存在)并限制文件大小
FFReader -c --batch -f /data/datafile/ -t xlsx --max-size 100MB -O /data/datafile/export/
# 4. 批量 SQL 导出(模板参数同单文件模式)
FFReader -c --batch -f /data/datafile/ -t sql -s "INSERT INTO t VALUES('%1','%2');" -n -C 1000
批量比对示例
# 1. 两个目录批量比对,输出 JSON 汇总+单文件报告(/data/oldpath/ 与 /data/newpath/ 为目录占位符)
FFReader -c --batch -R /data/oldpath/ -N /data/newpath/ -t diffjson
# 2. 仅过滤 C103 开头的文本文件比对
FFReader -c --batch -R /data/oldpath/ -N /data/newpath/ -t diffxlsx --filter "C103*.txt"
# 3. 仅输出汇总报告(跳过单文件差异报告)
FFReader -c --batch -R /data/oldpath/ -N /data/newpath/ -t diffjson --summary-only
# 4. 汇总报告输出到指定文件
FFReader -c --batch -R /data/oldpath/ -N /data/newpath/ -t diffjson --summary-only -O ./result.json
# 5. 汇总报告中每文件差异明细扩展到 200 行
FFReader -c --batch -R /data/oldpath/ -N /data/newpath/ -t diffhtml --batch-diff-summary-rows 200
# 6. 超大文件仅二进制比对(跳过接口比对)
FFReader -c --batch -R /data/oldpath/ -N /data/newpath/ -t diffjson --max-size 1GB
# 7. 主键冲突时仍强制接口比对
FFReader -c --batch -R /data/oldpath/ -N /data/newpath/ -t diffjson --force-compare
# 8. JSON 输出到 stdout(逐文件差异 JSON + 汇总 JSON 顺序拼接,多个 JSON 文档连排)
FFReader -c --batch -R /data/oldpath/ -N /data/newpath/ -t diffjson --stdout
# 9. 强制比对(存在主键冲突时继续)&& 仅仅输出差异摘要报告 && 忽略指定字段 && 限制数据接口比对大小 --这是一个生产验证过的方案,比如开放式基金文件比对仅仅生成摘要供AI分析,且大于1GB的文件仅仅进行二进制比较,防止内存占用超过预期
FFReader -c --batch -R /data/oldpath/ -N /data/newpath/ -t diffjson --force-compare --batch-diff-summary-rows 200 --max-size 1GB --summary-only -O ./result.json
十五、注意事项
- Windows 路径中若含空格,务必用引号包裹:
-f "D:\my data\OFD_XXX_XXX_20181016_04.TXT" - Windows 下请使用独立的 FFReaderCli 程序执行命令行操作:原生控制台程序,数据走 stdout、状态信息走 stderr,管道与重定向直接可用;GUI 程序(FFReader-*.exe)传入
-c等命令行参数时仅弹窗提示改用 FFReaderCli - SQL 模板中的分号(
;)是 SQL 语句结束符,需保留在模板末尾 -n(null-empty)仅影响 SQL 导出,CSV/JSON/Excel 导出中空字段始终输出空字符串- 单文件 CSV 导出必须显式提供
-q(引号策略);批量导出中-q可省略(默认 RFC4180) - 批量模式不递归处理子目录;批量模式
-O目录路径必须以/结尾且目录已存在 - OFD Index 文件支持 JSON 导出(专用元信息结构)与比对模式(虚拟主键 filename);SQL 导出未针对其单列结构适配,不建议使用
- DBF 文件仅支持二进制比对(二进制一致退出码 0,不一致退出码 1,与批量"仅二进制不一致"语义一致),不支持接口数据比对(行为与 ZIP 一致)
- DBF 文件 JSON 导出的
configSegment恒为空字符串,且所有字段type恒为S(值均为字符串) - DBF 文件 JSON 导出的
rowNum和lineNum值相同(无文件头) - 输出文件编码统一为 UTF-8(含 BOM 头取决于平台),建议用支持 UTF-8 的工具打开
- 比对模式要求两文件主键定义一致,否则无法比对
--stdout与-o/-O互斥(启用 stdout 时输出路径参数被忽略)- zip 或其他格式的压缩文件,需先使用外部工具解压缩后再进行解析和比对
- Excel 导出有 100 万行限制,超大文件请用 CSV 或 SQL;比对模式的 xlsx 差异报告同样受此限制,HTML 差异报告单文件上限 5000 差异行
- SQL 大批量导入时推荐
-C 1000(每千行 COMMIT 一次),避免事务过大 - 流式写出机制确保内存占用恒定,即使文件有数百万行也能正常处理(csv/json/sql 导出)
- xlsx 导出(数据全量载入内存)与比对模式(新旧文件均载入内存)不受流式机制保护,大文件场景可用
--max-size限制文件大小以控制内存占用:批量导出超限文件跳过不导出;批量比对超限文件仅二进制比对(跳过接口比对),汇总报告标记sizeExceeded=true,单文件明细报告仅diffjson生成
十六、在 AI 工具中使用(Skill 安装)
FFReader 随包提供一个技能文件 SKILL.md(位于程序包根目录)。将该文件连同可执行程序放入 AI 工具的技能目录后,即可在 Claude Code、WorkBuddy 等 AI 编程工具中以自然语言直接调用 FFReader,完成文件转换、批量处理与文件比对(diff),无需手动记忆命令行参数。
需要复制的文件
| 文件 | 说明 |
|---|---|
SKILL.md |
技能说明文件(程序包根目录),AI 据此理解如何调用 FFReader |
| 可执行程序 | 按平台选择,见 二、运行环境与可执行程序 |
config/ 配置目录 |
仅 Windows 需与可执行程序同级;Linux 首次运行自动解压到 ~/.ffreader/config/,macOS 集成在 .app 内 |
推荐目录结构
技能运行时按「技能目录 → 用户指定目录 → 环境变量配置路径」的顺序查找可执行程序。最简单可靠的做法,是把可执行程序(以及 Windows 下的 config/)与 SKILL.md 放在同一技能目录下:
<技能根目录>/ffreader/
├── SKILL.md # 技能说明文件
├── FFReaderCli-x64.exe # 可执行程序(此处以 Windows 为例)
└── config/ # Windows 必需;Linux / macOS 可省略
Claude Code
技能目录(二选一):
| 作用域 | 路径 | 说明 |
|---|---|---|
| 用户级(推荐) | ~/.claude/skills/ffreader/ |
全局生效,所有项目可用 |
| 项目级 | <项目根目录>/.claude/skills/ffreader/ |
仅当前项目可用 |
安装步骤(以 macOS / Linux 用户级为例):
mkdir -p ~/.claude/skills/ffreader
cp SKILL.md ~/.claude/skills/ffreader/
# 复制可执行程序到技能目录(按平台替换文件名)
# Linux: FFReader-x86_64.AppImage
# macOS: FFReader.app(或 FFReader.app/Contents/MacOS/FFReader)
# Windows: FFReaderCli-x64.exe(并把 config/ 目录复制到同级)
重启 Claude Code 后,输入 /ffreader 显式触发,或直接用自然语言描述(如「把 OFD_XXX.txt 转成 csv」)自动匹配该技能。
WorkBuddy
技能目录(用户级):
| 平台 | 路径 |
|---|---|
| macOS / Linux | ~/.workbuddy/skills/ffreader/ |
| Windows | C:\Users\<用户名>\.workbuddy\skills\ffreader |
注:WorkBuddy 不同版本技能目录路径可能略有差异,以界面中「技能管理」显示的本地技能目录为准;项目级也可放入
.codebuddy/skills/。
安装步骤与 Claude Code 相同:创建 ffreader 目录 → 放入 SKILL.md → 放入对应平台可执行程序(Windows 同时放 config/)。重启 WorkBuddy 后,输入 @ffreader 显式触发,或自然语言描述自动匹配。
常见问题
- 技能装了不生效? 确认
SKILL.md直接位于ffreader/目录下(不要多套一层),目录名与 frontmatter 的name(ffreader)一致;重启 AI 工具后重试。 - Windows 找不到可执行程序? 确认
FFReaderCli-*.exe与config/在同级目录。 - 可执行程序放技能目录还是放 PATH? 放技能目录最省心(免配置)。若 FFReader 已单独安装,也可在对话中直接告知 AI 程序所在路径,AI 会按「用户指定目录」查找。