## 介绍 ### 什么是YAML文件? YAML(YAML Ain't Markup Language)是一种**人类可读的数据序列化格式**,专注于简洁表达结构化数据。它广泛用于配置文件、数据交换和复杂数据结构的描述(如Kubernetes、Ansible等工具的配置),而非传统电子表格类的表格数据存储。 ### YAML文件的特点 - **可读性高**: 使用缩进和符号(如`-`、`:`)表示层级关系,类似自然语言的格式,比JSON/XML更易阅读。 ```yaml user: name: Alice age: 30 hobbies: - reading - hiking ``` - **支持复杂数据结构**: 可表示标量(字符串、数字)、列表、字典等类型,支持嵌套和多行文本(通过`|`或`>`)。 - **跨平台兼容**: 纯文本格式,所有操作系统和编程语言均可解析。 - **与编程语言无缝集成**: 大多数语言(Python、Java、Go等)提供原生或第三方库(如PyYAML)支持YAML解析。 ### YAML的用途 1. **配置文件**(核心用途) - 软件配置(如Docker Compose、GitLab CI/CD) - 云原生工具配置(Kubernetes manifests) ```yaml # Kubernetes Deployment示例 apiVersion: apps/v1 kind: Deployment metadata: name: nginx-deployment spec: replicas: 3 ``` 2. **数据序列化** - 替代JSON/XML传输复杂数据 - API请求/响应的结构化数据描述 3. **数据交换** - 不同系统间传递结构化信息(如微服务配置同步) ### 如何创建和编辑YAML文件? 1. **文本编辑器** - VS Code(推荐安装YAML插件实现语法高亮和校验) - Sublime Text / Vim等 2. **专用工具** - **Online YAML Validator**:校验语法有效性 - **yq**:命令行工具(类似jq,用于处理YAML) ### 注意事项 1. **缩进敏感** - 必须使用空格(通常2或4空格,禁止使用Tab) - 缩进错误会导致解析失败 2. **键值对格式** - 冒号后需有空格:`key: value`(而非`key:value`) 3. **特殊字符处理** - 字符串包含`:`、`#`等符号时建议使用引号: ```yaml message: "Hello:World" comment: "This is a # symbol" ``` 4. **多行文本** - 保留换行符:`|` ```yaml description: | This is a multi-line text. ``` - 折叠换行符:`>` ```yaml summary: > This will fold into a single line. ``` 5. **数据类型标记** - 强制指定类型: ```yaml boolean: !!bool "true" timestamp: !!timestamp 2023-07-20T15:30:00Z ``` ### 常见错误示例 ```yaml # 错误:缩进混用空格和Tab user: name: Bob age: 25 # 缩进不一致 # 错误:键值对缺少空格 key1:value1 # 应改为 key1: value1 # 错误:未转义特殊字符 message: Line 1 Line 2 # 缺少多行文本标识符 ``` ### C语言版YAML库 varch提供的YAML库,简便易用,能完成大部分对于YAML文件的基础操作,包含对yaml的加载和保存,增删改查等操作。 ## 接口 ### 创建和删除yaml对象 ```c yaml_t yaml_create(void); void yaml_delete(yaml_t yaml); ``` 其中**yaml_t**为yaml的结构体,创建方法会生成一个空yaml对象。删除方法则删除指定的yaml对象。 ### yaml对象加载 ```c yaml_t yaml_loads(const char* text, int flag); yaml_t yaml_file_load(char* filename, int flag); ``` yaml对象可以从字符串文本中加载,也可以从文件中加载。加载成功则会返回一个yaml对象,失败则返回NULL。 flag为操作函数的一些flag,定义如下: ``` #define YAML_F_NONE (0) #define YAML_F_DFLOW (0x01) /* dumps flow format */ #define YAML_F_LDOCS (0x02) /* load muti documents */ #define YAML_F_NOKEY (0x04) /* operate without key */ #define YAML_F_COMPLEX (0x08) /* operate with complex key */ #define YAML_F_ANCHOR (0x10) /* operate with anchor */ #define YAML_F_RECURSE (0x20) /* operate recurse */ #define YAML_F_REFERENCE (0x40) /* operate with reference */ ``` 当yaml对象加载失败的时候,可以调用`int yaml_error_info(int* line, int* column);`函数进行定位错误。 错误类型包含 ``` #define YAML_E_OK (0) /* ok, no error */ #define YAML_E_INVALID (1) /* invalid, not a valid expected value */ #define YAML_E_END (2) /* many invalid characters appear at the end */ #define YAML_E_KEY (3) /* parsing key, invalid key content found */ #define YAML_E_VALUE (4) /* parsing value, invalid value content found */ #define YAML_E_MEMORY (5) /* memory allocation failed */ #define YAML_E_SQUARE (6) /* mising ']' */ #define YAML_E_CURLY (7) /* mising '}' */ #define YAML_E_TAB (8) /* incorrect indent depth */ #define YAML_E_MIX (9) /* mix type */ #define YAML_E_FLINE (10) /* the first line of value can only be a literal */ #define YAML_E_LNUMBER (11) /* the number exceeds the storage capacity */ #define YAML_E_LBREAK (12) /* line break */ #define YAML_E_NANCHOR (13) /* null anchor */ #define YAML_E_IANCHOR (14) /* invalid anchor */ #define YAML_E_RANCHOR (15) /* repeat anchor */ #define YAML_E_UANCHOR (16) /* undefine anchor */ #define YAML_E_TANCHOR (17) /* type error anchor */ #define YAML_E_DATE (18) /* date error */ #define YAML_E_TARTGET (19) /* date error */ ``` ### yaml对象转储 ```c char* yaml_dumps(yaml_t yaml, int preset, int* len, int flag); int yaml_file_dump(yaml_t yaml, char* filename); ``` 首先**yaml_dumps**方法,将yaml对象按格式转储为字符串。`*len`则是转换出来的字符串长度,传入`NULL`时候就是不获取长度。返回值则是转换出来的字符串,这个字符串是函数分配的,**在结束使用需要`free`掉**。 **yaml_file_dump**方法则是在**yaml_dumps**的基础上将yaml转储到文件当中,`filename`传入文件名,返回值为转储的长度,负值表示转储失败。 ### yaml添加子对象 ```c #define yaml_seq_add_null(yaml) #define yaml_seq_add_int(yaml, num) #define yaml_seq_add_float(yaml, num) #define yaml_seq_add_string(yaml, string) #define yaml_seq_add_sequence(yaml, sequence) #define yaml_seq_add_mapping(yaml, mapping) #define yaml_map_add_null(yaml, key) #define yaml_map_add_int(yaml, key, num) #define yaml_map_add_float(yaml, key, num) #define yaml_map_add_string(yaml, key, string) #define yaml_map_add_sequence(yaml, key, sequence) #define yaml_map_add_mapping(yaml, key, mapping) ``` 以上方法分别在序列中和在映射中添加标量,其实就是套用 `insert` 类方法实现的 ```c yaml_t yaml_insert_null(yaml_t yaml, const char* key, unsigned int index); yaml_t yaml_insert_bool(yaml_t yaml, const char* key, unsigned int index, int b); yaml_t yaml_insert_int(yaml_t yaml, const char* key, unsigned int index, int num); yaml_t yaml_insert_float(yaml_t yaml, const char* key, unsigned int index, double num); yaml_t yaml_insert_string(yaml_t yaml, const char* key, unsigned int index, const char* string); yaml_t yaml_insert_sequence(yaml_t yaml, const char* key, unsigned int index, yaml_t sequence); yaml_t yaml_insert_mapping(yaml_t yaml, const char* key, unsigned int index, yaml_t mapping); yaml_t yaml_insert_document(yaml_t yaml, unsigned int index, yaml_t document); yaml_t yaml_insert_reference(yaml_t yaml, const char* key, unsigned int index, const char* anchor, yaml_t doc); ``` 这些`insert`类方法用于在`yaml`对象的指定位置插入不同类型的子对象。`key`为插入对象的键,`index`为插入的位置。返回值为插入操作后的`yaml`对象,若插入失败则返回`NULL`。具体如下: - `yaml_insert_null`:插入一个空类型的子对象。 - `yaml_insert_bool`:插入一个布尔类型的子对象,`b`为布尔值(`YAML_FALSE` 或 `YAML_TRUE`)。 - `yaml_insert_int`:插入一个整数类型的子对象,`num`为整数值。 - `yaml_insert_float`:插入一个浮点数类型的子对象,`num`为浮点数值。 - `yaml_insert_string`:插入一个字符串类型的子对象,`string`为字符串值。 - `yaml_insert_sequence`:插入一个序列类型的子对象,`sequence`为序列对象。 - `yaml_insert_mapping`:插入一个映射类型的子对象,`mapping`为映射对象。 - `yaml_insert_document`:插入一个文档类型的子对象,`document`为文档对象。 - `yaml_insert_reference`:插入一个引用类型的子对象,`anchor`为引用的锚点,`doc`为引用的文档对象。 ### yaml移除子对象 ```c int yaml_remove(yaml_t yaml, const char* key, unsigned int index); ``` 移除`yaml`对象中特定的键为`key`的第`index`个子对象。若移除成功,返回`YAML_E_OK`;若移除失败,返回相应的错误码。 ### yaml对象属性操作 ```c int yaml_type(yaml_t yaml); unsigned int yaml_size(yaml_t yaml); ``` - `yaml_type`:获取`yaml`对象的类型,返回值为`YAML_TYPE_*`系列宏定义的值,用于判断对象是`NULL`、布尔、整数、浮点数、字符串、序列、映射、文档、引用或复杂键等类型。 - `yaml_size`:获取`yaml`对象的大小。对于序列或映射类型的对象,返回其元素的数量;对于其他类型的对象,返回值的含义可能因具体实现而异。 ### yaml对象比较和复制 ```c int yaml_compare(yaml_t yaml, yaml_t cmp, int flag); yaml_t yaml_copy(yaml_t yaml, int flag); ``` - `yaml_compare`:比较两个`yaml`对象。`flag`为比较的标志位,用于指定比较的方式。返回值为比较结果,具体含义由实现决定,通常 0 表示相等,非 0 表示不相等。 - `yaml_copy`:复制一个`yaml`对象。`flag`为复制的标志位,用于指定复制的方式。返回值为复制后的`yaml`对象,若复制失败则返回`NULL`。 ### yaml对象键操作 ```c yaml_t yaml_set_key(yaml_t yaml, const char* key); yaml_t yaml_set_key_complex(yaml_t yaml, yaml_t key); const char* yaml_key(yaml_t yaml); yaml_t yaml_key_complex(yaml_t yaml); ``` - `yaml_set_key`:为`yaml`对象设置一个简单键,`key`为键的字符串值。返回值为设置键后的`yaml`对象。 - `yaml_set_key_complex`:为`yaml`对象设置一个复杂键,`key`为复杂键的`yaml`对象。返回值为设置键后的`yaml`对象。 - `yaml_key`:获取`yaml`对象的简单键,返回值为键的字符串值。 - `yaml_key_complex`:获取`yaml`对象的复杂键,返回值为复杂键的`yaml`对象。 ### yaml对象值设置 ```c yaml_t yaml_set_null(yaml_t yaml); yaml_t yaml_set_bool(yaml_t yaml, int b); yaml_t yaml_set_int(yaml_t yaml, int num); yaml_t yaml_set_float(yaml_t yaml, double num); yaml_t yaml_set_string(yaml_t yaml, const char* string); yaml_t yaml_set_date(yaml_t yaml, int year, char month, char day); yaml_t yaml_set_time(yaml_t yaml, char hour, char minute, char second, int msec); yaml_t yaml_set_utc(yaml_t yaml, char hour, char minute); yaml_t yaml_set_sequence(yaml_t yaml, yaml_t sequence); yaml_t yaml_set_mapping(yaml_t yaml, yaml_t mapping); yaml_t yaml_set_document(yaml_t yaml, yaml_t document); ``` 这些方法用于设置`yaml`对象的值,返回值为设置值后的`yaml`对象。具体如下: - `yaml_set_null`:将`yaml`对象的值设置为空类型。 - `yaml_set_bool`:将`yaml`对象的值设置为布尔类型,`b`为布尔值(`YAML_FALSE` 或 `YAML_TRUE`)。 - `yaml_set_int`:将`yaml`对象的值设置为整数类型,`num`为整数值。 - `yaml_set_float`:将`yaml`对象的值设置为浮点数类型,`num`为浮点数值。 - `yaml_set_string`:将`yaml`对象的值设置为字符串类型,`string`为字符串值。 - `yaml_set_date`:将`yaml`对象的值设置为日期类型,`year`为年份,`month`为月份,`day`为日期。 - `yaml_set_time`:将`yaml`对象的值设置为时间类型,`hour`为小时,`minute`为分钟,`second`为秒,`msec`为毫秒。 - `yaml_set_utc`:将`yaml`对象的值设置为 UTC 时间偏移类型,`hour`为小时偏移,`minute`为分钟偏移。 - `yaml_set_sequence`:将`yaml`对象的值设置为序列类型,`sequence`为序列对象。 - `yaml_set_mapping`:将`yaml`对象的值设置为映射类型,`mapping`为映射对象。 - `yaml_set_document`:将`yaml`对象的值设置为文档类型,`document`为文档对象。 ### yaml对象值获取 ```c int yaml_value_bool(yaml_t yaml); int yaml_value_int(yaml_t yaml); double yaml_value_float(yaml_t yaml); const char* yaml_value_string(yaml_t yaml); yaml_t yaml_value_sequence(yaml_t yaml); yaml_t yaml_value_mapping(yaml_t yaml); yaml_t yaml_value_document(yaml_t yaml); ``` 这些方法用于获取`yaml`对象的值,返回值为获取到的值。具体如下: - `yaml_value_bool`:获取`yaml`对象的布尔值。 - `yaml_value_int`:获取`yaml`对象的整数值。 - `yaml_value_float`:获取`yaml`对象的浮点数值。 - `yaml_value_string`:获取`yaml`对象的字符串值。 - `yaml_value_sequence`:获取`yaml`对象的序列值,返回序列对象。 - `yaml_value_mapping`:获取`yaml`对象的映射值,返回映射对象。 - `yaml_value_document`:获取`yaml`对象的文档值,返回文档对象。 ### yaml对象子元素操作 ```c yaml_t yaml_attach(yaml_t yaml, unsigned int index, yaml_t attach); yaml_t yaml_dettach(yaml_t yaml, unsigned int index); ``` - `yaml_attach`:在`yaml`对象的指定位置`index`附加一个`yaml`子对象`attach`。返回值为操作后的`yaml`对象,若操作失败则返回`NULL`。 - `yaml_dettach`:从`yaml`对象的指定位置`index`分离一个子对象。返回值为分离出的子对象,若操作失败则返回`NULL`。 ### yaml对象索引和子对象获取 ```c unsigned int yaml_get_index(yaml_t yaml, const char* key, unsigned int index); unsigned int yaml_get_index_complex(yaml_t yaml, yaml_t key); yaml_t yaml_get_child(yaml_t yaml, const char* key, unsigned int index); yaml_t yaml_get_child_complex(yaml_t yaml, yaml_t key); ``` - `yaml_get_index`:获取`yaml`对象中键为`key`的第`index`个子对象的索引。返回值为子对象的索引,若未找到则返回`YAML_INV_INDEX`。 - `yaml_get_index_complex`:获取`yaml`对象中复杂键为`key`的子对象的索引。返回值为子对象的索引,若未找到则返回`YAML_INV_INDEX`。 - `yaml_get_child`:获取`yaml`对象中键为`key`的第`index`个子对象。返回值为子对象的`yaml`对象,若未找到则返回`NULL`。 - `yaml_get_child_complex`:获取`yaml`对象中复杂键为`key`的子对象。返回值为子对象的`yaml`对象,若未找到则返回`NULL`。 ### yaml对象锚点和别名操作 ```c const char* yaml_get_alias(yaml_t yaml); yaml_t yaml_get_anchor(yaml_t yaml, unsigned int index); yaml_t yaml_set_alias(yaml_t yaml, const char* alias, yaml_t doc); yaml_t yaml_set_anchor(yaml_t yaml, const char* anchor, yaml_t doc); unsigned int yaml_anchor_size(yaml_t yaml); ``` - `yaml_get_alias`:获取`yaml`对象的别名,返回值为别名的字符串值。 - `yaml_get_anchor`:获取`yaml`对象中指定索引`index`的锚点对象,返回值为锚点的`yaml`对象。 - `yaml_set_alias`:为`yaml`对象设置别名,`alias`为别名的字符串值,`doc`为关联的文档对象。返回值为设置别名后的`yaml`对象。 - `yaml_set_anchor`:为`yaml`对象设置锚点,`anchor`为锚点的字符串值,`doc`为关联的文档对象。返回值为设置锚点后的`yaml`对象。 - `yaml_anchor_size`:获取`yaml`对象中锚点的数量。返回值为锚点的数量。 ## 参考例子 ### 生成yaml文件 ```c static void test_dump(void) { yaml_t root, node, temp; root = yaml_create(); yaml_set_mapping(root, NULL); node = yaml_map_add_mapping(root, "mapping", NULL); yaml_map_add_string(node, "version", "1.0.0"); yaml_map_add_string(node, "author", "Lamdonn"); yaml_map_add_string(node, "license", "GPL-2.0"); node = yaml_map_add_sequence(root, "sequence", NULL); yaml_seq_add_string(node, "file description"); yaml_seq_add_string(node, "This is a C language version of yaml streamlined parser"); yaml_seq_add_string(node, "Copyright (C) 2023 Lamdonn."); temp = yaml_seq_add_mapping(node, NULL); yaml_map_add_string(temp, "age", "18"); yaml_map_add_string(temp, "height", "178cm"); yaml_map_add_string(temp, "weight", "75kg"); yaml_remove(temp, 0, 1); /* preview yaml */ yaml_preview(root); /* dump yaml file */ yaml_file_dump(root, WRITE_FILE); yaml_delete(root); } ``` 转储的文件 **write.yaml** ```yaml mapping: version: 1.0.0 author: Lamdonn license: GPL-2.0 sequence: - file description - This is a C language version of yaml streamlined parser - Copyright (C) 2023 Lamdonn. - age: 18 weight: 75kg ``` ### 加载yaml文件 同样加载yaml文件 ```c static void test_load(void) { yaml_t root = NULL, x = NULL; root = yaml_file_load(READ_FILE, YAML_F_LDOCS); if (!root) { int type = 0, line = 0, column = 0; type = yaml_error_info(&line, &column); printf("error at line %d column %d type %d.\r\n", line, column, type); return; } printf("load success!\r\n"); yaml_preview(root); yaml_delete(root); } ``` 运行结果: ``` load success! mapping: version: 1.0.0 author: Lamdonn license: GPL-2.0 sequence: - file description - This is a C language version of yaml streamlined parser - Copyright (C) 2023 Lamdonn. - age: 18 weight: 75kg ```