csv

package
v0.3.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 14, 2026 License: MIT Imports: 10 Imported by: 0

Documentation

Overview

Package csv reads and writes graphs as edge lists in CSV format.

The format is a simple table of columns: source, destination, and (optionally) a weight. Lines beginning with the comment character (default '#') are skipped. A header row may declare the column types; without it the reader assumes a fixed (src, dst[, weight]) layout.

Index

Examples

Constants

View Source
const DefaultMaxBytes int64 = 128 << 20 // 128 MiB

DefaultMaxBytes is the default ceiling, in bytes, on the amount of input a reader will consume before failing with ErrInputTooLarge. It guards against memory exhaustion from untrusted files (a crafted multi-gigabyte field, for example). A value of zero or less disables the cap; see Options.MaxBytes.

Peak memory

The cap bounds the number of bytes drawn from the reader, but encoding/csv does not bound the size of a single field. A hostile input — for example an unterminated quoted field — is buffered by the decoder up to MaxBytes, and the decoder's working set (raw buffer plus the parsed record) amplifies that to roughly 4–5× the cap. Peak transient RAM is therefore on the order of 4–5 × MaxBytes, not MaxBytes.

DefaultMaxBytes is set to 128 MiB so that this worst-case transient stays well under 1 GiB even on a hostile single-token file. Callers importing larger trusted inputs raise Options.MaxBytes explicitly, accepting the proportionally higher peak; callers parsing untrusted input should keep the default or lower it further.

Variables

View Source
var ErrInputTooLarge = errors.New("csv: input exceeds maximum size")

ErrInputTooLarge is returned by ReadInto and ReadIntoCtx when the input stream exceeds the configured Options.MaxBytes ceiling. The reader stops drawing bytes from the input as soon as the limit is crossed; note, however, that a single oversized field may already have been buffered by encoding/csv up to the cap before the limit trips, so the decoder's peak working set is a multiple of MaxBytes (see DefaultMaxBytes).

Functions

func ReadInto

func ReadInto(r io.Reader, opts Options) (*adjlist.AdjList[string, int64], int, error)

ReadInto streams a CSV from r into an adjacency list, returning the loaded list and the number of rows ingested. Each row must have at least two fields (src, dst); a third field is parsed as a int64 weight.

Example

ExampleReadInto parses a CSV edge list (src,dst[,weight] per row, '#' comment lines skipped) into a mutable adjacency list and reports the resulting order, size and one edge.

package main

import (
	"fmt"
	"strings"

	"github.com/FlavioCFOliveira/GoGraph/graph/io/csv"
)

func main() {
	const data = "# a tiny directed triangle\n" +
		"a,b,1\n" +
		"b,c,2\n" +
		"c,a,3\n"

	opts := csv.DefaultOptions()
	opts.Directed = true

	g, rows, err := csv.ReadInto(strings.NewReader(data), opts)
	if err != nil {
		panic(err)
	}

	fmt.Println("rows:", rows)
	fmt.Println("order:", g.Order())
	fmt.Println("size:", g.Size())
	fmt.Println("a->b:", g.HasEdge("a", "b"))
}
Output:
rows: 3
order: 3
size: 3
a->b: true

func ReadIntoCtx

func ReadIntoCtx(ctx context.Context, r io.Reader, opts Options) (*adjlist.AdjList[string, int64], int, error)

ReadIntoCtx is the context-aware variant of ReadInto. ctx.Err() is checked every 4096 rows.

On any error — a parse error, context cancellation, or the ErrInputTooLarge cap — the returned graph is nil; the import is all-or-nothing at the in-memory level, so a caller cannot accidentally commit a half-built graph. The typed error (parse error, ctx.Err(), or ErrInputTooLarge) is returned unchanged; only the graph value is discarded.

func Write

func Write(w io.Writer, a *adjlist.AdjList[string, int64], opts Options) (int, error)

Write streams every edge of a in src,dst,weight order to w. Returns the number of rows written.

Example

ExampleWrite shows a CSV round-trip: build a graph, Write it to a buffer, then ReadInto a fresh graph and confirm the edges survived. The serialised row order follows internal NodeID assignment, so the example asserts on edge presence rather than on exact bytes.

package main

import (
	"bytes"
	"fmt"

	"github.com/FlavioCFOliveira/GoGraph/graph/adjlist"
	"github.com/FlavioCFOliveira/GoGraph/graph/io/csv"
)

func main() {
	src := adjlist.New[string, int64](adjlist.Config{Directed: true})
	_ = src.AddEdge("a", "b", 1)
	_ = src.AddEdge("a", "c", 2)
	_ = src.AddEdge("b", "c", 3)

	var buf bytes.Buffer
	rows, err := csv.Write(&buf, src, csv.DefaultOptions())
	if err != nil {
		panic(err)
	}

	readOpts := csv.DefaultOptions()
	readOpts.Directed = true
	dst, _, err := csv.ReadInto(&buf, readOpts)
	if err != nil {
		panic(err)
	}

	fmt.Println("rows written:", rows)
	fmt.Println("edges survive:", dst.HasEdge("a", "b") && dst.HasEdge("a", "c") && dst.HasEdge("b", "c"))
}
Output:
rows written: 3
edges survive: true

func WriteCtx

func WriteCtx(ctx context.Context, w io.Writer, a *adjlist.AdjList[string, int64], opts Options) (int, error)

WriteCtx is the context-aware variant of Write. ctx.Err() is checked every 4096 rows; on cancellation returns (rowsWritten, wrapped ctx.Err()).

Types

type Options

type Options struct {
	// Delimiter is the column separator; defaults to ','.
	Delimiter rune
	// Comment is the comment character; defaults to '#'.
	Comment rune
	// HasHeader skips the first line when true.
	HasHeader bool
	// Directed selects the underlying adjacency-list config.
	Directed bool
	// Multigraph allows parallel edges.
	Multigraph bool
	// MaxBytes caps the number of bytes read from the input before the
	// reader fails with [ErrInputTooLarge]. [DefaultOptions] sets it to
	// [DefaultMaxBytes]; a value of zero or less disables the cap.
	MaxBytes int64

	// SanitizeFormulae, when true, neutralises spreadsheet formula
	// injection (OWASP CSV injection, CWE-1236) on the write path. A cell
	// whose first character is one of '=', '+', '-', '@', TAB (0x09), or
	// CR (0x0D) is treated as a live formula by Excel, LibreOffice Calc,
	// and Google Sheets when the exported file is opened, enabling DDE
	// command execution or data exfiltration in the context of the human
	// who opens it. With this option set, [Write] and [WriteCtx] prefix
	// each such cell with a single apostrophe ('), the de-facto neutraliser
	// those spreadsheets honour, so the value is rendered as text.
	//
	// It is OFF by default to preserve the lossless round-trip: an
	// apostrophe-prefixed cell no longer re-imports byte-identically
	// through [ReadInto], so a graph written with the default options round
	// -trips exactly while one written with sanitisation enabled does not.
	// Enable it only when the destination is a spreadsheet and faithful
	// re-import is not required. This flag affects the writer only; the
	// reader ignores it.
	SanitizeFormulae bool
}

Options controls Reader / Writer behaviour.

func DefaultOptions

func DefaultOptions() Options

DefaultOptions returns the minimal config: comma delimiter, '#' comments, directed simple graph, no header, and the DefaultMaxBytes input-size ceiling.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL