Rix is the official package layer for Vix.cpp Browse packages
Skip to content

CSV API Reference

This page summarizes the public API exposed by rix/csv.

The documentation examples use the unified Rix facade:

cpp
#include <rix.hpp>

and access CSV through:

cpp
rix.csv

The lower-level CSV types live under:

cpp
rixlib::csv

Use the facade for application code.

Use lower-level types when you need explicit table, row, or options types.

Facade entry point

The CSV module is mounted at:

cpp
rix.csv

Common usage:

cpp
const auto table = rix.csv.parse(
    "name,language\n"
    "Ada,C++\n");

const auto output = rix.csv.write(table);

Headers

Recommended public header:

cpp
#include <rix.hpp>

Independent package header:

cpp
#include <rix/csv.hpp>

Use <rix.hpp> when you want the unified facade:

cpp
rix.csv
rix.debug
rix.auth
rix.pdf

Use <rix/csv.hpp> when you want only the CSV package.

Namespace

CSV types live in:

cpp
rixlib::csv

Common types:

cpp
rixlib::csv::Table
rixlib::csv::Row
rixlib::csv::Options

rix.csv

rix.csv exposes the public CSV module.

It provides the main CSV operations:

cpp
parse(...)
write(...)
version()
version_major()
version_minor()
version_patch()
version_number()

parse

cpp
Table parse(std::string_view input) const;

Parses CSV text into a table.

Example:

cpp
const auto table = rix.csv.parse(
    "name,language\n"
    "Ada,C++\n"
    "Gaspard,Vix\n");

Read values:

cpp
rix.debug.print(table[0][0]);
rix.debug.print(table[1][0]);

For the example above:

txt
table[0][0] -> name
table[0][1] -> language
table[1][0] -> Ada
table[1][1] -> C++

parse with options

cpp
Table parse(
    std::string_view input,
    const Options &options) const;

Parses CSV text with custom options.

Example:

cpp
rixlib::csv::Options options;

options.field_transformer = [](std::string value) {
  if (value == "Vix")
  {
    return std::string{"Vix.cpp"};
  }

  return value;
};

const auto table = rix.csv.parse(input, options);

Use options when parsing should transform fields or filter rows.

write

cpp
std::string write(const Table &table) const;

Writes a table into CSV text.

Example:

cpp
rixlib::csv::Table table;

table.push_back({"name", "language"});
table.push_back({"Ada", "C++"});
table.push_back({"Gaspard", "Vix"});

const auto output = rix.csv.write(table);

rix.debug.print(output);

Expected output:

csv
name,language
Ada,C++
Gaspard,Vix

Use write instead of building CSV manually.

The writer handles CSV escaping for commas, quotes, and newlines.

Table

A Table represents CSV rows.

Conceptually:

txt
Table
  -> Row
     -> Field

Common usage:

cpp
rixlib::csv::Table table;

table.push_back({"name", "language"});
table.push_back({"Ada", "C++"});

A table can be iterated:

cpp
for (const auto &row : table)
{
  for (const auto &field : row)
  {
    rix.debug.print(field);
  }
}

A table can be indexed:

cpp
rix.debug.print(table[0][0]);

Check that the table is not empty before indexing:

cpp
if (!table.empty())
{
  rix.debug.print(table[0][0]);
}

Row

A Row represents one CSV row.

A row contains fields.

Common usage:

cpp
rixlib::csv::Row row;

row.push_back("Ada");
row.push_back("C++");

Rows are normally created directly inside a table:

cpp
table.push_back({"Ada", "C++"});

A row can be iterated:

cpp
for (const auto &field : row)
{
  rix.debug.print(field);
}

A row can be indexed:

cpp
rix.debug.print(row[0]);

Check row size before indexing external CSV data:

cpp
if (row.size() >= 2)
{
  rix.debug.print(row[0], row[1]);
}

Field values

A field is represented as a string value.

Example:

cpp
const auto table = rix.csv.parse("name,language\nAda,C++\n");

const std::string name = table[1][0];
const std::string language = table[1][1];

