CSV
rix/csv is the Rix package for reading and writing CSV data.
It is designed for simple application workflows where a Vix.cpp project needs to parse CSV text, inspect rows, transform values, or generate CSV output.
The examples on this page use the public Rix facade:
#include <rix.hpp>and access CSV through:
rix.csvWhat rix/csv provides
rix/csv provides a small CSV API for:
parsing CSV text
reading rows and fields
writing CSV text
working with simple table data
using CSV from the unified rix facadeThe goal is not to make CSV complicated.
The goal is to give Vix.cpp applications a small, predictable CSV package that works well with the rest of Rix.
Install
For normal usage through the unified facade, install:
vix add rix/rix
vix installIn vix.app, declare:
deps = [
"rix/rix",
]If you want to use only the independent CSV package, install:
vix add rix/csv
vix installand declare:
deps = [
"rix/csv",
]For most documentation examples, prefer the facade package:
#include <rix.hpp>Run as a single file
Create a small working folder:
mkdir -p ~/rix-csv-example
cd ~/rix-csv-example
touch csv.cppAdd:
#include <rix.hpp>
#include <sstream>
#include <string>
static 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 it:
vix run csv.cppIf Rix is not available yet for single-file usage:
vix install -g rix/rix
vix run csv.cppExpected output
The output should look like this:
loaded rows: 3
name language
Ada C++
Gaspard VixThe first row is the CSV header.
The following rows are the data rows.
Basic parsing
Use:
const auto table = rix.csv.parse(input);Example:
const std::string input =
"name,language\n"
"Ada,C++\n"
"Gaspard,Vix\n";
const auto table = rix.csv.parse(input);The result is a CSV table.
You can iterate over it:
for (const auto &row : table)
{
for (const auto &field : row)
{
rix.debug.print(field);
}
}Table shape
A parsed table behaves like rows of fields.
Conceptually:
Table
-> Row
-> FieldExample:
const auto table = rix.csv.parse(input);
rix.debug.print("rows:", table.size());
rix.debug.print("first field:", table[0][0]);For this CSV:
name,language
Ada,C++the table contains:
table[0][0] -> name
table[0][1] -> language
table[1][0] -> Ada
table[1][1] -> C++Parse a header row
CSV does not force a special header model.
The first row can be treated as the header by convention:
const auto table = rix.csv.parse(input);
if (!table.empty())
{
const auto &header = table[0];
for (const auto &name : header)
{
rix.debug.print("column:", name);
}
}Then process data rows starting at index 1:
for (std::size_t i = 1; i < table.size(); ++i)
{
const auto &row = table[i];
rix.debug.print("name:", row[0]);
rix.debug.print("language:", row[1]);
}Writing CSV
Use the CSV writer when you need to generate CSV text.
Example shape:
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 CSV shape:
name,language
Ada,C++
Gaspard,VixUse writing when your application needs to export reports, logs, generated data, or small datasets.
CSV escaping
CSV writers should escape fields when needed.
For example, values containing commas, quotes, or newlines need proper CSV escaping.
Example input fields:
Ada
C++
Hello, world
She said "hi"The writer should produce CSV-safe output.
Use rix.csv.write(...) instead of building CSV manually with string concatenation.
Simple export example
#include <rix.hpp>
int main()
{
rixlib::csv::Table table;
table.push_back({"name", "project"});
table.push_back({"Ada", "Rix"});
table.push_back({"Gaspard", "Vix.cpp"});
const auto csv = rix.csv.write(table);
rix.debug.print(csv);
return 0;
}Run it:
vix run csv.cppUse CSV in a Vix project
Create an application:
vix new csv-app --app
cd csv-appAdd Rix:
vix add rix/rix
vix installIn vix.app, add:
deps = [
"rix/rix",
]A small manifest can look like this:
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",
]Then use:
#include <rix.hpp>
int main()
{
const auto table = rix.csv.parse("name,lang\nAda,C++\n");
rix.debug.print("rows:", table.size());
return 0;
}Build and run:
vix build
vix runFacade usage
The recommended application style is:
#include <rix.hpp>
const auto table = rix.csv.parse(input);This keeps all Rix packages available through one object:
rix.csv
rix.debug
rix.auth
rix.pdfUse this when your project already uses the Rix facade.
Independent usage
When using rix/csv without the full facade, include the independent package header:
#include <rix/csv.hpp>Then use the independent API from the CSV package.
This is useful when a project wants only CSV support and does not want to mount all Rix facade packages.
For most documentation examples, the public facade remains the preferred path:
#include <rix.hpp>Lightweight facade usage
If you want the unified facade but only want CSV mounted, define the feature macro before including rix.hpp:
#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.
This keeps the facade style while reducing what gets included.
Common use cases
Use rix/csv for:
small data imports
test fixtures
local reports
admin exports
configuration-like tabular data
examples and demos
simple analytics inputCSV is useful when the data is tabular and does not need a database.
For durable application state, use a database.
For structured nested data, use JSON.
CSV and debug
CSV works well with rix/debug.
Example:
const auto table = rix.csv.parse(input);
rix.debug.print("rows:", table.size());
for (const auto &row : table)
{
rix.debug.print(row.size());
}For real application logging, prefer the Vix logging system.
Use rix.debug mainly for examples, quick tools, and local diagnostics.
CSV and Auth
CSV can be useful for admin-style import workflows.
Example shapes:
import initial users
read test users
generate audit exports
create local seed dataDo not store plain-text production passwords in CSV files.
Do not export password hashes to client-facing files.
Treat user data as sensitive.
CSV and PDF
CSV can also feed PDF reports.
Example flow:
parse CSV
build table rows
render PDF table
save PDFThis keeps data loading separate from document generation.
rix.csv reads the data.
rix.pdf generates the document.
Error handling
CSV APIs are intentionally simple for common usage.
For strict applications, validate the table shape after parsing.
Example:
const auto table = rix.csv.parse(input);
if (table.empty())
{
rix.debug.eprint("CSV is empty");
return 1;
}
if (table[0].size() != 2)
{
rix.debug.eprint("expected two columns");
return 1;
}Validate rows before using indexes:
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]);
}Common mistakes
Assuming every row has the same size
CSV input can be inconsistent.
Check row sizes when the input is external:
if (row.size() < 2)
{
continue;
}Treating the header as data
If the first row is a header, start processing at index 1:
for (std::size_t i = 1; i < table.size(); ++i)
{
const auto &row = table[i];
}Building CSV manually
Avoid this for real CSV output:
output += name + "," + language + "\n";Use the CSV writer so fields can be escaped correctly:
auto output = rix.csv.write(table);Using CSV for secrets
Do not store production secrets in CSV files.
Avoid CSV for sensitive authentication data unless you have a controlled import process.
What you should remember
Use the public facade:
#include <rix.hpp>Parse CSV:
const auto table = rix.csv.parse(input);Iterate rows:
for (const auto &row : table)
{
for (const auto &field : row)
{
rix.debug.print(field);
}
}Write CSV:
const auto output = rix.csv.write(table);Use vix add rix/rix for the facade package.
Use deps = ["rix/rix"] in vix.app.
Validate row sizes when reading external CSV.
Next step
Continue with the debug package.
Next: Debug