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.