CSV values are text.

If your application needs numbers, booleans, dates, or custom types, parse those values after reading the field.

Options

Options customizes parsing behavior.

Create an options object:

cpp
rixlib::csv::Options options;

Pass it to parsing:

cpp
const auto table = rix.csv.parse(input, options);

Options::field_transformer

A field transformer receives one field value and returns the value that should be stored in the parsed table.

Example:

cpp
options.field_transformer = [](std::string value) {
  if (value == "Vix")
  {
    return std::string{"Vix.cpp"};
  }

  return value;
};

Use it for:

txt
normalizing values
trimming spaces
renaming imported values
mapping old values to new values
small parse-time cleanup

Example trim-style transformer:

cpp
options.field_transformer = [](std::string value) {
  while (!value.empty() && value.front() == ' ')
  {
    value.erase(value.begin());
  }

  while (!value.empty() && value.back() == ' ')
  {
    value.pop_back();
  }

  return value;
};

Options::row_filter

A row filter receives a parsed row and decides whether it should be kept.

Example:

cpp
options.row_filter = [](const rixlib::csv::Row &row) {
  if (!row.empty() && row[0] == "Skip")
  {
    return false;
  }

  return true;
};

Return:

txt
true  -> keep the row
false -> remove the row

Use it for:

txt
skipping empty rows
skipping comment rows
removing invalid rows
filtering imported data

Complete options example

cpp
#include <rix.hpp>

#include <string>

int main()
{
  const std::string input =
      "name,language\n"
      "Ada,C++\n"
      "Gaspard,Vix\n"
      "Skip,Ignored\n";

  rixlib::csv::Options options;

  options.field_transformer = [](std::string value) {
    if (value == "Vix")
    {
      return std::string{"Vix.cpp"};
    }

    return value;
  };

  options.row_filter = [](const rixlib::csv::Row &row) {
    if (!row.empty() && row[0] == "Skip")
    {
      return false;
    }

    return true;
  };

  const auto table = rix.csv.parse(input, options);

  rix.debug.print("rows:", table.size());

  return 0;
}

Version helpers

The CSV module exposes package version helpers.

cpp
std::string version() const;

int version_major() const noexcept;
int version_minor() const noexcept;
int version_patch() const noexcept;
int version_number() const noexcept;

Example:

cpp
auto version = rix.csv.version();

rix.debug.print("csv version:", version);

Use these helpers in diagnostics, examples, or compatibility checks.

Parse pattern

Use this pattern when reading CSV from trusted small input:

cpp
const auto table = rix.csv.parse(input);

for (const auto &row : table)
{
  for (const auto &field : row)
  {
    rix.debug.print(field);
  }
}

Use this pattern when reading external input:

cpp
const auto table = rix.csv.parse(input);

if (table.empty())
{
  rix.debug.eprint("CSV is empty");
  return 1;
}

for (std::size_t i = 1; i < table.size(); ++i)
{
  const auto &row = table[i];

  if (row.size() < 2)
  {
    rix.debug.eprint("invalid row:", i);
    continue;
  }

  rix.debug.print("name:", row[0]);
  rix.debug.print("language:", row[1]);
}

Write pattern

Use this pattern when generating CSV:

cpp
rixlib::csv::Table table;

table.push_back({"name", "project"});
table.push_back({"Ada", "Rix"});
table.push_back({"Gaspard", "Vix.cpp"});

const auto output = rix.csv.write(table);

Save it with normal C++ file output:

cpp
#include <fstream>

std::ofstream file("report.csv");

if (!file)
{
  rix.debug.eprint("failed to open report.csv");
  return 1;
}

file << output;

CSV escaping behavior

When writing CSV, fields that need escaping should be quoted.

Examples:

txt
Hello, world
She said "hello"
line one
line two

Expected CSV-safe output shape:

csv
"Hello, world"
"She said ""hello"""
"line one
line two"

Use rix.csv.write(table) instead of manual string concatenation.

Header convention

CSV headers are normal rows.

