A tiny single-header CSV parser for C.
eccsv is designed primarily for embedded systems and configuration files, but it can be used anywhere a lightweight CSV parser is needed.
- Single header library
- No heap allocation
- No external dependencies
- Callback-based API
- Supports quoted fields
- Supports escaped quotes (
"") - Supports embedded commas inside quoted fields
- Supports embedded newlines inside quoted fields
- Supports empty fields and trailing empty fields
- Supports LF and CRLF line endings
- Optional in-place unescaping for writable buffers
- Compile-time configuration
The parser aims to be RFC 4180 compatible for common CSV files while keeping code size and complexity small. It intentionally avoids features that are not typically useful for embedded applications, such as UTF-8 validation.
- Small code size
- No dynamic memory allocation
- Easy integration into existing projects
- Suitable for ROM/flash-backed data
- Reasonable CSV interoperability
- Zero-copy parsing
In one source file:
#define ECCSV_IMPLEMENTATION
#include "eccsv.h"In all other files:
#include "eccsv.h"#include <stdio.h>
static void csv_cb(char *field, int col, size_t len, void *ctx)
{
printf("column %d: %.*s\n", col, (int)len, field);
}
int main(void)
{
char line[] = "foo,\"bar,baz\",123";
eccsv_parse(line, csv_cb, NULL);
return 0;
}typedef void (*csv_cb_t)(
eccsv_fieldp_t field,
int col,
size_t len,
ECCSV_CTXTYPE ctx
);
int eccsv_parse(
eccsv_fieldp_t str,
csv_cb_t cb,
ECCSV_CTXTYPE ctx
);Pointer to a CSV record.
Callback invoked once for each field.
User-defined context pointer passed unchanged to every callback invocation.
Pointer to the field contents.
Zero-based column index.
Field length in bytes.
The field is not guaranteed to be NUL terminated. Use len when processing or copying data.
User context supplied to eccsv_parse().
Returns the number of bytes consumed from the input buffer.
The returned length includes any line terminator (\n, \r, or \r\n) if present.
Negative values indicate errors:
| Value | Meaning |
|---|---|
| -1 | Unterminated quoted field |
| -2 | Invalid characters after closing quote |
| -3 | NULL input pointer |
Default:
#define ECCSV_SEP ','Example:
#define ECCSV_SEP ';'
#include "eccsv.h"Default:
#define ECCSV_MUTABLE 1When enabled, quoted fields are unescaped in-place.
Example:
"a""b"becomes:
a"b
The input buffer must therefore be writable.
#define ECCSV_MUTABLE 0In immutable mode the parser never modifies the input buffer.
This allows parsing data stored in ROM, flash memory, or string literals.
Escaped quotes are not decoded in this mode. The callback receives a pointer into the original CSV data.
Default:
#define ECCSV_CTXTYPE void *Example:
struct parser_context;
#define ECCSV_CTXTYPE struct parser_context *
#include "eccsv.h"This avoids casts inside callbacks.
The parser supports:
- Quoted fields
- Escaped quotes (
"") - Embedded commas in quoted fields
- Embedded newlines in quoted fields
- Empty fields
- Trailing empty fields
- CRLF and LF line endings
The focus is practical interoperability and small size rather than complete implementation of every edge case described by CSV-related standards.