Programming/C
YAML 개요 및 libyaml 사용법
moxie2ks
2025. 3. 10. 18:01
728x90
반응형
개요
- YAML은 사람이 읽을 수 있는 데이터 직렬화 언어이다. 구성 파일과 데이터가 저장되거나 전송되는 응용 프로그램에 일반적으로 사용된다.
- YAML은 XML(Extensible Markup Language)과 동일한 많은 통신 응용 프로그램을 대상으로 하지만 SGML(Standard Generalized Markup Language)과는 의도적으로 다른 최소 구문을 가지고 있다.
- 파이썬 스타일 들여쓰기를 사용하여 네스팅을 나타내며 대부분의 문자열 값에 대한 따옴표를 필요로 하지 않는다.
- 사용자 지정 데이터 유형이 허용되지만 YAML은 기본적으로 스칼라(예: 문자열, int 및 float), 목록 및 연관 배열(map, dictionary 또는 hash)을 인코딩한다. 이러한 데이터 유형은 펄 프로그래밍 언어를 기반으로 하지만 일반적으로 사용되는 모든 고급 프로그래밍 언어는 매우 유사한 개념을 공유한다.
- key-value pair를 표현하는 데 사용되는 콜론 중심 구문은 RFC 822에서 정의된 전자 메일 헤더에서 영감을 받았으며 문서 구분자
---
는 MIME(RFC 2046)에서 차용했다. 이스케이프 시퀀스는 C에서 재사용되며 여러 줄 문자열에 대한 공백 래핑은 HTML에서 영감을 받았다. - 목록과 해시는 중첩된 목록과 해시를 포함하여 트리 구조를 형성할 수 있으며 임의의 그래프는 SOAP의 XML과 유사한 YAML 별칭을 사용하여 나타낼 수 있다.
- YAML은 SAX에서 영감을 받은 기능인 스트림으로 읽고 쓸 수 있다.
- 많은 프로그래밍 언어에서 YAML을 읽고 쓰기 위한 지원이 가능하다.
- YAML 파일의 공식 권장 파일 이름 확장자는 2006년부터
.yaml
이다. 2024년 MIME 유형application/yaml
이 최종 확정되었다.
특징
장점 | 단점 |
---|---|
가독성 - 들여쓰기와 개행을 사용하여 데이터 구조를 표현하여 사람이 읽기 쉽다. | 복잡성 - 복잡한 데이터 구조를 표현하려면 구문이 복잡해지고 가독성이 떨어진다. |
언어 독립성 - 다양한 프로그래밍 언어에서 사용할 수 있다. | 구문 오류 - 들여쓰기나 특수 문자의 사용에 신경 써야 하며, 실수하기 쉽다. |
복잡한 데이터 구조 지원 - 리스트, 맵 등의 복잡한 데이터 구조를 표현할 수 있다. | 성능 - JSON이나 XML에 비해 파싱 속도가 느릴 수 있다. |
재사용 가능한 데이터 구조 - 앵커와 별칭을 이용해 데이터의 재사용성을 높일 수 있다. | 호환성 - 다른 데이터 포맷에 비해 지원하는 도구나 라이브러리가 적을 수 있다. |
확장 가능성 - 사용자 정의 타입을 지원하여 데이터의 유연성과 확장성을 높인다. |
기본 문법
#
은 주석
---
문서의 시작을 나타내며 선택 사항이다.
...
문서의 끝을 나타내며 선택 사항이다.
기본 표현
'key: value' 로 표현하며, ':' 다음에는 무조건 공백 문자가 와야한다.
':' 은 통상 'key' 가 된다고 볼 수 있다.
key: value
key:
key_1:
key_2:
key_3:
..
자료형
int, string, boolean 을 지원한다.
int_type: 1
string_type: "1"
boolean_type: true
object 표현
key:
key: value
key: value
# 또는
key: {
key: value,
key: value
}
list 표현
key:
- item
- item
# 또는
key: [
item, item
]
text 표현
|
와 >
|
는 줄바꿈을 포함하고, >
는 줄바꿈을 무시한다.
예시
--- # 문서의 시작
# my test yaml syntax
name: wook
job: developer
basic_list:
- apple
- banana
- orange
another_list: [
apple,
banana,
orange
]
object_list:
- color: red
direction: left
- color: blue
direction: right
basic_object:
time: '12:34:11'
date: '2019-04-30'
another_object: {
time: '12:34:11',
date: '2019-04-30'
}
comment_line_break: |
Hello my name is wook.
Im developer.
comment_single_line: >
Hello world
my first yml syntax.
... # 문서의 끝
YAML 파일의 파싱하기
.yaml
파일을 파싱하기 위해, libyaml 라이브러리를 사용할 수 있다.
libyaml
libyaml은 YAML을 파싱하고 파싱된 값을 하기 위한 C 라이브러리이다.
Build
- libyaml 소스코드 다운로드 - https://pyyaml.org/download/libyaml/yaml-0.2.5.zip
- configure & make
$ ./configure --prefix=<path to install>
$ make
# make install
API Synopsis
Parser API Synopsis
#include <yaml.h>
yaml_parser_t parser;
yaml_event_t event;
int done = 0;
/* Create the Parser object. */
yaml_parser_initialize(&parser);
/* Set a string input. */
char *input = "...";
size_t length = strlen(input);
yaml_parser_set_input_string(&parser, input, length);
/* Set a file input. */
FILE *input = fopen("...", "rb");
yaml_parser_set_input_file(&parser, input);
/* Set a generic reader. */
void *ext = ...;
int read_handler(void *ext, char *buffer, int size, int *length) {
/* ... */
*buffer = ...;
*length = ...;
/* ... */
return error ? 0 : 1;
}
yaml_parser_set_input(&parser, read_handler, ext);
/* Read the event sequence. */
while (!done) {
/* Get the next event. */
if (!yaml_parser_parse(&parser, &event))
goto error;
/*
...
Process the event.
...
*/
/* Are we finished? */
done = (event.type == YAML_STREAM_END_EVENT);
/* The application is responsible for destroying the event object. */
yaml_event_delete(&event);
}
/* Destroy the Parser object. */
yaml_parser_delete(&parser);
return 1;
/* On error. */
error:
/* Destroy the Parser object. */
yaml_parser_delete(&parser);
return 0;
Emitter API Synopsis
#include <yaml.h>
yaml_emitter_t emitter;
yaml_event_t event;
/* Create the Emitter object. */
yaml_emitter_initialize(&emitter);
/* Set a file output. */
FILE *output = fopen("...", "wb");
yaml_emitter_set_output_file(&emitter, output);
/* Set a generic writer. */
void *ext = ...;
int write_handler(void *ext, char *buffer, int size) {
/*
...
Write `size` bytes.
...
*/
return error ? 0 : 1;
}
yaml_emitter_set_output(&emitter, write_handler, ext);
/* Create and emit the STREAM-START event. */
yaml_stream_start_event_initialize(&event, YAML_UTF8_ENCODING);
if (!yaml_emitter_emit(&emitter, &event))
goto error;
/*
...
Emit more events.
...
*/
/* Create and emit the STREAM-END event. */
yaml_stream_end_event_initialize(&event);
if (!yaml_emitter_emit(&emitter, &event))
goto error;
/* Destroy the Emitter object. */
yaml_emitter_delete(&emitter);
return 1;
/* On error. */
error:
/* Destroy the Emitter object. */
yaml_emitter_delete(emitter);
return 0;
예제코드
test.yaml
---
db_server: "localhost"
db_username: "test"
db_password: "wibble"
national_rail_username: test
national_rail_password: wibble
...
main.c
#include <stdio.h>
#include <string.h>
#include <yaml.h>
typedef struct Conf {
char db_server[100];
char db_pass[100];
char db_user[100];
char rail_user[100];
char rail_pass[100];
} Conf;
int readConf(char* filename, Conf* conf)
{
FILE* fh = fopen(filename, "r");
yaml_parser_t parser;
yaml_token_t token;
int state = 0;
if (!yaml_parser_initialize(&parser))
fputs("Failed to initialize parser!\n", stderr);
if (fh == NULL)
fputs("Failed to open file!\n", stderr);
yaml_parser_set_input_file(&parser, fh);
do {
char* target_key;
char* tk;
yaml_parser_scan(&parser, &token);
switch(token.type)
{
case YAML_KEY_TOKEN: state = 0; break;
case YAML_VALUE_TOKEN: state = 1; break;
case YAML_SCALAR_TOKEN:
tk = (char*)token.data.scalar.value;
if (state == 0)
{
if (!strcmp(tk, "db_server"))
target_key = conf->db_server;
else if (!strcmp(tk, "db_password"))
target_key = conf->db_pass;
else if (!strcmp(tk, "db_username"))
target_key = conf->db_user;
else if (!strcmp(tk, "national_rail_username"))
target_key = conf->rail_user;
else if (!strcmp(tk, "national_rail_password"))
target_key = conf->rail_pass;
else
printf("Unrecognised key: %s\n", tk);
}
else
strcpy(target_key, tk);
break;
default: break;
}
if (token.type != YAML_STREAM_END_TOKEN)
yaml_token_delete(&token);
}
while (token.type != YAML_STREAM_END_TOKEN);
yaml_token_delete(&token);
yaml_parser_delete(&parser);
fclose(fh);
return 0;
}
int main()
{
Conf conf = {0};
readConf("test.yaml", &conf);
printf("%s\n", conf.db_server);
printf("%s\n", conf.db_user);
printf("%s\n", conf.db_pass);
printf("%s\n", conf.rail_user);
printf("%s\n", conf.rail_pass);
return 0;
}
컴파일 및 실행
$ gcc main.c -o test -I<installed libyaml path>/include -L<installed libyaml path>/lib -lyaml && ./test
결과
참조문헌
728x90
반응형