If your CSV has a header, treat the first row as the header:

cpp
const auto &header = table[0];

Then process data rows from index 1:

cpp
for (std::size_t i = 1; i < table.size(); ++i)
{
  const auto &row = table[i];
}

If your CSV has no header, process every row as data:

cpp
for (const auto &row : table)
{
  rix.debug.print(row.size());
}

Single-file usage

For quick examples:

bash
vix run csv.cpp

If Rix is not available yet for single-file usage:

bash
vix install -g rix/rix
vix run csv.cpp

Project usage

For a Vix project, add Rix:

bash
vix add rix/rix
vix install

In vix.app:

txt
deps = [
  "rix/rix",
]

A small manifest can look like this:

txt
name = "csv-app"
type = "executable"
standard = "c++20"
output_dir = "bin"

sources = [
  "src/main.cpp",
]

include_dirs = [
  "include",
  "src",
]

deps = [
  "rix/rix",
]

packages = [
  "vix",
]

links = [
  "vix::vix",
]

Build and run:

bash
vix build
vix run

Independent package usage

If the project only needs CSV, install:

bash
vix add rix/csv
vix install

In vix.app:

txt
deps = [
  "rix/csv",
]

Then include:

cpp
#include <rix/csv.hpp>

Use this style when you do not need the unified rix.* facade.

Lightweight facade usage

If you want the unified facade but only want CSV mounted:

cpp
#define RIX_ENABLE_CSV
#include <rix.hpp>

int main()
{
  const auto table = rix.csv.parse("name,lang\nAda,C++\n");
  return 0;
}

When at least one RIX_ENABLE_* macro is defined, only selected modules are mounted.

Common mistakes

Calling table[0] on an empty table

Wrong:

cpp
const auto &header = table[0];

Better:

cpp
if (!table.empty())
{
  const auto &header = table[0];
}

Calling row[1] without checking row size

Wrong:

cpp
rix.debug.print(row[0], row[1]);

Better:

cpp
if (row.size() >= 2)
{
  rix.debug.print(row[0], row[1]);
}

Splitting CSV manually

Wrong:

cpp
std::getline(stream, field, ',');

Better:

cpp
const auto table = rix.csv.parse(input);

Manual splitting breaks with quoted commas, quotes, and newlines.

Building CSV manually

Wrong:

cpp
output += name + "," + language + "\n";

Better:

cpp
table.push_back({name, language});
const auto output = rix.csv.write(table);

Putting Rix packages in packages

Wrong:

txt
packages = [
  "rix/rix",
]

Correct:

txt
deps = [
  "rix/rix",
]

deps is for Vix Registry packages.

packages is for CMake package discovery.

API summary

Main facade:

cpp
rix.csv.parse(input)
rix.csv.parse(input, options)
rix.csv.write(table)
rix.csv.version()

Main types:

cpp
rixlib::csv::Table
rixlib::csv::Row
rixlib::csv::Options

Main options:

cpp
options.field_transformer
options.row_filter

Recommended include:

cpp
#include <rix.hpp>

Independent include:

cpp
#include <rix/csv.hpp>

Security and data notes

CSV is plain text.

Do not use CSV as a secure storage format.

Do not store production secrets in CSV files.

Be careful with user data exports.

Validate external CSV before using field indexes.

Use database storage for durable application state.

Use JSON when the data is nested or structured beyond simple rows and columns.

What you should remember

Parse CSV:

cpp
const auto table = rix.csv.parse(input);

Parse with options:

cpp
rixlib::csv::Options options;

const auto table = rix.csv.parse(input, options);

Write CSV:

cpp
const auto output = rix.csv.write(table);

Create a table:

cpp
rixlib::csv::Table table;

table.push_back({"name", "language"});

Validate before indexing external input:

cpp
if (row.size() >= 2)
{
  rix.debug.print(row[0], row[1]);
}

Use deps for Rix packages in vix.app:

txt
deps = [
  "rix/rix",
]

Next step

Continue with the debug package.

Next: Debug

Released under the MIT License.