aboutsummaryrefslogtreecommitdiff
path: root/core/encoding/csv
diff options
context:
space:
mode:
authorJeroen van Rijn <Kelimion@users.noreply.github.com>2024-05-24 13:26:12 +0200
committerJeroen van Rijn <Kelimion@users.noreply.github.com>2024-05-24 13:26:12 +0200
commit3b739dc5ccf1ddb29b53a86155e5671b5be00c54 (patch)
treec53e7ac13d7a42d261c532dbb7741982e683cb36 /core/encoding/csv
parent7dc1f114b96b9e25dfc2537ab153fe655e65affc (diff)
Add iterator_next(&r) to CSV.
Diffstat (limited to 'core/encoding/csv')
-rw-r--r--core/encoding/csv/example.odin89
-rw-r--r--core/encoding/csv/reader.odin25
2 files changed, 112 insertions, 2 deletions
diff --git a/core/encoding/csv/example.odin b/core/encoding/csv/example.odin
new file mode 100644
index 000000000..1484dd940
--- /dev/null
+++ b/core/encoding/csv/example.odin
@@ -0,0 +1,89 @@
+//+build ignore
+package encoding_csv
+
+import "core:fmt"
+import "core:encoding/csv"
+import "core:os"
+import "core:mem"
+
+// Requires keeping the entire CSV file in memory at once
+iterate_csv_from_string :: proc(filename: string) {
+ r: csv.Reader
+ r.trim_leading_space = true
+ r.reuse_record = true // Without it you have to delete(record)
+ r.reuse_record_buffer = true // Without it you have to each of the fields within it
+ defer csv.reader_destroy(&r)
+
+ if csv_data, ok := os.read_entire_file(filename); ok {
+ csv.reader_init_with_string(&r, string(csv_data))
+ defer delete(csv_data)
+ } else {
+ fmt.printfln("Unable to open file: %v", filename)
+ return
+ }
+
+ for r, i, err in csv.iterator_next(&r) {
+ if err != nil { /* Do something with error */ }
+ for f, j in r {
+ fmt.printfln("Record %v, field %v: %q", i, j, f)
+ }
+ }
+}
+
+// Reads the CSV as it's processed (with a small buffer)
+iterate_csv_from_stream :: proc(filename: string) {
+ fmt.printfln("Hellope from %v", filename)
+ r: csv.Reader
+ r.trim_leading_space = true
+ r.reuse_record = true // Without it you have to delete(record)
+ r.reuse_record_buffer = true // Without it you have to each of the fields within it
+ defer csv.reader_destroy(&r)
+
+ handle, errno := os.open(filename)
+ if errno != os.ERROR_NONE {
+ fmt.printfln("Error opening file: %v", filename)
+ return
+ }
+ defer os.close(handle)
+ csv.reader_init(&r, os.stream_from_handle(handle))
+
+ for r, i in csv.iterator_next(&r) {
+ for f, j in r {
+ fmt.printfln("Record %v, field %v: %q", i, j, f)
+ }
+ }
+ fmt.printfln("Error: %v", csv.iterator_last_error(r))
+}
+
+// Read all records at once
+read_csv_from_string :: proc(filename: string) {
+ r: csv.Reader
+ r.trim_leading_space = true
+ r.reuse_record = true // Without it you have to delete(record)
+ r.reuse_record_buffer = true // Without it you have to each of the fields within it
+ defer csv.reader_destroy(&r)
+
+ if csv_data, ok := os.read_entire_file(filename); ok {
+ csv.reader_init_with_string(&r, string(csv_data))
+ defer delete(csv_data)
+ } else {
+ fmt.printfln("Unable to open file: %v", filename)
+ return
+ }
+
+ records, err := csv.read_all(&r)
+ if err != nil { /* Do something with CSV parse error */ }
+
+ defer {
+ for rec in records {
+ delete(rec)
+ }
+ delete(records)
+ }
+
+ for r, i in records {
+ for f, j in r {
+ fmt.printfln("Record %v, field %v: %q", i, j, f)
+ }
+ }
+} \ No newline at end of file
diff --git a/core/encoding/csv/reader.odin b/core/encoding/csv/reader.odin
index f8c72c423..200bf43ea 100644
--- a/core/encoding/csv/reader.odin
+++ b/core/encoding/csv/reader.odin
@@ -57,6 +57,9 @@ Reader :: struct {
field_indices: [dynamic]int,
last_record: [dynamic]string,
sr: strings.Reader, // used by reader_init_with_string
+
+ // Set and used by the iterator. Query using `iterator_last_error`
+ last_iterator_error: Error,
}
@@ -121,6 +124,25 @@ reader_destroy :: proc(r: ^Reader) {
bufio.reader_destroy(&r.r)
}
+/*
+ Returns a record at a time.
+
+ for record, row_idx in csv.iterator_next(&r) { ... }
+
+ TIP: If you process the results within the loop and don't need to own the results,
+ you can set the Reader's `reuse_record` and `reuse_record_reuse_record_buffer` to true;
+ you won't need to delete the record or its fields.
+*/
+iterator_next :: proc(r: ^Reader) -> (record: []string, idx: int, err: Error, more: bool) {
+ record, r.last_iterator_error = read(r)
+ return record, r.line_count - 1, r.last_iterator_error, r.last_iterator_error == nil
+}
+
+// Get last error if we the iterator
+iterator_last_error :: proc(r: Reader) -> (err: Error) {
+ return r.last_iterator_error
+}
+
// read reads a single record (a slice of fields) from r
//
// All \r\n sequences are normalized to \n, including multi-line field
@@ -460,5 +482,4 @@ _read_record :: proc(r: ^Reader, dst: ^[dynamic]string, allocator := context.all
r.fields_per_record = len(dst)
}
return dst[:], err
-
-}
+} \ No newline at end of file