什么是CSV转YAML转换?
CSV转YAML转换是将逗号分隔的表格数据转化为YAML(YAML Ain't Markup Language)格式的过程。CSV文件的每一行都会成为YAML序列中的一个条目,每个列标题则映射为YAML映射中的一个键。输出结果是一个以缩进表示层级的可读性文档,配置工具和数据流水线无需额外的Schema文件即可直接解析。
YAML于2001年首次提出,并于2009年发布了当前规范(YAML 1.2)。它是Kubernetes清单、Docker Compose文件、Ansible Playbook、GitHub Actions工作流以及众多CI/CD系统的默认配置格式。当源数据存储在电子表格或数据库导出文件中时,将CSV转换为YAML是为这些系统生成配置条目、数据初始化记录或测试输入的最快方式。
一个正确的CSV转YAML转换器必须处理RFC 4180解析规则:包含逗号或换行符的引用字段、转义的双引号以及不同的分隔符。在YAML一侧,看起来像布尔值(yes、no、true、false)、数字或null的字符串必须加上引号,以防止YAML解析器将其强制转换为非预期类型。转换器还应处理Unicode内容并生成有效的UTF-8输出,因为YAML 1.2要求默认使用UTF-8编码。
为什么使用CSV转YAML工具?
手动从电子表格数据编写YAML既繁琐又容易出错。一个错位的缩进或一个未加引号的特殊字符就会产生无效的YAML,导致配置部署或数据导入失败。此转换器解析CSV字段,将标题映射为键,并生成缩进正确、引号处理恰当的YAML输出。
CSV转YAML使用场景
CSV转YAML映射参考
CSV文件中的每个结构元素在YAML中都有直接对应的概念。
| CSV概念 | YAML等价物 | 说明 |
|---|---|---|
| CSV file | YAML document | The entire file becomes a YAML sequence (array) of mappings |
| Header row | Mapping keys | Each column header becomes a key in every mapping entry |
| Data row | Sequence item (- ...) | Each row becomes one mapping item in the top-level array |
| Cell value | Scalar value | Strings, numbers, and booleans are inferred by YAML parsers |
| Empty cell | null or empty string | Rendered as null, ~, or an empty value depending on the tool |
| Comma delimiter | Indentation + colon | CSV delimiters are replaced by YAML key: value structure |
CSV与YAML对比
CSV是一种平面的分隔符分隔格式,不包含类型信息或层级结构。YAML是JSON的超集,使用缩进表示嵌套,支持多种数据类型,并允许注释。具体选择取决于下游工具的要求。
代码示例
以下是Node.js、Python、Go和CLI工具的实用示例。每个示例均读取CSV表头行作为YAML键,将每条数据行映射为序列条目,并对类型不明确的值进行引号处理。
import { parse } from 'csv-parse/sync'
import { stringify } from 'yaml'
const csv = `name,age,city
Alice,30,Berlin
Bob,25,"New York"`
const records = parse(csv, { columns: true, skip_empty_lines: true })
console.log(stringify(records))
// → - name: Alice
// → age: "30"
// → city: Berlin
// → - name: Bob
// → age: "25"
// → city: New York
// Vanilla JS (no dependencies)
function csvToYaml(csv) {
const [headerLine, ...dataLines] = csv.trim().split('\n')
const headers = headerLine.split(',')
return dataLines.map(line => {
const values = line.split(',')
return headers.map((h, i) => ` ${h}: ${values[i] || ''}`).join('\n')
}).map(block => `- ${block.trimStart()}`).join('\n')
}import csv, io, yaml
csv_string = """name,age,city
Alice,30,Berlin
Bob,25,New York"""
reader = csv.DictReader(io.StringIO(csv_string))
data = list(reader)
# default_flow_style=False produces block-style YAML
print(yaml.dump(data, default_flow_style=False, sort_keys=False))
# → - age: '30'
# → city: Berlin
# → name: Alice
# → - age: '25'
# → city: New York
# → name: Bob
# Preserve insertion order with sort_keys=False (Python 3.7+)
# To type-cast numbers: data = [{k: int(v) if v.isdigit() else v ...}]package main
import (
"encoding/csv"
"fmt"
"gopkg.in/yaml.v3"
"strings"
)
func main() {
input := "name,age,city
Alice,30,Berlin
Bob,25,New York"
r := csv.NewReader(strings.NewReader(input))
records, _ := r.ReadAll()
headers := records[0]
var data []map[string]string
for _, row := range records[1:] {
entry := make(map[string]string)
for i, h := range headers {
entry[h] = row[i]
}
data = append(data, entry)
}
out, _ := yaml.Marshal(data)
fmt.Println(string(out))
// → - age: "30"
// → city: Berlin
// → name: Alice
// → - age: "25"
// → city: New York
// → name: Bob
}# Using yq (https://github.com/mikefarah/yq) # yq reads CSV with --input-format=csv yq --input-format=csv --output-format=yaml '.' data.csv # Using Miller (mlr) — converts between CSV, JSON, YAML, and more mlr --icsv --oyaml cat data.csv # Python one-liner for quick conversion python3 -c " import csv, sys, yaml data = list(csv.DictReader(sys.stdin)) print(yaml.dump(data, default_flow_style=False, sort_keys=False)) " < data.csv