mirror of
https://gitee.com/zlgopen/awtk.git
synced 2024-11-29 18:48:09 +08:00
add csv file
This commit is contained in:
parent
1cd6e324db
commit
83a49bbeae
@ -2,6 +2,7 @@
|
||||
|
||||
2021/01/21
|
||||
* 将销毁自定义属性代码放到on\_destroy回调函数之后。
|
||||
* 增加csv file,请参考[csv file](../src/csv/README.md)。
|
||||
* text\_selector 支持同时修改选项的步长和格式 (感谢智明提供补丁)
|
||||
|
||||
2021/01/20
|
||||
|
@ -15,6 +15,7 @@ BASE_SOURCES=Glob('layouters/*.c') + \
|
||||
Glob('ui_loader/*.c') + \
|
||||
Glob('fscript_ext/*.c') + \
|
||||
Glob('xml/*.c') + \
|
||||
Glob('csv/*.c') + \
|
||||
Glob('svg/*.c') + \
|
||||
Glob('charset/*.c') + \
|
||||
Glob('clip_board/*.c') + \
|
||||
|
87
src/csv/README.md
Normal file
87
src/csv/README.md
Normal file
@ -0,0 +1,87 @@
|
||||
# csv 文件
|
||||
|
||||
> 本目录的文件从 [awtk-csv-file](https://github.com/zlgopen/awtk-csv-file) 拷贝而来。如果需要修改,请先修改 [awtk-csv-file](https://github.com/zlgopen/awtk-csv-file),再拷贝过来。
|
||||
|
||||
csv 为可选组件,需要自己包含头文件
|
||||
|
||||
```c
|
||||
#include "csv/csv_file.h"
|
||||
#include "csv/csv_file_object.h"
|
||||
```
|
||||
|
||||
## 示例:
|
||||
|
||||
* 普通方式访问
|
||||
|
||||
```c
|
||||
const char* str = "11,12,13\n21,22,23";
|
||||
csv_file_t* csv = csv_file_create_with_buff(str, strlen(str), ',');
|
||||
ASSERT_EQ(csv_file_get_rows(csv), 2);
|
||||
ASSERT_EQ(csv_file_get_cols(csv), 3);
|
||||
|
||||
ASSERT_STREQ(csv_file_get(csv, 0, 0), "11");
|
||||
ASSERT_STREQ(csv_file_get(csv, 0, 1), "12");
|
||||
ASSERT_STREQ(csv_file_get(csv, 0, 2), "13");
|
||||
|
||||
ASSERT_STREQ(csv_file_get(csv, 1, 0), "21");
|
||||
ASSERT_STREQ(csv_file_get(csv, 1, 1), "22");
|
||||
ASSERT_STREQ(csv_file_get(csv, 1, 2), "23");
|
||||
|
||||
csv_file_destroy(csv);
|
||||
```
|
||||
|
||||
```c
|
||||
TEST(csv_file, title) {
|
||||
const char* str = "aa,bb,cc\n11,12,13\n21,22,23";
|
||||
csv_file_t* csv = csv_file_create_with_buff(str, strlen(str), ',');
|
||||
ASSERT_EQ(csv_file_get_rows(csv), 3);
|
||||
ASSERT_EQ(csv_file_get_cols(csv), 3);
|
||||
|
||||
ASSERT_STREQ(csv_file_get(csv, 0, 0), "aa");
|
||||
ASSERT_STREQ(csv_file_get(csv, 0, 1), "bb");
|
||||
ASSERT_STREQ(csv_file_get(csv, 0, 2), "cc");
|
||||
|
||||
ASSERT_STREQ(csv_file_get(csv, 1, 0), "11");
|
||||
ASSERT_STREQ(csv_file_get(csv, 1, 1), "12");
|
||||
ASSERT_STREQ(csv_file_get(csv, 1, 2), "13");
|
||||
|
||||
ASSERT_STREQ(csv_file_get(csv, 2, 0), "21");
|
||||
ASSERT_STREQ(csv_file_get(csv, 2, 1), "22");
|
||||
ASSERT_STREQ(csv_file_get(csv, 2, 2), "23");
|
||||
|
||||
ASSERT_STREQ(csv_file_get_by_name(csv, 1, "aa"), "11");
|
||||
ASSERT_STREQ(csv_file_get_by_name(csv, 1, "bb"), "12");
|
||||
ASSERT_STREQ(csv_file_get_by_name(csv, 1, "cc"), "13");
|
||||
|
||||
ASSERT_STREQ(csv_file_get_by_name(csv, 2, "aa"), "21");
|
||||
ASSERT_STREQ(csv_file_get_by_name(csv, 2, "bb"), "22");
|
||||
ASSERT_STREQ(csv_file_get_by_name(csv, 2, "cc"), "23");
|
||||
|
||||
ASSERT_EQ(csv_file_get_col_of_name(csv, "aa"), 0);
|
||||
ASSERT_EQ(csv_file_get_col_of_name(csv, "bb"), 1);
|
||||
ASSERT_EQ(csv_file_get_col_of_name(csv, "cc"), 2);
|
||||
|
||||
csv_file_destroy(csv);
|
||||
}
|
||||
```
|
||||
* 以对象的方式访问:
|
||||
|
||||
```c
|
||||
const char* str = "aa,bb,cc\n11,12,13\n21,22,23";
|
||||
csv_file_t* csv = csv_file_create_with_buff(str, strlen(str), ',');
|
||||
object_t* obj = csv_file_object_create(csv);
|
||||
|
||||
ASSERT_EQ(object_get_prop_int(obj, "#size", 0), 3);
|
||||
ASSERT_STREQ(object_get_prop_str(obj, "[0].[0]"), "aa");
|
||||
ASSERT_STREQ(object_get_prop_str(obj, "[0].[1]"), "bb");
|
||||
ASSERT_STREQ(object_get_prop_str(obj, "[0].[2]"), "cc");
|
||||
|
||||
ASSERT_STREQ(object_get_prop_str(obj, "[1].[0]"), "11");
|
||||
ASSERT_STREQ(object_get_prop_str(obj, "[1].[1]"), "12");
|
||||
ASSERT_STREQ(object_get_prop_str(obj, "[1].[2]"), "13");
|
||||
OBJECT_UNREF(obj);
|
||||
```
|
||||
|
||||
## 具体用法请参考:
|
||||
* https://github.com/zlgopen/awtk-csv-file/blob/master/tests/csv_file_test.cc
|
||||
* https://github.com/zlgopen/awtk-csv-file/blob/master/tests/csv_file_object_test.cc
|
703
src/csv/csv_file.c
Normal file
703
src/csv/csv_file.c
Normal file
@ -0,0 +1,703 @@
|
||||
/**
|
||||
* File: csv_file.c
|
||||
* Author: AWTK Develop Team
|
||||
* Brief: csv file
|
||||
*
|
||||
* Copyright (c) 2020 - 2020 Guangzhou ZHIYUAN Electronics Co.,Ltd.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* License file for more details.
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* History:
|
||||
* ================================================================
|
||||
* 2020-06-08 Li XianJing <xianjimli@hotmail.com> created
|
||||
*
|
||||
*/
|
||||
|
||||
#include "tkc/mem.h"
|
||||
#include "tkc/utils.h"
|
||||
|
||||
#include "csv_file.h"
|
||||
#include "streams/mem/istream_mem.h"
|
||||
#include "streams/file/istream_file.h"
|
||||
|
||||
static const uint8_t s_utf8_bom[3] = {0xEF, 0xBB, 0xBF};
|
||||
|
||||
ret_t csv_file_reset(csv_file_t* csv);
|
||||
ret_t csv_file_clear(csv_file_t* csv);
|
||||
static ret_t csv_rows_extend_rows(csv_rows_t* rows, uint32_t delta);
|
||||
static csv_file_t* csv_file_load_input(csv_file_t* csv, tk_istream_t* input);
|
||||
|
||||
const char* csv_row_get(csv_row_t* row, uint32_t col) {
|
||||
uint32_t i = 0;
|
||||
uint32_t c = 0;
|
||||
return_value_if_fail(row != NULL, NULL);
|
||||
|
||||
while (i < row->size) {
|
||||
const char* p = row->buff + i;
|
||||
if (c == col) {
|
||||
return p;
|
||||
}
|
||||
|
||||
c++;
|
||||
i += strlen(p) + 1;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int32_t csv_row_get_col(csv_row_t* row, const char* value) {
|
||||
uint32_t i = 0;
|
||||
uint32_t col = 0;
|
||||
return_value_if_fail(row != NULL && value != NULL, -1);
|
||||
|
||||
while (i < row->size) {
|
||||
const char* p = row->buff + i;
|
||||
if (tk_str_eq(p, value)) {
|
||||
return col;
|
||||
}
|
||||
|
||||
col++;
|
||||
i += strlen(p) + 1;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static ret_t csv_col_to_str(str_t* str, const char* data, char sep) {
|
||||
const char* p = data;
|
||||
bool_t need_escape = strchr(p, sep) != NULL || strchr(p, '\"') != NULL;
|
||||
|
||||
if (need_escape) {
|
||||
str_append_char(str, '\"');
|
||||
while (*p) {
|
||||
if (*p == '\\' || *p == '\"') {
|
||||
str_append_char(str, '\\');
|
||||
}
|
||||
str_append_char(str, *p);
|
||||
p++;
|
||||
}
|
||||
str_append_char(str, '\"');
|
||||
} else {
|
||||
str_append(str, data);
|
||||
}
|
||||
|
||||
return RET_OK;
|
||||
}
|
||||
|
||||
ret_t csv_row_to_str(csv_row_t* row, str_t* str, char sep) {
|
||||
uint32_t i = 0;
|
||||
return_value_if_fail(row != NULL && str != NULL, RET_BAD_PARAMS);
|
||||
|
||||
str_set(str, "");
|
||||
while ((i + 1) < row->size) {
|
||||
const char* p = row->buff + i;
|
||||
csv_col_to_str(str, p, sep);
|
||||
str_append_char(str, sep);
|
||||
|
||||
i += strlen(p) + 1;
|
||||
}
|
||||
|
||||
str_append(str, "\r\n");
|
||||
|
||||
return RET_OK;
|
||||
}
|
||||
|
||||
uint32_t csv_row_count_cols(csv_row_t* row) {
|
||||
uint32_t i = 0;
|
||||
uint32_t cols = 0;
|
||||
return_value_if_fail(row != NULL, 0);
|
||||
|
||||
while (i < row->size) {
|
||||
const char* p = row->buff + i;
|
||||
cols++;
|
||||
i += strlen(p) + 1;
|
||||
}
|
||||
|
||||
return cols;
|
||||
}
|
||||
|
||||
ret_t csv_row_set(csv_row_t* row, uint32_t col, const char* value) {
|
||||
char* p = NULL;
|
||||
uint32_t old_len = 0;
|
||||
uint32_t new_len = 0;
|
||||
return_value_if_fail(row != NULL && value != NULL, RET_BAD_PARAMS);
|
||||
return_value_if_fail(row->buff != NULL, RET_BAD_PARAMS);
|
||||
p = (char*)csv_row_get(row, col);
|
||||
return_value_if_fail(p != NULL, RET_BAD_PARAMS);
|
||||
|
||||
old_len = strlen(p);
|
||||
new_len = strlen(value);
|
||||
|
||||
if (old_len == new_len) {
|
||||
strcpy(p, value);
|
||||
return RET_OK;
|
||||
} else if (old_len > new_len) {
|
||||
uint32_t len = row->size - (p + old_len - row->buff);
|
||||
strcpy(p, value);
|
||||
memmove(p + new_len, p + old_len, len);
|
||||
row->size = row->size + new_len - old_len;
|
||||
return RET_OK;
|
||||
} else {
|
||||
uint32_t d = 0;
|
||||
uint32_t s = 0;
|
||||
uint32_t size = 0;
|
||||
uint32_t len = row->size + new_len - old_len;
|
||||
char* buff = TKMEM_ALLOC(len);
|
||||
return_value_if_fail(buff != NULL, RET_OOM);
|
||||
|
||||
memset(buff, 0x00, len);
|
||||
size = p - row->buff;
|
||||
memcpy(buff, row->buff, size);
|
||||
|
||||
d = size;
|
||||
s = size;
|
||||
memcpy(buff + d, value, new_len + 1);
|
||||
|
||||
d += new_len + 1;
|
||||
s += old_len + 1;
|
||||
size = row->size - s;
|
||||
memcpy(buff + d, row->buff + s, size);
|
||||
|
||||
csv_row_reset(row);
|
||||
|
||||
row->buff = buff;
|
||||
row->size = len;
|
||||
row->should_free_buff = TRUE;
|
||||
return RET_OK;
|
||||
}
|
||||
}
|
||||
|
||||
ret_t csv_row_init(csv_row_t* row, char* buff, uint32_t size, bool_t should_free_buff) {
|
||||
return_value_if_fail(row != NULL && buff != NULL, RET_BAD_PARAMS);
|
||||
|
||||
row->buff = buff;
|
||||
row->size = size;
|
||||
row->should_free_buff = should_free_buff;
|
||||
|
||||
return RET_OK;
|
||||
}
|
||||
|
||||
static ret_t csv_row_parse(csv_row_t* row, char sep) {
|
||||
char* s = row->buff;
|
||||
char* d = row->buff;
|
||||
bool_t escape = FALSE;
|
||||
bool_t in_quota = FALSE;
|
||||
|
||||
while (*s) {
|
||||
char c = *s;
|
||||
|
||||
s++;
|
||||
if (in_quota) {
|
||||
if (escape) {
|
||||
*d++ = c;
|
||||
escape = FALSE;
|
||||
} else {
|
||||
if (c == '\"') {
|
||||
in_quota = FALSE;
|
||||
} else if (c == '\\') {
|
||||
escape = TRUE;
|
||||
} else {
|
||||
*d++ = c;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (c == sep) {
|
||||
*d++ = '\0';
|
||||
} else if (c == '\"') {
|
||||
in_quota = TRUE;
|
||||
} else {
|
||||
*d++ = c;
|
||||
}
|
||||
}
|
||||
}
|
||||
*d = '\0';
|
||||
row->size = d - row->buff + 1;
|
||||
|
||||
return RET_OK;
|
||||
}
|
||||
|
||||
ret_t csv_row_set_data(csv_row_t* row, const char* data, char sep) {
|
||||
csv_row_reset(row);
|
||||
|
||||
row->buff = tk_strdup(data);
|
||||
return_value_if_fail(row->buff != NULL, RET_OOM);
|
||||
|
||||
row->should_free_buff = TRUE;
|
||||
row->size = strlen(data) + 1;
|
||||
|
||||
return csv_row_parse(row, sep);
|
||||
}
|
||||
|
||||
ret_t csv_row_reset(csv_row_t* row) {
|
||||
return_value_if_fail(row != NULL, RET_BAD_PARAMS);
|
||||
if (row->should_free_buff) {
|
||||
TKMEM_FREE(row->buff);
|
||||
}
|
||||
memset(row, 0x00, sizeof(*row));
|
||||
|
||||
return RET_OK;
|
||||
}
|
||||
|
||||
static ret_t csv_rows_extend_rows(csv_rows_t* rows, uint32_t delta) {
|
||||
uint32_t capacity = 0;
|
||||
csv_row_t* first = NULL;
|
||||
return_value_if_fail(rows != NULL, RET_BAD_PARAMS);
|
||||
|
||||
if ((rows->size + delta) < rows->capacity) {
|
||||
return RET_OK;
|
||||
}
|
||||
|
||||
first = rows->rows;
|
||||
capacity = rows->capacity * 1.5 + delta;
|
||||
first = TKMEM_REALLOC(first, capacity * sizeof(csv_row_t));
|
||||
return_value_if_fail(first != NULL, RET_OOM);
|
||||
|
||||
rows->rows = first;
|
||||
rows->capacity = capacity;
|
||||
|
||||
return RET_OK;
|
||||
}
|
||||
|
||||
ret_t csv_rows_remove(csv_rows_t* rows, uint32_t row) {
|
||||
uint32_t i = 0;
|
||||
csv_row_t* r = NULL;
|
||||
return_value_if_fail(rows != NULL && row < rows->size, RET_BAD_PARAMS);
|
||||
|
||||
csv_row_reset(rows->rows + row);
|
||||
|
||||
r = rows->rows;
|
||||
for (i = row; i < rows->size; i++) {
|
||||
r[i] = r[i + 1];
|
||||
}
|
||||
rows->size--;
|
||||
|
||||
return RET_OK;
|
||||
}
|
||||
|
||||
csv_row_t* csv_rows_append(csv_rows_t* rows) {
|
||||
csv_row_t* r = NULL;
|
||||
return_value_if_fail(csv_rows_extend_rows(rows, 1) == RET_OK, NULL);
|
||||
|
||||
r = rows->rows + rows->size++;
|
||||
memset(r, 0x00, sizeof(*r));
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
csv_row_t* csv_rows_insert(csv_rows_t* rows, uint32_t row) {
|
||||
uint32_t i = 0;
|
||||
csv_row_t* r = NULL;
|
||||
return_value_if_fail(rows != NULL, NULL);
|
||||
return_value_if_fail(csv_rows_extend_rows(rows, 1) == RET_OK, NULL);
|
||||
|
||||
if (row >= rows->size) {
|
||||
return csv_rows_append(rows);
|
||||
}
|
||||
|
||||
r = rows->rows;
|
||||
for (i = rows->size; i > row; i--) {
|
||||
r[i] = r[i - 1];
|
||||
}
|
||||
r = r + row;
|
||||
memset(r, 0x00, sizeof(*r));
|
||||
rows->size++;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
csv_row_t* csv_rows_get(csv_rows_t* rows, uint32_t row) {
|
||||
return_value_if_fail(rows != NULL && row < rows->size, NULL);
|
||||
|
||||
return rows->rows + row;
|
||||
}
|
||||
|
||||
ret_t csv_rows_reset(csv_rows_t* rows) {
|
||||
return_value_if_fail(rows != NULL && rows->rows != NULL, RET_BAD_PARAMS);
|
||||
|
||||
TKMEM_FREE(rows->rows);
|
||||
memset(rows, 0x00, sizeof(*rows));
|
||||
|
||||
return RET_OK;
|
||||
}
|
||||
|
||||
ret_t csv_rows_init(csv_rows_t* rows, uint32_t init_capacity) {
|
||||
return_value_if_fail(rows != NULL, RET_BAD_PARAMS);
|
||||
|
||||
init_capacity = tk_max(10, init_capacity);
|
||||
|
||||
rows->size = 0;
|
||||
rows->capacity = init_capacity;
|
||||
rows->rows = TKMEM_ZALLOCN(csv_row_t, init_capacity);
|
||||
|
||||
return rows->rows != NULL ? RET_OK : RET_OOM;
|
||||
}
|
||||
|
||||
csv_file_t* csv_file_load(csv_file_t* csv) {
|
||||
tk_istream_t* input = NULL;
|
||||
return_value_if_fail(csv != NULL && csv->filename != NULL, NULL);
|
||||
|
||||
input = tk_istream_file_create(csv->filename);
|
||||
return_value_if_fail(input != NULL, NULL);
|
||||
csv_file_load_input(csv, input);
|
||||
OBJECT_UNREF(input);
|
||||
|
||||
return csv;
|
||||
}
|
||||
|
||||
static csv_file_t* csv_file_load_input(csv_file_t* csv, tk_istream_t* input) {
|
||||
str_t str;
|
||||
char* p = NULL;
|
||||
uint32_t index = 0;
|
||||
csv_row_t* r = NULL;
|
||||
char sep = csv->sep;
|
||||
|
||||
str_init(&str, 2048);
|
||||
while (tk_istream_read_line_str(input, &str) == RET_OK) {
|
||||
p = str.str;
|
||||
if (csv->rows.size == 0) {
|
||||
if (memcmp(p, s_utf8_bom, sizeof(s_utf8_bom)) == 0) {
|
||||
p += sizeof(s_utf8_bom);
|
||||
log_debug("skip utf-8 bom\n");
|
||||
}
|
||||
}
|
||||
|
||||
/*skip malformed data*/
|
||||
while ((p - str.str) < str.size) {
|
||||
if (*p == '\0') {
|
||||
p++;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (*p == '\0') {
|
||||
log_debug("skip empty line\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
r = csv_rows_append(&(csv->rows));
|
||||
return_value_if_fail(r != NULL, NULL);
|
||||
return_value_if_fail(csv_row_init(r, tk_strdup(p), str.size + 1, TRUE) == RET_OK, NULL);
|
||||
csv_row_parse(r, sep);
|
||||
|
||||
if (csv->filter != NULL) {
|
||||
ret_t ret = csv->filter(csv->filter_ctx, csv, index, r);
|
||||
if (ret == RET_STOP) {
|
||||
break;
|
||||
} else if (ret == RET_FAIL) {
|
||||
csv_row_reset(r);
|
||||
csv->rows.size--;
|
||||
}
|
||||
}
|
||||
|
||||
index++;
|
||||
}
|
||||
|
||||
r = csv_file_get_row(csv, 0);
|
||||
if (r != NULL) {
|
||||
uint32_t cols1 = 0;
|
||||
uint32_t cols0 = csv_row_count_cols(r);
|
||||
|
||||
if (cols0 == 1) {
|
||||
r = csv_file_get_row(csv, 1);
|
||||
cols1 = csv_row_count_cols(r);
|
||||
if (cols1 > cols0) {
|
||||
csv->has_title = TRUE;
|
||||
}
|
||||
csv->cols = tk_max(cols0, cols1);
|
||||
} else {
|
||||
csv->cols = csv_row_count_cols(r);
|
||||
}
|
||||
}
|
||||
|
||||
return csv;
|
||||
}
|
||||
|
||||
csv_file_t* csv_file_create_empty(char sep, csv_file_filter_t filter, void* ctx) {
|
||||
csv_file_t* csv = TKMEM_ZALLOC(csv_file_t);
|
||||
return_value_if_fail(csv != NULL, NULL);
|
||||
|
||||
if (csv_rows_init(&(csv->rows), 100) == RET_OK) {
|
||||
csv->sep = sep;
|
||||
csv->filter = filter;
|
||||
csv->filter_ctx = ctx;
|
||||
} else {
|
||||
TKMEM_FREE(csv);
|
||||
}
|
||||
|
||||
return csv;
|
||||
}
|
||||
|
||||
ret_t csv_file_set_filter(csv_file_t* csv, csv_file_filter_t filter, void* ctx) {
|
||||
return_value_if_fail(csv != NULL, RET_BAD_PARAMS);
|
||||
|
||||
csv->filter = filter;
|
||||
csv->filter_ctx = ctx;
|
||||
|
||||
return RET_OK;
|
||||
}
|
||||
|
||||
csv_file_t* csv_file_create(const char* filename, char sep) {
|
||||
csv_file_t* csv = NULL;
|
||||
return_value_if_fail(filename != NULL, NULL);
|
||||
csv = csv_file_create_empty(sep, NULL, NULL);
|
||||
return_value_if_fail(csv != NULL, NULL);
|
||||
|
||||
if (csv_file_load_file(csv, filename) != RET_OK) {
|
||||
csv_file_destroy(csv);
|
||||
csv = NULL;
|
||||
}
|
||||
|
||||
return csv;
|
||||
}
|
||||
|
||||
ret_t csv_file_load_buff(csv_file_t* csv, const char* buff, uint32_t size) {
|
||||
ret_t ret = RET_OK;
|
||||
tk_istream_t* input = NULL;
|
||||
|
||||
return_value_if_fail(csv != NULL && buff != NULL && size > 0, RET_BAD_PARAMS);
|
||||
input = tk_istream_mem_create((uint8_t*)buff, size, 0, FALSE);
|
||||
return_value_if_fail(input != NULL, RET_BAD_PARAMS);
|
||||
|
||||
csv_file_clear(csv);
|
||||
ret = csv_file_load_input(csv, input) != NULL ? RET_OK : RET_FAIL;
|
||||
OBJECT_UNREF(input);
|
||||
|
||||
return RET_OK;
|
||||
}
|
||||
|
||||
csv_file_t* csv_file_create_with_buff(const char* buff, uint32_t size, char sep) {
|
||||
csv_file_t* csv = NULL;
|
||||
csv = csv_file_create_empty(sep, NULL, NULL);
|
||||
return_value_if_fail(csv != NULL, NULL);
|
||||
|
||||
if (csv_file_load_buff(csv, buff, size) != RET_OK) {
|
||||
csv_file_destroy(csv);
|
||||
csv = NULL;
|
||||
}
|
||||
|
||||
return csv;
|
||||
}
|
||||
|
||||
const char* csv_file_get(csv_file_t* csv, uint32_t row, uint32_t col) {
|
||||
csv_row_t* r = csv_file_get_row(csv, row);
|
||||
return_value_if_fail(r != NULL, NULL);
|
||||
return_value_if_fail(col < csv_file_get_cols(csv), NULL);
|
||||
|
||||
return csv_row_get(r, col);
|
||||
}
|
||||
|
||||
int32_t csv_file_get_col_of_name(csv_file_t* csv, const char* name) {
|
||||
return csv_row_get_col(csv_file_get_row(csv, 0), name);
|
||||
}
|
||||
|
||||
const char* csv_file_get_by_name(csv_file_t* csv, uint32_t row, const char* name) {
|
||||
int32_t col = csv_file_get_col_of_name(csv, name);
|
||||
return_value_if_fail(col >= 0, NULL);
|
||||
|
||||
return csv_file_get(csv, row, col);
|
||||
}
|
||||
|
||||
uint32_t csv_file_get_rows(csv_file_t* csv) {
|
||||
return_value_if_fail(csv != NULL, 0);
|
||||
|
||||
return csv->rows.size;
|
||||
}
|
||||
|
||||
uint32_t csv_file_get_checked_rows(csv_file_t* csv) {
|
||||
uint32_t i = 0;
|
||||
uint32_t nr = 0;
|
||||
csv_row_t* r = NULL;
|
||||
csv_rows_t* rows = NULL;
|
||||
return_value_if_fail(csv != NULL, 0);
|
||||
|
||||
rows = &(csv->rows);
|
||||
for (i = 0; i < rows->size; i++) {
|
||||
r = rows->rows + i;
|
||||
if (r->checked) {
|
||||
nr++;
|
||||
}
|
||||
}
|
||||
|
||||
return nr;
|
||||
}
|
||||
|
||||
uint32_t csv_file_get_cols(csv_file_t* csv) {
|
||||
return_value_if_fail(csv != NULL, 0);
|
||||
|
||||
return csv->cols;
|
||||
}
|
||||
|
||||
ret_t csv_file_set(csv_file_t* csv, uint32_t row, uint32_t col, const char* value) {
|
||||
csv_row_t* r = csv_file_get_row(csv, row);
|
||||
return_value_if_fail(r != NULL, RET_BAD_PARAMS);
|
||||
return_value_if_fail(col < csv_file_get_cols(csv), RET_BAD_PARAMS);
|
||||
|
||||
return csv_row_set(r, col, value);
|
||||
}
|
||||
|
||||
ret_t csv_file_set_row_checked(csv_file_t* csv, uint32_t row, bool_t checked) {
|
||||
csv_row_t* r = csv_file_get_row(csv, row);
|
||||
return_value_if_fail(r != NULL, RET_BAD_PARAMS);
|
||||
|
||||
r->checked = checked;
|
||||
|
||||
return RET_OK;
|
||||
}
|
||||
|
||||
bool_t csv_file_is_row_checked(csv_file_t* csv, uint32_t row) {
|
||||
csv_row_t* r = csv_file_get_row(csv, row);
|
||||
return_value_if_fail(r != NULL, FALSE);
|
||||
|
||||
return r->checked;
|
||||
}
|
||||
|
||||
ret_t csv_file_remove_checked_rows(csv_file_t* csv) {
|
||||
uint32_t i = 0;
|
||||
uint32_t d = 0;
|
||||
csv_row_t* r = NULL;
|
||||
csv_rows_t* rows = NULL;
|
||||
return_value_if_fail(csv != NULL, RET_BAD_PARAMS);
|
||||
|
||||
rows = &(csv->rows);
|
||||
for (i = 0; i < rows->size; i++) {
|
||||
r = rows->rows + i;
|
||||
if (r->checked) {
|
||||
csv_row_reset(r);
|
||||
} else {
|
||||
rows->rows[d++] = rows->rows[i];
|
||||
}
|
||||
}
|
||||
|
||||
rows->size = d;
|
||||
|
||||
return RET_OK;
|
||||
}
|
||||
|
||||
csv_row_t* csv_file_get_row(csv_file_t* csv, uint32_t row) {
|
||||
return_value_if_fail(csv != NULL, NULL);
|
||||
|
||||
return csv_rows_get(&(csv->rows), row);
|
||||
}
|
||||
|
||||
ret_t csv_file_insert_row(csv_file_t* csv, uint32_t row, const char* data) {
|
||||
csv_row_t* r = NULL;
|
||||
return_value_if_fail(csv != NULL && data != NULL, RET_BAD_PARAMS);
|
||||
|
||||
r = csv_rows_insert(&(csv->rows), row);
|
||||
return_value_if_fail(r != NULL, RET_OOM);
|
||||
|
||||
return csv_row_set_data(r, data, csv->sep);
|
||||
}
|
||||
|
||||
ret_t csv_file_append_row(csv_file_t* csv, const char* data) {
|
||||
csv_row_t* r = NULL;
|
||||
return_value_if_fail(csv != NULL && data != NULL, RET_BAD_PARAMS);
|
||||
|
||||
r = csv_rows_append(&(csv->rows));
|
||||
return_value_if_fail(r != NULL, RET_OOM);
|
||||
|
||||
return csv_row_set_data(r, data, csv->sep);
|
||||
}
|
||||
|
||||
ret_t csv_file_remove_row(csv_file_t* csv, uint32_t row) {
|
||||
return_value_if_fail(csv != NULL, RET_BAD_PARAMS);
|
||||
|
||||
return csv_rows_remove(&(csv->rows), row);
|
||||
}
|
||||
|
||||
ret_t csv_file_save(csv_file_t* csv, const char* filename) {
|
||||
str_t str;
|
||||
uint32_t i = 0;
|
||||
csv_row_t* r = NULL;
|
||||
fs_file_t* f = NULL;
|
||||
return_value_if_fail(csv != NULL, RET_BAD_PARAMS);
|
||||
filename = filename != NULL ? filename : csv->filename;
|
||||
return_value_if_fail(filename != NULL, RET_BAD_PARAMS);
|
||||
return_value_if_fail(str_init(&str, 512) != NULL, RET_OOM);
|
||||
|
||||
f = fs_open_file(os_fs(), filename, "wb+");
|
||||
if (f != NULL) {
|
||||
ENSURE(fs_file_write(f, s_utf8_bom, sizeof(s_utf8_bom)) == sizeof(s_utf8_bom));
|
||||
|
||||
for (i = 0; i < csv->rows.size; i++) {
|
||||
r = csv->rows.rows + i;
|
||||
csv_row_to_str(r, &str, csv->sep);
|
||||
ENSURE(fs_file_write(f, str.str, str.size) == str.size);
|
||||
}
|
||||
fs_file_close(f);
|
||||
|
||||
if (csv->filename != filename) {
|
||||
TKMEM_FREE(csv->filename);
|
||||
csv->filename = tk_strdup(filename);
|
||||
}
|
||||
}
|
||||
str_reset(&str);
|
||||
|
||||
return RET_NOT_IMPL;
|
||||
}
|
||||
|
||||
const char* csv_file_get_title(csv_file_t* csv) {
|
||||
csv_row_t* r = NULL;
|
||||
return_value_if_fail(csv != NULL && csv->has_title, NULL);
|
||||
|
||||
r = csv_file_get_row(csv, 0);
|
||||
return_value_if_fail(r != NULL, NULL);
|
||||
|
||||
return r->buff;
|
||||
}
|
||||
|
||||
ret_t csv_file_clear(csv_file_t* csv) {
|
||||
uint32_t i = 0;
|
||||
return_value_if_fail(csv != NULL, RET_BAD_PARAMS);
|
||||
|
||||
for (i = 0; i < csv->rows.size; i++) {
|
||||
csv_row_t* r = csv->rows.rows + i;
|
||||
csv_row_reset(r);
|
||||
}
|
||||
csv->rows.size = 0;
|
||||
|
||||
return RET_OK;
|
||||
}
|
||||
|
||||
ret_t csv_file_reset(csv_file_t* csv) {
|
||||
return_value_if_fail(csv != NULL, RET_BAD_PARAMS);
|
||||
|
||||
csv_file_clear(csv);
|
||||
TKMEM_FREE(csv->filename);
|
||||
TKMEM_FREE(csv->rows.rows);
|
||||
memset(csv, 0x00, sizeof(*csv));
|
||||
|
||||
return RET_OK;
|
||||
}
|
||||
|
||||
ret_t csv_file_destroy(csv_file_t* csv) {
|
||||
if (csv_file_reset(csv) == RET_OK) {
|
||||
TKMEM_FREE(csv);
|
||||
}
|
||||
|
||||
return RET_OK;
|
||||
}
|
||||
|
||||
ret_t csv_file_reload(csv_file_t* csv) {
|
||||
return_value_if_fail(csv != NULL && csv->filename != NULL, RET_BAD_PARAMS);
|
||||
|
||||
csv_file_clear(csv);
|
||||
|
||||
return (csv_file_load(csv) != NULL) ? RET_OK : RET_FAIL;
|
||||
}
|
||||
|
||||
ret_t csv_file_load_file(csv_file_t* csv, const char* filename) {
|
||||
return_value_if_fail(csv != NULL && filename != NULL, RET_BAD_PARAMS);
|
||||
|
||||
csv_file_clear(csv);
|
||||
csv->filename = tk_str_copy(csv->filename, filename);
|
||||
|
||||
return (csv_file_load(csv) != NULL) ? RET_OK : RET_FAIL;
|
||||
}
|
372
src/csv/csv_file.h
Normal file
372
src/csv/csv_file.h
Normal file
@ -0,0 +1,372 @@
|
||||
/**
|
||||
* File: csv_file.h
|
||||
* Author: AWTK Develop Team
|
||||
* Brief: csv file
|
||||
*
|
||||
* Copyright (c) 2020 - 2020 Guangzhou ZHIYUAN Electronics Co.,Ltd.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* License file for more details.
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* History:
|
||||
* ================================================================
|
||||
* 2020-06-08 Li XianJing <xianjimli@hotmail.com> created
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef TK_CSV_FILE_H
|
||||
#define TK_CSV_FILE_H
|
||||
|
||||
#include "tkc/istream.h"
|
||||
|
||||
BEGIN_C_DECLS
|
||||
|
||||
typedef struct _csv_row_t {
|
||||
char* buff;
|
||||
uint32_t size : 30;
|
||||
uint32_t checked : 1;
|
||||
uint32_t should_free_buff : 1;
|
||||
} csv_row_t;
|
||||
|
||||
typedef struct _csv_rows_t {
|
||||
csv_row_t* rows;
|
||||
uint32_t size;
|
||||
uint32_t capacity;
|
||||
} csv_rows_t;
|
||||
|
||||
struct _csv_file_t;
|
||||
typedef struct _csv_file_t csv_file_t;
|
||||
|
||||
/**
|
||||
* 返回值:
|
||||
* RET_OK: 保留
|
||||
* RET_STOP: 停止解析
|
||||
* RET_FAIL: 忽略此行
|
||||
*/
|
||||
typedef ret_t (*csv_file_filter_t)(void* ctx, csv_file_t* csv, uint32_t index, csv_row_t* row);
|
||||
|
||||
/**
|
||||
* @class csv_file_t
|
||||
* 操作CSV文件。
|
||||
*/
|
||||
struct _csv_file_t {
|
||||
/**
|
||||
* @property {bool_t} has_title
|
||||
* 是否有标题。
|
||||
*/
|
||||
bool_t has_title;
|
||||
|
||||
/*private*/
|
||||
char sep;
|
||||
uint32_t cols;
|
||||
char* filename;
|
||||
csv_rows_t rows;
|
||||
void* filter_ctx;
|
||||
csv_file_filter_t filter;
|
||||
};
|
||||
|
||||
/**
|
||||
* @method csv_file_create_empty
|
||||
*
|
||||
* 创建空的csv对象。
|
||||
*
|
||||
* @param {char} sep 分隔符。
|
||||
* @param {csv_file_filter_t} filter 过滤函数。
|
||||
* @param {void*} ctx 过滤函数的上下文。
|
||||
*
|
||||
* @return {csv_file_t*} 返回csv对象。
|
||||
*/
|
||||
csv_file_t* csv_file_create_empty(char sep, csv_file_filter_t filter, void* ctx);
|
||||
|
||||
/**
|
||||
* @method csv_file_set_filter
|
||||
*
|
||||
* 设置过滤函数。
|
||||
*
|
||||
* @param {csv_file_t*} csv csv对象。
|
||||
* @param {csv_file_filter_t} filter 过滤函数。
|
||||
* @param {void*} ctx 过滤函数的上下文。
|
||||
*
|
||||
* @return {ret_t} 返回RET_OK表示成功,否则表示失败。
|
||||
*/
|
||||
ret_t csv_file_set_filter(csv_file_t* csv, csv_file_filter_t filter, void* ctx);
|
||||
|
||||
/**
|
||||
* @method csv_file_create
|
||||
*
|
||||
* 根据文件创建csv对象。
|
||||
*
|
||||
* @param {const char*} filename 文件名。
|
||||
* @param {char} sep 分隔符。
|
||||
*
|
||||
* @return {csv_file_t*} 返回csv对象。
|
||||
*/
|
||||
csv_file_t* csv_file_create(const char* filename, char sep);
|
||||
|
||||
/**
|
||||
* @method csv_file_create_with_buff
|
||||
*
|
||||
* 根据buff创建csv对象。
|
||||
*
|
||||
* @param {const char*} buff 数据。
|
||||
* @param {uint32_t} size 数据长度。
|
||||
* @param {char} sep 分隔符。
|
||||
*
|
||||
* @return {csv_file_t} 返回csv对象。
|
||||
*/
|
||||
csv_file_t* csv_file_create_with_buff(const char* buff, uint32_t size, char sep);
|
||||
|
||||
/**
|
||||
* @method csv_file_load_file
|
||||
*
|
||||
* 从文件加载csv。
|
||||
*
|
||||
* @param {csv_file_t*} csv csv对象。
|
||||
* @param {const char*} filename 文件名。
|
||||
*
|
||||
* @return {ret_t} 返回RET_OK表示成功,否则表示失败。
|
||||
*/
|
||||
ret_t csv_file_load_file(csv_file_t* csv, const char* filename);
|
||||
|
||||
/**
|
||||
* @method csv_file_load_buff
|
||||
*
|
||||
* 从内存加载csv。
|
||||
*
|
||||
* @param {csv_file_t*} csv csv对象。
|
||||
* @param {const char*} buff 数据。
|
||||
* @param {uint32_t} size 数据长度。
|
||||
*
|
||||
* @return {ret_t} 返回RET_OK表示成功,否则表示失败。
|
||||
*/
|
||||
ret_t csv_file_load_buff(csv_file_t* csv, const char* buff, uint32_t size);
|
||||
|
||||
/**
|
||||
* @method csv_file_reload
|
||||
*
|
||||
* 丢弃内存中的修改,重新加载当前文件。
|
||||
*
|
||||
* @param {csv_file_t*} csv csv对象。
|
||||
*
|
||||
* @return {ret_t} 返回RET_OK表示成功,否则表示失败。
|
||||
*/
|
||||
ret_t csv_file_reload(csv_file_t* csv);
|
||||
|
||||
/**
|
||||
* @method csv_file_get
|
||||
*
|
||||
* 获取指定行列的数据。
|
||||
*
|
||||
* @param {csv_file_t*} csv csv对象。
|
||||
* @param {uint32_t} row 行号。
|
||||
* @param {uint32_t} col 列号。
|
||||
*
|
||||
* @return {const char*} 返回数据。
|
||||
*/
|
||||
const char* csv_file_get(csv_file_t* csv, uint32_t row, uint32_t col);
|
||||
|
||||
/**
|
||||
* @method csv_file_set
|
||||
*
|
||||
* 修改指定行列的数据。
|
||||
*
|
||||
* @param {csv_file_t*} csv csv对象。
|
||||
* @param {uint32_t} row 行号。
|
||||
* @param {uint32_t} col 列号。
|
||||
* @param {const char*} value 值。
|
||||
*
|
||||
* @return {ret_t} 返回RET_OK表示成功,否则表示失败。
|
||||
*/
|
||||
ret_t csv_file_set(csv_file_t* csv, uint32_t row, uint32_t col, const char* value);
|
||||
|
||||
/**
|
||||
* @method csv_file_set_row_checked
|
||||
*
|
||||
* 勾选指定行。
|
||||
*
|
||||
* @param {csv_file_t*} csv csv对象。
|
||||
* @param {uint32_t} row 行号。
|
||||
* @param {bool_t} checked 是否勾选。
|
||||
*
|
||||
* @return {ret_t} 返回RET_OK表示成功,否则表示失败。
|
||||
*/
|
||||
ret_t csv_file_set_row_checked(csv_file_t* csv, uint32_t row, bool_t checked);
|
||||
|
||||
/**
|
||||
* @method csv_file_is_row_checked
|
||||
*
|
||||
* 判断指定行是否勾选。
|
||||
*
|
||||
* @param {csv_file_t*} csv csv对象。
|
||||
* @param {uint32_t} row 行号。
|
||||
*
|
||||
* @return {bool_t} 返回TRUE表示勾选,否则表示没勾选。
|
||||
*/
|
||||
bool_t csv_file_is_row_checked(csv_file_t* csv, uint32_t row);
|
||||
|
||||
/**
|
||||
* @method csv_file_remove_checked_rows
|
||||
*
|
||||
* 删除全部勾选的行。
|
||||
*
|
||||
* @param {csv_file_t*} csv csv对象。
|
||||
*
|
||||
* @return {ret_t} 返回RET_OK表示成功,否则表示失败。
|
||||
*/
|
||||
ret_t csv_file_remove_checked_rows(csv_file_t* csv);
|
||||
|
||||
/**
|
||||
* @method csv_file_get_title
|
||||
*
|
||||
* 获取标题(不存在则返回NULL)。
|
||||
*
|
||||
* @param {csv_file_t*} csv csv对象。
|
||||
*
|
||||
* @return {const char*} 返回标题。
|
||||
*/
|
||||
const char* csv_file_get_title(csv_file_t* csv);
|
||||
|
||||
/**
|
||||
* @method csv_file_get_rows
|
||||
*
|
||||
* 获取行数(包括标题)。
|
||||
*
|
||||
* @param {csv_file_t*} csv csv对象。
|
||||
*
|
||||
* @return {uint32_t} 返回行数。
|
||||
*/
|
||||
uint32_t csv_file_get_rows(csv_file_t* csv);
|
||||
|
||||
/**
|
||||
* @method csv_file_get_checked_rows
|
||||
*
|
||||
* 获取checked行数(包括标题)。
|
||||
*
|
||||
* @param {csv_file_t*} csv csv对象。
|
||||
*
|
||||
* @return {uint32_t} 返回checked行数。
|
||||
*/
|
||||
uint32_t csv_file_get_checked_rows(csv_file_t* csv);
|
||||
|
||||
/**
|
||||
* @method csv_file_get_cols
|
||||
*
|
||||
* 获取列数。
|
||||
*
|
||||
* @param {csv_file_t*} csv csv对象。
|
||||
*
|
||||
* @return {uint32_t} 返回列数。
|
||||
*/
|
||||
uint32_t csv_file_get_cols(csv_file_t* csv);
|
||||
|
||||
/**
|
||||
* @method csv_file_remove_row
|
||||
*
|
||||
* 删除指定行。
|
||||
*
|
||||
* @param {csv_file_t*} csv csv对象。
|
||||
* @param {uint32_t} row 行号。
|
||||
*
|
||||
* @return {ret_t} 返回RET_OK表示成功,否则表示失败。
|
||||
*/
|
||||
ret_t csv_file_remove_row(csv_file_t* csv, uint32_t row);
|
||||
|
||||
/**
|
||||
* @method csv_file_append_row
|
||||
*
|
||||
* 追加一行。
|
||||
*
|
||||
* @param {csv_file_t*} csv csv对象。
|
||||
* @param {const char*} data 数据。
|
||||
*
|
||||
* @return {ret_t} 返回RET_OK表示成功,否则表示失败。
|
||||
*/
|
||||
ret_t csv_file_append_row(csv_file_t* csv, const char* data);
|
||||
|
||||
/**
|
||||
* @method csv_file_insert_row
|
||||
*
|
||||
* 插入一行。
|
||||
*
|
||||
* @param {csv_file_t*} csv csv对象。
|
||||
* @param {uint32_t} row 行号。
|
||||
* @param {const char*} data 数据。
|
||||
*
|
||||
* @return {ret_t} 返回RET_OK表示成功,否则表示失败。
|
||||
*/
|
||||
ret_t csv_file_insert_row(csv_file_t* csv, uint32_t row, const char* data);
|
||||
|
||||
/**
|
||||
* @method csv_file_save
|
||||
*
|
||||
* 保存。
|
||||
*
|
||||
* @param {csv_file_t*} csv csv对象。
|
||||
* @param {const char*} filename 文件名。
|
||||
*
|
||||
* @return {ret_t} 返回RET_OK表示成功,否则表示失败。
|
||||
*/
|
||||
ret_t csv_file_save(csv_file_t* csv, const char* filename);
|
||||
|
||||
/**
|
||||
* @method csv_file_clear
|
||||
*
|
||||
* 保存。
|
||||
*
|
||||
* @param {csv_file_t*} csv csv对象。
|
||||
* @param {const char*} filename 文件名。
|
||||
*
|
||||
* @return {ret_t} 返回RET_OK表示成功,否则表示失败。
|
||||
*/
|
||||
ret_t csv_file_clear(csv_file_t* csv);
|
||||
|
||||
/**
|
||||
* @method csv_file_load_file
|
||||
*
|
||||
* 保存。
|
||||
*
|
||||
* @param {csv_file_t*} csv csv对象。
|
||||
* @param {const char*} filename 文件名。
|
||||
*
|
||||
* @return {ret_t} 返回RET_OK表示成功,否则表示失败。
|
||||
*/
|
||||
ret_t csv_file_load_file(csv_file_t* csv, const char* filename);
|
||||
|
||||
/**
|
||||
* @method csv_file_destroy
|
||||
*
|
||||
* 销毁csv对象。
|
||||
*
|
||||
* @param {csv_file_t*} csv csv对象。
|
||||
*
|
||||
* @return {ret_t} 返回RET_OK表示成功,否则表示失败。
|
||||
*/
|
||||
ret_t csv_file_destroy(csv_file_t* csv);
|
||||
|
||||
/*public for test*/
|
||||
const char* csv_file_get_by_name(csv_file_t* csv, uint32_t row, const char* name);
|
||||
int32_t csv_file_get_col_of_name(csv_file_t* csv, const char* name);
|
||||
csv_row_t* csv_file_get_row(csv_file_t* csv, uint32_t row);
|
||||
|
||||
uint32_t csv_row_count_cols(csv_row_t* row);
|
||||
const char* csv_row_get(csv_row_t* row, uint32_t col);
|
||||
ret_t csv_row_set(csv_row_t* row, uint32_t col, const char* value);
|
||||
ret_t csv_row_init(csv_row_t* row, char* buff, uint32_t size, bool_t should_free_buff);
|
||||
ret_t csv_row_reset(csv_row_t* row);
|
||||
|
||||
ret_t csv_rows_init(csv_rows_t* rows, uint32_t init_capacity);
|
||||
ret_t csv_rows_remove(csv_rows_t* rows, uint32_t row);
|
||||
csv_row_t* csv_rows_append(csv_rows_t* rows);
|
||||
csv_row_t* csv_rows_insert(csv_rows_t* rows, uint32_t row);
|
||||
csv_row_t* csv_rows_get(csv_rows_t* rows, uint32_t row);
|
||||
ret_t csv_rows_reset(csv_rows_t* rows);
|
||||
ret_t csv_row_set_data(csv_row_t* row, const char* data, char sep);
|
||||
|
||||
END_C_DECLS
|
||||
|
||||
#endif /*TK_CSV_FILE_H*/
|
240
src/csv/csv_file_object.c
Normal file
240
src/csv/csv_file_object.c
Normal file
@ -0,0 +1,240 @@
|
||||
/**
|
||||
* File: csv_file_object.h
|
||||
* Author: AWTK Develop Team
|
||||
* Brief: csv file object
|
||||
*
|
||||
* Copyright (c) 2020 - 2020 Guangzhou ZHIYUAN Electronics Co.,Ltd.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* License file for more details.
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* History:
|
||||
* ================================================================
|
||||
* 2020-07-19 Li XianJing <xianjimli@hotmail.com> created
|
||||
*
|
||||
*/
|
||||
|
||||
#include "tkc/str.h"
|
||||
#include "tkc/mem.h"
|
||||
#include "tkc/utils.h"
|
||||
#include "tkc/object.h"
|
||||
#include "csv_file.h"
|
||||
|
||||
typedef struct _csv_file_object_t {
|
||||
object_t object;
|
||||
|
||||
/*private*/
|
||||
csv_file_t* csv;
|
||||
str_t str;
|
||||
} csv_file_object_t;
|
||||
|
||||
static csv_file_object_t* csv_file_object_cast(object_t* obj);
|
||||
#define CSV_FILE_OBJECT(obj) csv_file_object_cast((object_t*)obj)
|
||||
|
||||
typedef struct _csv_path_t {
|
||||
int32_t row;
|
||||
int32_t col;
|
||||
const char* col_name;
|
||||
} csv_path_t;
|
||||
|
||||
static ret_t csv_path_parse(csv_path_t* path, csv_file_t* csv, const char* name) {
|
||||
const char* p = name;
|
||||
memset(path, 0x00, sizeof(*path));
|
||||
|
||||
while (*p && *p != '[') p++;
|
||||
return_value_if_fail(*p == '[', RET_FAIL);
|
||||
|
||||
p++;
|
||||
path->row = tk_atoi(p);
|
||||
p = strchr(p, '.');
|
||||
if (p == NULL) {
|
||||
return RET_OK;
|
||||
} else {
|
||||
p++;
|
||||
}
|
||||
|
||||
if (tk_str_eq(p, OBJECT_PROP_CHECKED)) {
|
||||
path->col_name = p;
|
||||
|
||||
return RET_OK;
|
||||
}
|
||||
|
||||
if (*p == '[') {
|
||||
path->col = tk_atoi(p + 1);
|
||||
} else {
|
||||
path->col = csv_file_get_col_of_name(csv, p);
|
||||
if (path->col < 0) {
|
||||
path->col = tk_atoi(p);
|
||||
}
|
||||
}
|
||||
return_value_if_fail((path->col >= 0) && (path->col < csv_file_get_cols(csv)), RET_BAD_PARAMS);
|
||||
return_value_if_fail((path->row >= 0) && (path->row < csv_file_get_rows(csv)), RET_BAD_PARAMS);
|
||||
|
||||
return RET_OK;
|
||||
}
|
||||
|
||||
static ret_t csv_file_object_remove_prop(object_t* obj, const char* name) {
|
||||
csv_path_t p;
|
||||
csv_file_object_t* o = CSV_FILE_OBJECT(obj);
|
||||
return_value_if_fail(o != NULL, RET_BAD_PARAMS);
|
||||
return_value_if_fail(csv_path_parse(&p, o->csv, name) == RET_OK, RET_FAIL);
|
||||
|
||||
return csv_file_remove_row(o->csv, p.row);
|
||||
}
|
||||
|
||||
static ret_t csv_file_object_set_prop(object_t* obj, const char* name, const value_t* v) {
|
||||
csv_path_t p;
|
||||
csv_file_object_t* o = CSV_FILE_OBJECT(obj);
|
||||
return_value_if_fail(o != NULL, RET_BAD_PARAMS);
|
||||
return_value_if_fail(csv_path_parse(&p, o->csv, name) == RET_OK, RET_FAIL);
|
||||
|
||||
if (p.col_name != NULL && tk_str_ieq(p.col_name, OBJECT_PROP_CHECKED)) {
|
||||
return csv_file_set_row_checked(o->csv, p.row, value_bool(v));
|
||||
}
|
||||
|
||||
str_from_value(&(o->str), v);
|
||||
return csv_file_set(o->csv, p.row, p.col, o->str.str);
|
||||
}
|
||||
|
||||
static ret_t csv_file_object_get_prop(object_t* obj, const char* name, value_t* v) {
|
||||
csv_path_t p;
|
||||
const char* str = NULL;
|
||||
csv_file_object_t* o = CSV_FILE_OBJECT(obj);
|
||||
return_value_if_fail(o != NULL, RET_BAD_PARAMS);
|
||||
|
||||
if (tk_str_ieq(name, OBJECT_PROP_SIZE)) {
|
||||
value_set_int(v, csv_file_get_rows(o->csv));
|
||||
return RET_OK;
|
||||
}
|
||||
|
||||
return_value_if_fail(csv_path_parse(&p, o->csv, name) == RET_OK, RET_FAIL);
|
||||
|
||||
if (p.col_name != NULL && tk_str_ieq(p.col_name, OBJECT_PROP_CHECKED)) {
|
||||
return_value_if_fail(p.row < csv_file_get_rows(o->csv), RET_FAIL);
|
||||
|
||||
value_set_bool(v, csv_file_is_row_checked(o->csv, p.row));
|
||||
return RET_OK;
|
||||
}
|
||||
|
||||
value_set_str(v, "");
|
||||
str = csv_file_get(o->csv, p.row, p.col);
|
||||
return_value_if_fail(str != NULL, RET_FAIL);
|
||||
value_set_str(v, str);
|
||||
|
||||
return RET_OK;
|
||||
}
|
||||
|
||||
static bool_t csv_file_object_can_exec(object_t* obj, const char* name, const char* args) {
|
||||
csv_file_object_t* o = CSV_FILE_OBJECT(obj);
|
||||
return_value_if_fail(o != NULL, RET_BAD_PARAMS);
|
||||
|
||||
if (tk_str_ieq(name, OBJECT_CMD_SAVE)) {
|
||||
return TRUE;
|
||||
} else if (tk_str_ieq(name, OBJECT_CMD_RELOAD)) {
|
||||
return TRUE;
|
||||
} else if (tk_str_ieq(name, OBJECT_CMD_CLEAR)) {
|
||||
return TRUE;
|
||||
} else if (tk_str_ieq(name, OBJECT_CMD_REMOVE)) {
|
||||
return TRUE;
|
||||
} else if (tk_str_ieq(name, OBJECT_CMD_REMOVE_CHECKED)) {
|
||||
return csv_file_get_checked_rows(o->csv) > 0;
|
||||
} else if (tk_str_ieq(name, OBJECT_CMD_ADD)) {
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static ret_t csv_file_object_exec(object_t* obj, const char* name, const char* args) {
|
||||
ret_t ret = RET_NOT_IMPL;
|
||||
csv_file_object_t* o = CSV_FILE_OBJECT(obj);
|
||||
return_value_if_fail(o != NULL, RET_BAD_PARAMS);
|
||||
|
||||
if (tk_str_ieq(name, OBJECT_CMD_SAVE)) {
|
||||
ret = csv_file_save(o->csv, NULL);
|
||||
} else if (tk_str_ieq(name, OBJECT_CMD_RELOAD)) {
|
||||
csv_file_reload(o->csv);
|
||||
ret = RET_ITEMS_CHANGED;
|
||||
} else if (tk_str_ieq(name, OBJECT_CMD_CLEAR)) {
|
||||
csv_file_clear(o->csv);
|
||||
ret = RET_ITEMS_CHANGED;
|
||||
} else if (tk_str_ieq(name, OBJECT_CMD_REMOVE)) {
|
||||
const char* index = strrchr(args, '[');
|
||||
if (index != NULL) {
|
||||
index++;
|
||||
} else {
|
||||
index = args;
|
||||
}
|
||||
return_value_if_fail(index != NULL, RET_FAIL);
|
||||
ret = csv_file_remove_row(o->csv, tk_atoi(index)) == RET_OK ? RET_ITEMS_CHANGED : RET_FAIL;
|
||||
} else if (tk_str_ieq(name, OBJECT_CMD_REMOVE_CHECKED)) {
|
||||
ret = csv_file_remove_checked_rows(o->csv) == RET_OK ? RET_ITEMS_CHANGED : RET_FAIL;
|
||||
} else if (tk_str_ieq(name, OBJECT_CMD_ADD)) {
|
||||
return_value_if_fail(args != NULL, RET_FAIL);
|
||||
ret = csv_file_append_row(o->csv, args) == RET_OK ? RET_ITEMS_CHANGED : RET_FAIL;
|
||||
} else {
|
||||
return RET_NOT_IMPL;
|
||||
}
|
||||
|
||||
if (ret == RET_ITEMS_CHANGED) {
|
||||
emitter_dispatch_simple_event(EMITTER(obj), EVT_PROPS_CHANGED);
|
||||
emitter_dispatch_simple_event(EMITTER(obj), EVT_ITEMS_CHANGED);
|
||||
}
|
||||
|
||||
return RET_OK;
|
||||
}
|
||||
|
||||
static ret_t csv_file_object_destroy(object_t* obj) {
|
||||
csv_file_object_t* o = CSV_FILE_OBJECT(obj);
|
||||
return_value_if_fail(o != NULL, RET_BAD_PARAMS);
|
||||
|
||||
csv_file_destroy(o->csv);
|
||||
o->csv = NULL;
|
||||
str_reset(&(o->str));
|
||||
|
||||
return RET_OK;
|
||||
}
|
||||
|
||||
static const object_vtable_t s_csv_file_object_vtable = {.type = "csv_file_object",
|
||||
.desc = "csv_file_object",
|
||||
.size = sizeof(csv_file_object_t),
|
||||
.is_collection = TRUE,
|
||||
.exec = csv_file_object_exec,
|
||||
.can_exec = csv_file_object_can_exec,
|
||||
.remove_prop = csv_file_object_remove_prop,
|
||||
.get_prop = csv_file_object_get_prop,
|
||||
.set_prop = csv_file_object_set_prop,
|
||||
.on_destroy = csv_file_object_destroy};
|
||||
|
||||
static csv_file_object_t* csv_file_object_cast(object_t* obj) {
|
||||
return_value_if_fail(obj != NULL && obj->vt == &s_csv_file_object_vtable, NULL);
|
||||
|
||||
return (csv_file_object_t*)obj;
|
||||
}
|
||||
|
||||
object_t* csv_file_object_create(csv_file_t* csv) {
|
||||
object_t* obj = NULL;
|
||||
csv_file_object_t* o = NULL;
|
||||
return_value_if_fail(csv != NULL, NULL);
|
||||
|
||||
obj = object_create(&s_csv_file_object_vtable);
|
||||
o = CSV_FILE_OBJECT(obj);
|
||||
return_value_if_fail(o != NULL, NULL);
|
||||
|
||||
o->csv = csv;
|
||||
str_init(&(o->str), 0);
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
csv_file_t* csv_file_object_get_csv(object_t* obj) {
|
||||
csv_file_object_t* o = CSV_FILE_OBJECT(obj);
|
||||
return_value_if_fail(o != NULL, NULL);
|
||||
|
||||
return o->csv;
|
||||
}
|
61
src/csv/csv_file_object.h
Normal file
61
src/csv/csv_file_object.h
Normal file
@ -0,0 +1,61 @@
|
||||
/**
|
||||
* File: object_csv_file.h
|
||||
* Author: AWTK Develop Team
|
||||
* Brief: csv file object
|
||||
*
|
||||
* Copyright (c) 2020 - 2020 Guangzhou ZHIYUAN Electronics Co.,Ltd.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* License file for more details.
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* History:
|
||||
* ================================================================
|
||||
* 2020-07-19 Li XianJing <xianjimli@hotmail.com> created
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef TK_CSV_FILE_OBJECT_H
|
||||
#define TK_CSV_FILE_OBJECT_H
|
||||
|
||||
#include "tkc/object.h"
|
||||
#include "csv_file.h"
|
||||
|
||||
BEGIN_C_DECLS
|
||||
|
||||
/**
|
||||
* @class csv_file_object_t
|
||||
* @parent object_t
|
||||
* @annotation["fake"]
|
||||
* 将cvs file包装成object对象。
|
||||
*/
|
||||
|
||||
/**
|
||||
* @method csv_file_object_create
|
||||
*
|
||||
* 将csv_file对象包装成object。
|
||||
*
|
||||
* @param {csv_file_t*} csv csv对象(由object释放)。
|
||||
*
|
||||
* @return {object_t*} 返回对象。
|
||||
*/
|
||||
object_t* csv_file_object_create(csv_file_t* csv);
|
||||
|
||||
/**
|
||||
* @method csv_file_object_get_csv
|
||||
*
|
||||
* 获取csv对象。
|
||||
*
|
||||
* @param {object_t*} obj obj对象。
|
||||
*
|
||||
* @return {csv_file_t*} 返回csv对象。
|
||||
*/
|
||||
csv_file_t* csv_file_object_get_csv(object_t* obj);
|
||||
|
||||
END_C_DECLS
|
||||
|
||||
#endif /*TK_CSV_FILE_OBJECT_H*/
|
2
src/xml/README.md
Normal file
2
src/xml/README.md
Normal file
@ -0,0 +1,2 @@
|
||||
# xml 解析器
|
||||
|
@ -11,6 +11,7 @@ if(process.argv.length == 3) {
|
||||
let sourcesPath = [
|
||||
path.normalize(path.join(__dirname, '../../src/tkc')),
|
||||
path.normalize(path.join(__dirname, '../../src/ubjson')),
|
||||
path.normalize(path.join(__dirname, '../../src/csv')),
|
||||
path.normalize(path.join(__dirname, '../../src/streams')),
|
||||
path.normalize(path.join(__dirname, '../../src/conf_io'))].join(';');
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user