diff options
| author | zhibog <zhibog-github@web.de> | 2019-11-01 22:35:46 +0100 |
|---|---|---|
| committer | zhibog <zhibog-github@web.de> | 2019-11-01 22:35:46 +0100 |
| commit | 4b718aae7556511e7461faf89b147a3416ea8f8e (patch) | |
| tree | 856b3f30b8ceed8d23931eca16dc8e58fbf33fc6 /core/encoding | |
| parent | 20db0e7f09f26884fb53c6872fc7633c48b94b4c (diff) | |
Added an implementation for reading and writing csv files
Diffstat (limited to 'core/encoding')
| -rw-r--r-- | core/encoding/csv/csv.odin | 106 |
1 files changed, 106 insertions, 0 deletions
diff --git a/core/encoding/csv/csv.odin b/core/encoding/csv/csv.odin new file mode 100644 index 000000000..a8f387e69 --- /dev/null +++ b/core/encoding/csv/csv.odin @@ -0,0 +1,106 @@ +package csv
+
+// @note(zh): Encoding utility for csv files
+// You may use the provided struct to build your csv dynamically.
+// If you have a string with the whole content already, just use write_raw.
+// You are able to read the csv with the headers included in the data or omitted by providing
+// a bool parameter to the proc as shown down below.
+// Example useage:
+/*
+import "core:fmt"
+main :: proc() {
+ ctx: CSV;
+ ctx.data = {{"Col 1", "Col 2", "Col 3"}, {"aaa", "bbb", "ccc"}, {"ddd", "eee", "fff"}, {"ggg", "hhh", "iii"}};
+ ctx.line_ending = CRLF;
+ file_name := "test.csv";
+
+ // Write file and read with the headers omitted
+ if isOkWrite := write(file_name, &ctx); isOkWrite {
+ if content, col_count, isOkRead := read(file_name, DELIMITER, true); isOkRead {
+ fmt.println("Column count(no headers): ", col_count);
+ fmt.println(content);
+ }
+ }
+
+ // Write file and read with the headers being read as well
+ if isOkWrite := write(file_name, &ctx); isOkWrite {
+ if content, col_count, isOkRead := read(file_name); isOkRead {
+ fmt.println("Column count(with headers): ", col_count);
+ fmt.println(content);
+ }
+ }
+}
+*/
+
+import "core:os"
+import "core:strings"
+
+CSV :: struct {
+ data: [][]string,
+ line_ending: string,
+ delimiter: string,
+};
+
+LF :: "\n";
+CRLF :: "\r\n";
+DELIMITER :: ",";
+
+write :: proc(path: string, ctx: ^CSV) -> bool {
+ b := strings.make_builder();
+ defer strings.destroy_builder(&b);
+
+ if ctx.line_ending == "" do ctx.line_ending = LF;
+ if ctx.delimiter == "" do ctx.delimiter = DELIMITER;
+
+ for row in ctx.data {
+ for col, i in row {
+ strings.write_string(&b, col);
+ if i + 1 < len(row) do strings.write_string(&b, ctx.delimiter);
+ }
+ strings.write_string(&b, ctx.line_ending);
+ }
+ return write_raw(path, b.buf[:]);
+}
+
+write_raw :: proc(path: string, data: []byte) -> bool {
+ file, err := os.open(path, os.O_RDWR | os.O_CREATE | os.O_TRUNC);
+ if err != os.ERROR_NONE do return false;
+ defer os.close(file);
+
+ if _, err := os.write(file, data); err != os.ERROR_NONE do return false;
+ return true;
+}
+
+read :: proc(path: string, delimiter := DELIMITER, skip_header := false) -> ([]string, int, bool) {
+ if bytes, isOk := os.read_entire_file(path); isOk {
+ cols: [dynamic]string;
+ defer delete(cols);
+ out: [dynamic]string;
+ col_count := 0;
+ prev_index := 0;
+ for i := 0; i < len(bytes); i += 1 {
+ if bytes[i] == '\n' {
+ append(&cols, string(bytes[prev_index:i]));
+ i += 1;
+ prev_index = i;
+ col_count += 1;
+ } else if bytes[i] == '\r' {
+ if bytes[i + 1] == '\n' {
+ append(&cols, string(bytes[prev_index:i]));
+ i += 2;
+ prev_index = i;
+ col_count += 1;
+ } else {
+ append(&cols, string(bytes[prev_index:i]));
+ i += 1;
+ prev_index = i;
+ col_count += 1;
+ }
+ }
+ }
+ for col in cols do append(&out, ..strings.split(col, delimiter));
+ if skip_header do return out[col_count:], col_count - 1, true;
+ else do return out[:], col_count, true;
+ }
+ return nil, -1, false;
+}
\ No newline at end of file |