Pārlūkot izejas kodu

Add iterator_next(&r) to CSV.

Jeroen van Rijn 1 gadu atpakaļ
vecāks
revīzija
3b739dc5cc
2 mainītis faili ar 112 papildinājumiem un 2 dzēšanām
  1. 89 0
      core/encoding/csv/example.odin
  2. 23 2
      core/encoding/csv/reader.odin

+ 89 - 0
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)
+		}
+	}
+}

+ 23 - 2
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
-
-}
+}