mirror of
https://gitee.com/Lamdonn/varch.git
synced 2025-12-08 01:36:42 +08:00
433 lines
18 KiB
Markdown
433 lines
18 KiB
Markdown
## 介绍
|
||
|
||
### 什么是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
|
||
|
||
```
|