yq 实战指南:CSV/TSV 与 YAML 互转——编码、解码与原地更新(Round Trip)

yq 实战指南:CSV/TSV 与 YAML 互转——编码、解码与原地更新(Round Trip) yq 实战指南CSV/TSV 与 YAML 互转——编码、解码与原地更新Round Trip【免费下载链接】yqyq is a portable command-line YAML, JSON, XML, CSV, TOML, HCL and properties processor项目地址: https://gitcode.com/GitHub_Trending/yq/yq本文基于 yq 官方文档 csv-tsv.md 整理并深度扩充聚焦 yq 的 CSV/TSV 处理能力如何把 YAML 编码为 CSV/TSV、如何把 CSV/TSV 解析为对象数组、如何控制字段的自动解析行为以及如何完成“读入 CSV → 修改 → 输出 CSV”的原地更新闭环。读完本文你将掌握yq -ocsv、yq -pcsv、--csv-auto-parse等关键用法并理解其底层编码/解码实现与限制边界。一、核心能力概览yq 是一个可移植的命令行 YAML/JSON/XML/CSV/TOML/HCL/properties 处理器CSV/TSV 是其支持的一组“对等格式”既可作为输出格式-ocsv/-otsv也可作为输入格式-pcsv/-ptsv。在格式注册表中CSV 与 TSV 共用同一套编码器/解码器仅分隔符不同这一点可以从源码中直接确认在 format.go 中var CSVFormat Format{csv, []string{c}, func() Encoder { return NewCsvEncoder(ConfiguredCsvPreferences) }, func() Decoder { return NewCSVObjectDecoder(ConfiguredCsvPreferences) }, } var TSVFormat Format{tsv, []string{t}, func() Encoder { return NewCsvEncoder(ConfiguredTsvPreferences) }, func() Decoder { return NewCSVObjectDecoder(ConfiguredTsvPreferences) }, }也就是说csv/tsv各有短别名c/t例如yq -oc二者共享同一个csvEncoder差异只体现在分隔符偏好上。两个偏好结构体定义在 csv.go 中是理解后文所有行为的关键type CsvPreferences struct { Separator rune AutoParse bool } func NewDefaultCsvPreferences() CsvPreferences { return CsvPreferences{Separator: ,, AutoParse: true} } func NewDefaultTsvPreferences() CsvPreferences { return CsvPreferences{Separator: \t, AutoParse: true} }SeparatorCSV 为逗号,TSV 为制表符\tAutoParse解码时是否自动把单元格内容按 YAML/JSON 解析默认均为true。二、编码EncodeYAML → CSV/TSV2.1 支持的数据形态CSV 编码器只接受两类输入结构同质扁平对象数组——无嵌套且假定第一个对象包含全部所需键作为表头来源- name: Bobo type: dog - name: Fifi type: cat标量数组的数组字符串/数字/布尔- [Bobo, dog] - [Fifi, cat]这一约束由 encoder_csv.go 中的错误分支直接体现例如编码标量行时if child.Kind ! ScalarNode { return fmt.Errorf(csv encoding only works for arrays of scalars (string/numbers/booleans), child[%v] is a %v, i, child.Tag) }对象分支则会拒绝非 MappingNodecsv object encoding only works for arrays of flat objects (string key string/numbers/boolean value)。此外csvEncoder.CanHandleAliases()返回false说明 CSV 输出不支持 YAML 锚点/别名。2.2 标量数组编码为 CSV给定 sample.yml- [i, like, csv] - [because, excel, is, cool]执行yq -ocsv sample.yml输出i,like,csv because,excel,is,cool2.3 标量数组编码为 TSV同样的输入执行yq -otsv sample.yml输出字段以制表符分隔i like csv because excel is cool2.4 对象数组编码为 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表头来自第一个对象encoder_csv.go 的encodeObjects先调用extractHeader(content[0])从首个对象提取键集合作为表头行再逐行生成数据。单元格内的逗号、换行等会由 Go 标准库encoding/csv自动加引号转义测试用例 csv_test.go 中 Comma in value 场景即验证了这一点输入[comma, in, value, things]输出comma, in, value,things。2.5 自定义 CSV 格式挑选列与自定义表头手动追加表头行再把每个对象转换为值数组——最终得到“数组的数组”列的取舍与表头命名完全由表达式决定给定同样的 sample.yml执行yq -ocsv [[Name, Number of Cats]] [.[] | [.name, .numberOfCats ]] sample.yml输出Name,Number of Cats Gary,1 Samanthas Rabbit,2该技巧的本质用把自定义表头数组与map出的值数组合并成一个标量数组的数组交给 CSV 编码器逐行写出。2.6 字段缺失行为第一个条目决定表头后续条目缺少的键输出为空单元格给定 sample.yml第一个对象缺likesApples第二个对象缺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源码印证encoder_csv.go 的createChildRow中findKeyInMap找不到键时返回空标量节点createScalarNode(nil, )因此输出空单元格而不是报错——这要求用户自行保证“首个对象字段最全”否则会静默丢列。三、解码DecodeCSV/TSV → YAML解码器假定第一行为表头行其下所有行按表头键组装成对象整体构成对象数组。该逻辑在 decoder_csv_object.go 中func (dec *csvObjectDecoder) Decode() (*CandidateNode, error) { headerRow, err : dec.reader.Read() // ... rootArray : CandidateNode{Kind: SequenceNode, Tag: !!seq} contentRow, err : dec.reader.Read() for err nil len(contentRow) 0 { rootArray.AddChild(dec.createObject(headerRow, contentRow)) contentRow, err dec.reader.Read() } // ... }另有两个值得注意的实现细节BOM 处理Init使用utfbom.Skip跳过文件头 BOM因此带 BOM 的 CSV 也能正确解析decoder_csv_object.go引号内换行依赖encoding/csv的多行字段解析测试 Decode CSV line breaks 验证了some data\nwith a line break会被解析为保留换行的字符串值。3.1 解析 CSV 为对象数组默认自动解析给定 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输出- 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/JSON 格式如1、true、cool: true、[1, 2]会被自动解析为对应类型——数字变数字、布尔变布尔、映射文本变成嵌套对象。3.2 关闭自动解析--csv-auto-parsef给定同样的 sample.csv执行yq -pcsv --csv-auto-parsef sample.csv输出- 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此时facts列保留为原始字符串。该开关对应 cmd/root.go 中的命令行标志rootCmd.PersistentFlags().BoolVar(yqlib.ConfiguredCsvPreferences.AutoParse, csv-auto-parse, yqlib.ConfiguredCsvPreferences.AutoParse, parse CSV YAML/JSON values)其底层判断在 decoder_csv_object.gofunc (dec *csvObjectDecoder) convertToNode(content string) *CandidateNode { node, err : parseSnippet(content) // 若不启用自动解析则不放入解析后的对象/数组 // 但标量仍会解析 if err ! nil || (!dec.prefs.AutoParse (node.Kind ! ScalarNode || node.Value ! content)) { return createScalarNode(content, content) } return node }注意边界关闭自动解析后1、true这类标量依然会被解析为 YAML 标量类型只有“解析结果不再是与原内容一致的标量”即映射/数组等结构时才会整体退化为字符串。3.3 解析 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.8TSV 解码与 CSV 完全同构只是Separator为制表符仓库示例文件 examples/sample_objects.csv 展示了带缺省单元格的真实 CSV 输入形态。四、Round TripCSV 读取、修改并写回 CSV这是 CSV 支持最有实用价值的场景——无需手工拼接 shell 管道即可完成“读 CSV → 用 yq 表达式更新 → 输出 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表达式语义遍历数组选中name Gary的对象将其numberOfCats更新为3。解码器把 CSV 还原为对象数组后走标准 yq 求值编码器再按原表头写回——表头行保持不变。同样的更新还可以直接配合--in-place修改文件yq -i -pcsv -ocsv ... sample.csv。五、测试与文档生成机制CSV/TSV 的功能测试与本文示例共用同一份场景定义全部集中在 csv_test.gocsvScenarios场景列表既驱动TestCSVScenarios断言执行结果又通过documentCSVScenario生成csv-tsv.md文档章节带skipDoc: true的场景只参与测试、不出现在文档中。这意味着文档中的每个命令示例都有对应测试断言背书包括文档未展示的边界场景空数组[]编码输出为空值内含逗号的自动加引号解码后使用key/parent等通用运算符如.[0].name | key值以#开头时默认按 YAML 注释处理--csv-auto-parsef则保留为字符串#ffff。运行验证go test ./pkg/yqlib/ -run TestCSVScenarios测试同时会重新生成文档若输出不一致说明示例过期。六、限制与注意事项综合文档与源码使用 CSV/TSV 格式时需注意以下边界仅支持扁平结构编码要求“数组 of 标量”或“数组 of 扁平对象”嵌套对象/序列会触发明确错误见 encoder_csv.go 的encodeRow/encodeObjects错误分支解码产物必然是“扁平对象数组”CSV 本身也无法表达嵌套Round trip 后嵌套结构会丢失。表头取自第一个对象若第一个对象字段不全缺失列会静默丢弃不报错建议在导出前确保首个条目字段完整或使用 2.5 节的自定义表达式显式控制列。自动解析是双刃剑默认AutoParsetrue会让1、true、[a, b]等单元格被解析为结构化值当这些内容应当作为纯文本如十六进制色值#ffff、含 YAML 冒号的描述时务必加--csv-auto-parsef。短别名与格式自动检测-oc/-ot、-pc/-pt均可用未指定-p时 yq 按文件扩展名自动探测格式.csv→ csv.tsv→ tsv无法识别时回退为 yaml见 format.go 的FormatStringFromFilename。构建选项CSV 支持受 build tagyq_nocsv控制见 no_csv.go 与各文件的//go:build !yq_nocsv约束精简编译时可移除该格式。七、参考文件内容路径原始文档pkg/yqlib/doc/usage/csv-tsv.md偏好定义分隔符/自动解析pkg/yqlib/csv.goCSV/TSV 编码器pkg/yqlib/encoder_csv.goCSV/TSV 解码器pkg/yqlib/decoder_csv_object.go格式注册与别名pkg/yqlib/format.go--csv-auto-parse命令行标志cmd/root.go场景测试与文档生成pkg/yqlib/csv_test.go示例 CSV 文件examples/sample_objects.csv【免费下载链接】yqyq is a portable command-line YAML, JSON, XML, CSV, TOML, HCL and properties processor项目地址: https://gitcode.com/GitHub_Trending/yq/yq创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考