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

CSV API

This page documents the public rix/csv API.

Use CSV through the unified Rix facade:

cpp
#include <rix.hpp>

Then access CSV with:

cpp
rix.csv

The CSV API provides helpers for:

txt
parsing CSV text
writing CSV text
working with rows and fields
using parse options
using write options
transforming fields
filtering rows

Package

The CSV package is:

txt
rix/csv

For facade usage, install:

bash
vix add rix/rix
vix install

In vix.app:

txt
deps = [
  "rix/rix",
]

For independent package usage, install:

bash
vix add rix/csv
vix install

In vix.app:

txt
deps = [
  "rix/csv",
]

Facade usage:

cpp
#include <rix.hpp>

Independent usage:

cpp
#include <rix/csv.hpp>

Most application examples should use the facade.

Facade member

The CSV facade member is:

cpp
rix.csv

Example:

cpp
#include <rix.hpp>

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

  return table.empty() ? 1 : 0;
}

Main operations

Common CSV operations are:

cpp
rix.csv.parse(...)
rix.csv.write(...)

Use parse to convert CSV text into a table.

Use write to convert a table back into CSV text.

Table type

CSV data is represented as a table.

A table is a list of rows.

Each row is a list of fields.

Common shape:

txt
Table
  Row
    Field
    Field
  Row
    Field
    Field

Example:

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

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

Parse CSV text

Use:

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

Example:

cpp
#include <rix.hpp>

int main()
{
  const auto input =
      "name,language\n"
      "Ada,C++\n"
      "Gaspard,Vix\n";

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

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

  return 0;
}

Access rows

Iterate over the table:

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

Access a row by index:

cpp
const auto &header = table[0];

Check that the table is not empty before indexing.

Access fields

Fields are stored as strings.

Example:

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

Access a field by index:

cpp
const auto &name = table[1][0];
const auto &language = table[1][1];

Check row sizes before indexing.

Safe indexing

Wrong:

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

Better:

cpp
if (table.size() > 1 && table[1].size() > 0)
{
  rix.debug.print(table[1][0]);
}

CSV input can be incomplete, so code should check sizes when reading by index.

cpp
#include <rix.hpp>

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

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

  return 0;
}

For real Vix application logs, prefer the Vix logging system.

For examples and small tools, rix.debug.print is fine.

Write CSV text

Use:

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

Example:

cpp
#include <rix.hpp>

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

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

  rix.debug.print(output);

  return 0;
}

Use write when you need to export table data as CSV text.

Round trip example

cpp
#include <rix.hpp>

int main()
{
  const auto input =
      "name,language\n"
      "Ada,C++\n"
      "Gaspard,Vix\n";

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

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

  rix.debug.print(output);

  return 0;
}

This parses CSV text and writes it back as CSV text.

Parse with options

CSV parsing can use options when you need custom behavior.

Example shape:

cpp
rixlib::csv::ParseOptions options;

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

Use options for custom parsing behavior such as trimming, delimiter changes, field transforms, or row filtering when supported by the package version.

Field transformer

A field transformer can normalize fields while parsing.

Example:

cpp
rixlib::csv::ParseOptions 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 a field transformer when parsed values need light normalization.

Row filter

A row filter can skip rows while parsing.

Example:

cpp
rixlib::csv::ParseOptions options;

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

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

Use a row filter when you want to remove empty rows or application-specific rows.

Complete parse options example

cpp
#include <rix.hpp>

#include <string>

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

  rixlib::csv::ParseOptions 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) {
    return !row.empty();
  };

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

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

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

  return 0;
}

Run:

bash
vix run csv.cpp

Write with options

CSV writing can use options when you need custom output behavior.

Example shape:

cpp
rixlib::csv::WriteOptions options;

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

Use write options when you need to control output format such as delimiter or newline behavior when supported by the package version.

Table construction

You can build a table manually.

Example shape:

cpp
rixlib::csv::Table table;

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

Then write it:

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

Complete write example

cpp
#include <rix.hpp>

int main()
{
  rixlib::csv::Table table;

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

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

  rix.debug.print(output);

  return 0;
}

Run:

bash
vix run csv.cpp

CSV and PDF

CSV tables can be converted into PDF tables.

Example:

cpp
#include <rix.hpp>

int main()
{
  const auto csv = rix.csv.parse(
      "Name,Language,Project\n"
      "Ada,C++,Rix\n"
      "Gaspard,C++,Vix.cpp\n");

  auto doc = rix.pdf.document();

  auto &page = doc.add_page();

  auto y = page.heading(
      page.x_left(),
      page.y_top(),
      "CSV table",
      1);

  y -= 20.0F;

  rixlib::pdf::Table table;

  table.set_column_widths({
      160.0F,
      160.0F,
      160.0F});

  if (!csv.empty() && csv[0].size() >= 3)
  {
    table.add_header({
        csv[0][0],
        csv[0][1],
        csv[0][2]});
  }

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

    if (row.size() >= 3)
    {
      table.add_row({
          row[0],
          row[1],
          row[2]});
    }
  }

  page.table(
      page.x_left(),
      y,
      table);

  auto saved = rix.pdf.save(doc, "csv-table.pdf");

  return saved.ok() ? 0 : 1;
}

