Write CSV
This page explains how to write CSV output with rix/csv.
The examples use the public Rix facade:
#include <rix.hpp>and access CSV through:
rix.csvWriting turns a table of rows and fields into CSV text.
Use it when your application needs to export small datasets, reports, generated data, test fixtures, or simple tabular output.
Basic example
Create a file:
mkdir -p ~/rix-csv-write
cd ~/rix-csv-write
touch write.cppAdd:
#include <rix.hpp>
int main()
{
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);
return 0;
}Run it:
vix run write.cppIf Rix is not available yet for single-file usage:
vix install -g rix/rix
vix run write.cppExpected output
The output should look like this:
name,language
Ada,C++
Gaspard,VixThe first row is the header.
The following rows are data rows.
Create a table
A CSV table is a list of rows.
A row is a list of fields.
rixlib::csv::Table table;Add rows with push_back:
table.push_back({"name", "language"});
table.push_back({"Ada", "C++"});
table.push_back({"Gaspard", "Vix"});Then write it:
const auto output = rix.csv.write(table);Write a header
CSV does not force a special header type.
The first row can be used as a header by convention:
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);Output shape:
name,project
Ada,Rix
Gaspard,Vix.cppWrite without a header
If your data does not need a header, start directly with data rows:
rixlib::csv::Table table;
table.push_back({"Ada", "C++"});
table.push_back({"Gaspard", "Vix"});
const auto output = rix.csv.write(table);Output shape:
Ada,C++
Gaspard,VixWrite fields with commas
CSV fields that contain commas must be escaped.
Use the writer instead of building CSV manually.
rixlib::csv::Table table;
table.push_back({"name", "note"});
table.push_back({"Ada", "created with C++"});
table.push_back({"Grace", "systems, tools, and documents"});
const auto output = rix.csv.write(table);
rix.debug.print(output);Expected shape:
name,note
Ada,created with C++
Grace,"systems, tools, and documents"The comma inside the field remains part of the field value.
Write fields with quotes
Quotes inside fields must be escaped.
rixlib::csv::Table table;
table.push_back({"name", "quote"});
table.push_back({"Ada", "She said \"hello\""});
const auto output = rix.csv.write(table);
rix.debug.print(output);Expected shape:
name,quote
Ada,"She said ""hello"""Use the writer so quote escaping stays correct.
Write fields with newlines
Fields can contain newlines.
When a field contains a newline, the writer should quote it:
rixlib::csv::Table table;
table.push_back({"name", "note"});
table.push_back({"Ada", "line one\nline two"});
const auto output = rix.csv.write(table);
rix.debug.print(output);Expected shape:
name,note
Ada,"line one
line two"This is why manual string concatenation is unsafe for CSV.
Avoid manual CSV building
Avoid this:
std::string output;
output += name;
output += ",";
output += language;
output += "\n";This breaks when fields contain:
commas
quotes
newlinesUse:
rixlib::csv::Table table;
table.push_back({name, language});
const auto output = rix.csv.write(table);Complete export example
#include <rix.hpp>
int main()
{
rixlib::csv::Table table;
table.push_back({"id", "name", "project"});
table.push_back({"1", "Ada", "Rix"});
table.push_back({"2", "Gaspard", "Vix.cpp"});
table.push_back({"3", "Grace", "PDF"});
const auto output = rix.csv.write(table);
rix.debug.print(output);
return 0;
}Run it:
vix run write.cppExpected output:
id,name,project
1,Ada,Rix
2,Gaspard,Vix.cpp
3,Grace,PDFConvert parsed data back to CSV
You can parse CSV, inspect or modify the table, then write it again.
#include <rix.hpp>
#include <string>
int main()
{
const std::string input =
"name,project\n"
"Ada,Rix\n"
"Gaspard,Vix.cpp\n";
auto table = rix.csv.parse(input);
table.push_back({"Grace", "PDF"});
const auto output = rix.csv.write(table);
rix.debug.print(output);
return 0;
}This is useful for small transformations.
Validate before writing
When data comes from outside the program, validate the table before writing it.
if (table.empty())
{
rix.debug.eprint("nothing to write");
return 1;
}Check row sizes when your output requires a fixed shape:
for (std::size_t i = 0; i < table.size(); ++i)
{
if (table[i].size() != 3)
{
rix.debug.eprint("invalid row:", i);
return 1;
}
}Then write:
const auto output = rix.csv.write(table);Save CSV to a file
rix.csv.write(...) returns CSV text.
Use normal C++ file output when you want to save it.
#include <rix.hpp>
#include <fstream>
int main()
{
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);
std::ofstream file("report.csv");
if (!file)
{
rix.debug.eprint("failed to open report.csv");
return 1;
}
file << output;
rix.debug.print("created:", "report.csv");
return 0;
}Run it:
vix run write.cppThis creates:
report.csvUse in a Vix project
Create an application:
vix new csv-write --app
cd csv-writeAdd Rix:
vix add rix/rix
vix installIn vix.app, add:
deps = [
"rix/rix",
]A small manifest can look like this:
name = "csv-write"
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()
{
rixlib::csv::Table table;
table.push_back({"name", "language"});
table.push_back({"Ada", "C++"});
const auto output = rix.csv.write(table);
rix.debug.print(output);
return 0;
}Build and run:
vix build
vix runSingle-file usage
For small scripts, examples, and experiments:
vix run write.cppIf Rix is installed globally for single-file usage:
vix install -g rix/rix
vix run write.cppFor project usage, prefer:
vix add rix/rix
vix installand keep the dependency in vix.app:
deps = [
"rix/rix",
]Facade usage
The recommended documentation style is:
#include <rix.hpp>Then:
const auto output = rix.csv.write(table);This keeps CSV available through the same public object as the other Rix packages:
rix.csv
rix.debug
rix.auth
rix.pdfIndependent package usage
If your project only needs CSV, you can install the independent package:
vix add rix/csv
vix installIn vix.app:
deps = [
"rix/csv",
]Then include:
#include <rix/csv.hpp>Use this style when you do not need the unified rix.* facade.
Lightweight facade usage
If you want the facade style but only want CSV mounted:
#define RIX_ENABLE_CSV
#include <rix.hpp>
int main()
{
rixlib::csv::Table table;
table.push_back({"name", "lang"});
table.push_back({"Ada", "C++"});
const auto output = rix.csv.write(table);
return 0;
}When at least one RIX_ENABLE_* macro is defined, only selected modules are mounted.
Common mistakes
Building CSV manually
Wrong:
output += name + "," + language + "\n";Better:
table.push_back({name, language});
const auto output = rix.csv.write(table);Forgetting quoted-field rules
These fields need escaping:
Hello, world
She said "hello"
line one
line twoLet the CSV writer handle them.
Forgetting deps
For a Vix project, do not put Rix packages in packages.
Use:
deps = [
"rix/rix",
]packages is for CMake package discovery.
deps is for Vix Registry packages.
Assuming all rows have the same size
CSV can contain rows with different numbers of fields.
Validate the table if your output requires a fixed number of columns.
if (row.size() != 3)
{
return 1;
}What you should remember
Create a table:
rixlib::csv::Table table;Add rows:
table.push_back({"name", "language"});
table.push_back({"Ada", "C++"});Write CSV:
const auto output = rix.csv.write(table);Print or save the output:
rix.debug.print(output);For a Vix project, install Rix:
vix add rix/rix
vix installand use:
deps = [
"rix/rix",
]Use the writer instead of manual string concatenation.
Validate row sizes when your output expects a fixed schema.
Next step
Learn how to use CSV safely with external input.
Next: Safe Access