CsvPath 是一种容易的语言还是困难的语言?
Source: Dev.to
CsvPath 与 CsvPath 验证语言 (CVL)
CsvPath 框架包含 CsvPath 验证语言,简称 CVL。
CVL 是一种面向文档的、表格数据验证语言,类似于 XSD、Schematron、DDL 和 JSONQuery。它可以验证 CSV、Excel、JSONL 文件以及内存中的数据帧(例如 Pandas、Polars)。在 CsvPath 框架中,CVL 为更大的数据预入职架构提供验证和升级能力。
按行验证
CVL 是 基于行 的:它在流式读取时逐行验证数据,而不是一次性将整个数据集加载到内存中。
其他语言(XSD、DDL 等)一次性在完整数据集上操作,以内存使用换取速度。虽然 DDL 有处理大于内存的数据的策略,但 CVL 的流式方式能够实现全量语言难以完成的任务,反之亦然。运行时环境可以缓解其中的许多限制。
声明式特性
所有提到的语言——包括 CVL——都是 声明式 的。你描述 应该发生什么 或 你在测试什么,运行时负责决定 如何 达成结果。
基于模式和基于规则
- 模式定义 在 CVL 中类似于 SQL 的 DDL。
- 规则定义 类似于 Schematron 和 SQL 查询。
CVL 在语法上刻意保持简洁,但仍提供 数百个函数 用于业务逻辑验证。为特定任务挑选合适的函数往往是最大的学习曲线。
为什么需要表格数据验证语言?
- CSV 及类似格式无处不在且极其灵活,这会降低生产力并增加风险。
- 数据常常来自外部合作伙伴,提前发现问题至关重要。
- 错误会在数据向企业内部流动的过程中传播并放大,提升修复成本。
尽可能靠近数据源进行验证可以最小化影响范围,减少后续的抢救工作。
学习曲线:容易还是困难?
学习 CVL 所需的努力取决于你的目标:
| 用例 | CVL 适用性 |
|---|---|
| 对单个数据帧进行偶尔、临时的数据类型检查 | 往往显得大材小用;快速的 Python 脚本可能更快。 |
| 涉及众多合作伙伴和文档类型的大规模数据收集 | CVL(以及 CsvPath 框架)大放异彩,能减少自定义代码、技术债务和知识流失。 |
CVL 旨在 自动化,而非一次性脚本。把 CVL 脚本当作代码来对待:进行版本管理、编写测试、保持简洁可复用。正确使用时,CVL 易学、易用,但和任何强大的语言一样,也可能被误用。
示例场景
一家零售数据合作伙伴每周发送订单 CSV 文件。你需要在文件进入企业系统前进行验证。
示例数据
ID,date,time,store,address,city,state,zip,category,type,shelf,vendor,product name,UPC,SKU,unit,quantity,a price
03358993,03/21/2024,10:24:14,Bob's store,1 Lakeshore Drive, Chicago, IL, 33581,OFFICE,PAPER,1-5,Sams Paper,20lbs Ream,0301024855,,per each,8,20.99
03358994,03/21/2024,10:31:28,Fred's store,1 Lakeshore Drive, Chicago, IL, 33581,OPERA,WRITING,1-5,Biz Pen,10-pack Black,0541931855,0432950078,per each,2,4
03358995,03/21/2024,11:26:18,Mary's story, 1 Lakeshore Drive, Chicago, IL, 33581,FOOD,CANDY,7,Starbursts,Single,3583900656,0899920453,per each,1,1.29d
验证目标
- 检测 错误的价格(例如缺少小数位)。
- 标记 错误的类别。
- 识别 缺失的 SKU。
- 识别 缺失的 UPC。
CVL 脚本 (CsvPath)
validation-mode:no-raise, no-stop, print, no-fail, collect
logic-mode:OR
${FILE}[1*][
# --- Category check ---
@in = in(#category, "OFFICE|COMPUTING|FURNITURE|PRINT|FOOD|OTHER")
not(@in.asbool) ->
error.category("Bad category $.headers.category at line $.csvpath.count_lines", fail())
# --- Price format check ---
@price_format = exact(end(), /\$?\d*\.\d{2}/)
not(@price_format.asbool) ->
error.price("Bad price $.headers.'a price' at line $.csvpath.count_lines", fail())
# --- SKU presence check ---
not(#SKU) ->
error.sku("No SKU at line $.csvpath.count_lines", fail())
# --- UPC presence check ---
not(#UPC) ->
error.upc("No UPC at line $.csvpath.count_lines", fail())
]
在生产环境中,你可能会将这些规则拆分为多个 CsvPath 文件,并作为命名路径组运行。上面的示例为了说明直接在脚本中嵌入了文件路径(
${FILE})。
关键要点
- 逻辑模式(
OR与AND)决定多条规则的组合方式。 - validation‑mode 控制错误处理方式(
no-raise、no-stop、print等)。 - 当需要函数的布尔结果时使用
asbool。 - 赋值(
@var = …)本身 不决定 匹配成功与否;只有后续的谓词才起作用。 - 含空格或特殊字符的列名需要使用引号(
'a price')。 fail()表示当前行应被视为无效。- 为错误添加描述性名称(
error.category、error.price等)有助于下游处理。
结论
一旦理解了 CVL 的声明式、逐行特性并熟悉其核心函数,使用它会感觉 很容易。学习曲线主要在于记住语法细节以及编写可维护验证脚本的最佳实践。对于大规模、可重复的数据预入职流水线,CVL 提供了简洁、易维护的解决方案,其可扩展性远胜于临时脚本。
欲了解更多细节和高级示例,请访问官方文档。