This example uses:

txt
rix.csv
rix.pdf

so the unified facade is the right dependency.

CSV and debug

CSV examples often use debug output:

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

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

rix.debug is useful for examples, quick checks, and small tools.

For real Vix application logs, prefer the Vix logging system.

Use in a Vix project

Create a Vix app:

bash
vix new rix-csv-api --app
cd rix-csv-api

Add Rix:

bash
vix add rix/rix
vix install

Make sure vix.app contains:

txt
deps = [
  "rix/rix",
]

Use in src/main.cpp:

cpp
#include <rix.hpp>

Build and run:

bash
vix build
vix run

Single-file usage

Create a file:

bash
mkdir -p ~/rix-csv-api
cd ~/rix-csv-api
touch csv.cpp

Add:

cpp
#include <rix.hpp>

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

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

Run:

bash
vix run csv.cpp

If Rix is not available globally:

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

Independent usage

Install only CSV:

bash
vix add rix/csv
vix install

In vix.app:

txt
deps = [
  "rix/csv",
]

Then include:

cpp
#include <rix/csv.hpp>

Example:

cpp
#include <rix/csv.hpp>

int main()
{
  auto csv = rixlib::csv::module();

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

  return table.empty() ? 1 : 0;
}

Use independent package APIs when you intentionally do not want the unified facade.

For most documentation and application examples, prefer:

cpp
#include <rix.hpp>

and:

cpp
rix.csv

Enable only CSV in the facade

Use feature macros when you want the facade style but only want CSV mounted:

cpp
#define RIX_ENABLE_CSV
#include <rix.hpp>

int main()
{
  const auto table = rix.csv.parse(
      "name\n"
      "Ada\n");

  return table.empty() ? 1 : 0;
}

If you also want debug output:

cpp
#define RIX_ENABLE_CSV
#define RIX_ENABLE_DEBUG
#include <rix.hpp>

int main()
{
  const auto table = rix.csv.parse(
      "name\n"
      "Ada\n");

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

  return 0;
}

Complete CSV example

cpp
#include <rix.hpp>

#include <sstream>
#include <string>

namespace
{
  void print_table(const rixlib::csv::Table &table)
  {
    for (const auto &row : table)
    {
      std::ostringstream line;

      for (std::size_t i = 0; i < row.size(); ++i)
      {
        if (i > 0)
        {
          line << " ";
        }

        line << row[i];
      }

      rix.debug.print(line.str());
    }
  }
}

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

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

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

  return 0;
}

Run:

bash
vix run csv.cpp

Common mistakes

Forgetting to install Rix

If your code uses:

cpp
#include <rix.hpp>

install:

bash
vix add rix/rix
vix install

If your code uses:

cpp
#include <rix/csv.hpp>

install:

bash
vix add rix/csv
vix install

Putting Rix in packages

Wrong:

txt
packages = [
  "rix/csv",
]

Correct:

txt
deps = [
  "rix/csv",
]

deps is for Vix Registry packages.

packages is for CMake package discovery.

Installing rix/csv but including <rix.hpp>

If your code uses:

cpp
#include <rix.hpp>

then install:

bash
vix add rix/rix
vix install

If your code uses:

cpp
#include <rix/csv.hpp>

then install:

bash
vix add rix/csv
vix install

Accessing fields without checking row size

Wrong:

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

Better:

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

Assuming all rows have the same number of fields

CSV files can have incomplete rows.

Check row sizes when reading fields by index.

Using debug output as production logging

rix.debug is useful for examples and small tools.

For real Vix application logs, prefer the Vix logging system.

Mixing facade and independent usage

Avoid this without a clear reason:

cpp
#include <rix.hpp>
#include <rix/csv.hpp>

Use one style per file.

Facade:

cpp
#include <rix.hpp>

Independent:

cpp
#include <rix/csv.hpp>

What you should remember

Parse CSV:

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

Write CSV:

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

Iterate rows:

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

Use the facade for most examples:

cpp
#include <rix.hpp>

and:

cpp
rix.csv

For project usage:

bash
vix add rix/rix
vix install

and keep:

txt
deps = [
  "rix/rix",
]

For independent CSV usage:

bash
vix add rix/csv
vix install

and:

cpp
#include <rix/csv.hpp>

Always check row sizes before indexed field access.

Next step

Continue with the debug API.

Next: Debug API

Released under the MIT License.