
yq CSV/TSV 处理完全指南编码、解码与往返转换实战【免费下载链接】yqyq is a portable command-line YAML, JSON, XML, CSV, TOML, HCL and properties processor项目地址: https://gitcode.com/GitHub_Trending/yq/yqyq 除了处理 YAML 和 JSON还支持将 CSV 与 TSV制表符分隔文件作为输入格式解码为对象数组、将 YAML 数据编码为 CSV/TSV 输出并且可以在 CSV 上执行 yq 表达式后再写回 CSV实现完整的“解码-变换-编码”往返流程。读完本文你将掌握-ocsv/-otsv、-pcsv/-ptsv、--csv-auto-parse、--csv-separator等参数的用法与边界行为并理解其背后的 Go 源码实现编码器 encoder_csv.go 与解码器 decoder_csv_object.go。一、CSV/TSV 支持概览与相关参数yq 中 CSV 与 TSV 共用同一套编解码实现区别仅在于分隔符。核心配置由CsvPreferences结构体承载定义在 csv.gotype CsvPreferences struct { Separator rune AutoParse bool } func NewDefaultCsvPreferences() CsvPreferences { return CsvPreferences{Separator: ,, AutoParse: true} } func NewDefaultTsvPreferences() CsvPreferences { return CsvPreferences{Separator: \t, AutoParse: true} }即 CSV 默认分隔符为,、TSV 默认分隔符为制表符两者的AutoParse是否自动解析单元格中的 YAML/JSON 内容默认为true。在命令行上相关的参数有见 cmd/root.go 及 README 帮助输出参数说明默认值-p, --parse-format输入格式取csv或tsvauto-o, --output-format输出格式取csv或tsvauto--csv-auto-parse解码 CSV 时是否把形如 YAML/JSON 的单元格值解析为对象/数组true--tsv-auto-parse同上作用于 TSV 输入true--csv-separator自定义 CSV 分隔字符rune 类型参数,csv与tsv编解码器在 format.go 中注册到格式工厂因此在-p/-o中即可直接使用csv、tsv关键字。二、Encode从 YAML 编码为 CSV/TSV从源码 encoder_csv.go 的Encode方法可以看出CSV 编码器只接受三种顶层结构其余会报错标量直接原样输出加换行扁平对象数组数组的第一个元素是映射节点以第一个对象的键作为表头标量数组的数组第一个元素是序列节点每行一个子数组。编码器会先检查node.Content[0]的类型来分派处理空序列len(node.Content) 0不产生任何输出。支持的数据形状形状一同构的扁平对象数组——不支持嵌套且假定第一个对象拥有所有必需的键其余对象缺键时该列留空- name: Bobo type: dog - name: Fifi type: cat形状二标量字符串/数字/布尔数组的数组- [Bobo, dog] - [Fifi, cat]如果输入不符合上述形状例如对象中出现嵌套对象encodeRow/encodeArrays会返回形如csv encoding only works for arrays of scalars (string/numbers/booleans)的错误见 encoder_csv.go#L31-L56。编码简单 CSV给定sample.yml- [i, like, csv] - [because, excel, is, cool]执行yq -ocsv sample.yml输出i,like,csv because,excel,is,cool编码简单 TSV同样的输入把输出格式换成tsvyq -otsv sample.yml输出i like csv because excel is cool对象数组编码为 CSV给定sample.yml- name: Gary numberOfCats: 1 likesApples: true height: 168.8 - name: Samanthas Rabbit numberOfCats: 2 likesApples: false height: -188.8执行yq -ocsv sample.yml输出name,numberOfCats,likesApples,height Gary,1,true,168.8 Samanthas Rabbit,2,false,-188.8表头来自第一个对象的键顺序extractHeader只对content[0]调用getMapKeys取出键见 encoder_csv.go#L58-L64。自定义 CSV 表头与列如果想控制列的选取与表头文案可以手动构造一个“表头数组 各行值数组”的数组合并表达式先把自定义表头作为第一行数组再把每个对象投影成值数组yq -ocsv [[Name, Number of Cats]] [.[] | [.name, .numberOfCats ]] sample.yml输出Name,Number of Cats Gary,1 Samanthas Rabbit,2这是典型的 yq 表达式技巧连接两个序列得到“数组的数组”编码器便按行原样写出。字段缺失时的行为表头由第一条记录决定。下面的示例中第一条记录缺少likesApples因此该列根本不出现在 CSV 中第二条记录缺少numberOfCats对应单元格为空- name: Gary numberOfCats: 1 height: 168.8 - name: Samanthas Rabbit height: -188.8 likesApples: false执行yq -ocsv sample.yml输出name,numberOfCats,height Gary,1,168.8 Samanthas Rabbit,,-188.8对应源码createChildRow对每个表头键用findKeyInMap查找找不到则写入空标量节点见 encoder_csv.go#L66-L77。特殊字符的转义底层使用 Go 标准库encoding/csv的 Writercsv.NewWriter并设置Comma e.separator因此含分隔符、引号或换行的值会被标准库自动加引号。测试用例csv_test.go 中的 “Comma in value” 场景验证了这一点输入[comma, in, value, things]编码结果为comma, in, value,things。三、Decode把 CSV/TSV 解析为对象数组解码器假定第一行是表头行其后每行数据与表头一一映射为对象键值最终形成一个对象数组。解码器入口为 decoder_csv_object.goInit阶段先用utfbom.Skip剥离开头的 UTF-8 BOM再创建csv.Reader并应用配置的Separator见 decoder_csv_object.go#L23-L30Decode读取第一行作为headerRow随后逐行读取内容行并调用createObject构造映射节点直到 EOF。解析 CSV 为对象数组默认自动解析默认情况下形如 YAML/JSON 的单元格内容会被解析“auto-parsing”。给定sample.csvname,numberOfCats,likesApples,height,facts Gary,1,true,168.8,cool: true Samanthas Rabbit,2,false,-188.8,tall: indeed执行yq -pcsv sample.csv输出注意facts列被解析成了嵌套对象- name: Gary numberOfCats: 1 likesApples: true height: 168.8 facts: cool: true - name: Samanthas Rabbit numberOfCats: 2 likesApples: false height: -188.8 facts: tall: indeed关闭自动解析如果单元格内容是数据而非 YAML例如值以#开头会被 YAML 视为注释或值本身含:可以用--csv-auto-parsef让这类内容保持为纯字符串yq -pcsv --csv-auto-parsef sample.csv输出中facts保留为字符串- name: Gary numberOfCats: 1 likesApples: true height: 168.8 facts: cool: true - name: Samanthas Rabbit numberOfCats: 2 likesApples: false height: -188.8 facts: tall: indeed自动解析的判定逻辑在convertToNode见 decoder_csv_object.go#L32-L40先对单元格内容调用parseSnippet当AutoParse为 false且解析结果不是“与原文完全相同的标量”时就回退为原始字符串。注意即使关闭自动解析数字、布尔等标量仍会被正常解析上例中1、false依然是数值/布尔只有会被解析成对象或数组的内容才会被压平为字符串。TSV 侧也有对应的--tsv-auto-parse参数cmd/root.go。解析 TSV 为对象数组给定sample.tsvname numberOfCats likesApples height Gary 1 true 168.8 Samanthas Rabbit 2 false -188.8执行yq -ptsv sample.tsv输出- name: Gary numberOfCats: 1 likesApples: true height: 168.8 - name: Samanthas Rabbit numberOfCats: 2 likesApples: false height: -188.8解码的边界行为测试用例 csv_test.go 中还验证了几个值得注意的边界场景引号内的换行输入heading1与some data\nwith a line break会解码为带字面块标量的 YAMLheading1: |-形式说明多行单元格能完整保留以#开头的值开启自动解析时#ffff会被当成 YAML 注释而“消失”输出为- value: #ffff即空值关闭自动解析后则正确保留为- value: #ffff。这正是需要--csv-auto-parsef的典型场景。四、Round Trip在 CSV 上执行表达式并写回把-p与-o组合起来就可以直接对 CSV 做查询和原地式修改。给定sample.csvname,numberOfCats,likesApples,height Gary,1,true,168.8 Samanthas Rabbit,2,false,-188.8执行yq -pcsv -ocsv (.[] | select(.name Gary) | .numberOfCats) 3 sample.csv输出name,numberOfCats,likesApples,height Gary,3,true,168.8 Samanthas Rabbit,2,false,-188.8流程即CSV 解码为对象数组 → 表达式将 Gary 的numberOfCats更新为 3 → 编码器以第一行对象键为表头重新写出。测试 csv_test.go 中的 “Round trip” 场景对该行为做了断言eaevaluate all子命令同样支持该流程见验收测试 acceptance_tests/inputs-format.sh。五、实现要点小结从源码结构看CSV/TSV 支持的关键实现链路为格式配置csv.go 中的CsvPreferences{Separator, AutoParse}及 CSV/TSV 两套默认值编码encoder_csv.go 中的csvEncoder依据首元素类型分派标量行/对象数组/数组数组三种路径表头取自第一个对象缺键补空底层使用标准库encoding/csvComma字段由Separator驱动解码decoder_csv_object.go 中的csvObjectDecoder跳过 UTF-8 BOM首行作为表头逐行映射为对象并依据AutoParse决定是否将单元格内容解析为 YAML 片段CLI 接线cmd/root.go 将--csv-auto-parse、--csv-separator、--tsv-auto-parse绑定到全局配置ConfiguredCsvPreferences/ConfiguredTsvPreferences。仓库根目录的 utf8.csv 可作为包含多字节字符的 CSV 测试样例参考相关测试数据与场景全部集中在 pkg/yqlib/csv_test.go其非skipDoc场景同时驱动了本文所用的文档示例生成documentCSVScenario。参考文件文档原文pkg/yqlib/doc/usage/csv-tsv.md编码器pkg/yqlib/encoder_csv.go解码器pkg/yqlib/decoder_csv_object.go偏好配置pkg/yqlib/csv.go命令行参数cmd/root.go测试与文档生成pkg/yqlib/csv_test.go验收测试acceptance_tests/inputs-format.sh【免费下载链接】yqyq is a portable command-line YAML, JSON, XML, CSV, TOML, HCL and properties processor项目地址: https://gitcode.com/GitHub_Trending/yq/yq创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考