This commit is contained in:
2025-04-09 10:22:44 +02:00
commit 96ff5392c8
330 changed files with 28300 additions and 0 deletions
+52
View File
@@ -0,0 +1,52 @@
cmake_minimum_required(VERSION 3.28)
# define CXX interface library
# define target & alias (i.e. consuming projects can experience namespaces)
add_library(iue-io_csv INTERFACE)
target_link_libraries(iue-io_csv INTERFACE iue-other_honly)
#target_link_libraries(iue-io_csv INTERFACE OpenMP::OpenMP_CXX)
# define sources of target
target_sources(iue-io_csv
INTERFACE FILE_SET public_headers TYPE HEADERS BASE_DIRS ${PROJECT_SOURCE_DIR} FILES csv.hpp)
# define installation step
install(TARGETS iue-io_csv EXPORT iue-io-export
FILE_SET public_headers DESTINATION "./")
# define test
if(BUILD_TESTING AND PROJECT_IS_TOP_LEVEL)
add_executable(iue-io_csv.test csv.test.cpp)
target_link_libraries(iue-io_csv.test iue-io_csv)
add_test(NAME iue-io_csv.test COMMAND iue-io_csv.test)
endif()
# define C interface library
# define target & alias (i.e. consuming projects can experience namespaces)
add_library(iue-io_ccsv INTERFACE)
# define sources of target
target_sources(iue-io_ccsv
INTERFACE FILE_SET public_headers TYPE HEADERS BASE_DIRS ${PROJECT_SOURCE_DIR} FILES ccsv.h)
# define installation step
install(TARGETS iue-io_ccsv EXPORT iue-io-export
FILE_SET public_headers DESTINATION "./")
# define test
if(BUILD_TESTING AND PROJECT_IS_TOP_LEVEL)
add_executable(iue-io_ccsv.test ccsv.test.c)
target_link_libraries(iue-io_ccsv.test iue-io_ccsv)
add_test(NAME iue-io_ccsv.test COMMAND iue-io_ccsv.test)
endif()
# export targets for consuming cmake projects
# generate and install a .cmake file to allow dependend projects consuming the "install-tree" to import all targets
install(EXPORT iue-io-export DESTINATION "iue-io" )
# allow dependend projects consuming the "build-tree" to use all targets
export(EXPORT iue-io-export )
+143
View File
@@ -0,0 +1,143 @@
#pragma once
#include <assert.h> // assert
#include <math.h> // NAN
#include <stddef.h> // size_t
#include <stdio.h> // FILE|snprintf
#include <stdlib.h> // EXIT_FAILURE|EXIT_SUCCESS
#include <string.h> // strtok|strdup|memcpy
/// @brief Set of values in a row
struct Row {
double* values; ///< pointer to row value array
size_t n; ///< length of array 'value'
};
/// @brief Set of rows
struct Table {
struct Row* rows; ///< pointer to array of rows
size_t n; ///< length of array 'rows'
};
static void table_clear(struct Table* table) {
for (size_t i = 0; i != table->n; ++i) {
free(table->rows[i].values);
}
free(table->rows);
table->rows = NULL;
table->n = 0;
}
static void table_append_copy(struct Table* table, const double* data, size_t n) {
assert(table != NULL);
table->rows = realloc(table->rows, sizeof(struct Row) * ++table->n);
double* ptr = malloc(sizeof(double) * n);
memcpy(ptr, data, sizeof(double) * n);
table->rows[table->n - 1].values = ptr;
table->rows[table->n - 1].n = n;
}
static void table_append_move(struct Table* table, const struct Row* row) {
assert(table != NULL);
table->rows = realloc(table->rows, sizeof(struct Row) * ++table->n);
table->rows[table->n - 1] = *row;
}
/// @brief Deserialize a row from a string
static void row_serialize(const struct Row* row, FILE* stream, char del) {
for (size_t r = 0; r != row->n - 1; ++r) {
fprintf(stream, "%.18e%c", row->values[r], del);
}
fprintf(stream, "%.18e", row->values[row->n - 1]); // avoid trailing return
fprintf(stream, "\n");
}
/// @brief Writes a numeric values to a file using a csv-format
/// @param filepath file to be written including the desired extension
/// @param table table with the rows to be written
/// @param del delimiter between individual values
/// @param header informative comment in the output file
/// @param comments character signalling that a line is a comment (if used as first character of the line )
static int iueio_savetxt(const char* filepath, const struct Table* table, char del, const char* header, char comments) {
// check if data is present
if (table->n == 0)
return EXIT_SUCCESS;
// open file
FILE* stream = fopen(filepath, "w");
if (stream == NULL)
return EXIT_FAILURE;
// write header lines
if (header[0] != '\0') {
char* tmp = malloc((strlen(header) + 1) * sizeof(char));
strcpy(tmp, header);
char* pos = strtok(tmp, "\n");
while (pos != NULL) {
fprintf(stream, "%c %s%c\n", comments, pos, del);
pos = strtok(NULL, "\n");
}
free(tmp);
}
// write data
for (size_t i = 0; i != table->n; ++i) {
row_serialize(&table->rows[i], stream, del);
}
fclose(stream);
return EXIT_SUCCESS;
}
/// @brief Serializes a Row from a stream
static int row_deserialize(struct Row* row, const char* line, char del) {
assert(row->n == 0);
char format[32];
// snprintf(format, sizeof(format) / sizeof(char), "%%lf %c", del);
double value = NAN;
const char* iter = line;
int n = 0;
while (sscanf(iter, "%lf %n", &value, &n) != EOF) {
iter += n;
row->values = realloc(row->values, sizeof(double) * ++row->n);
row->values[row->n - 1] = value;
char format[16];
snprintf(format, sizeof(format) / sizeof(char), " %%*[%c] %%n", del);
sscanf(iter, format, &n);
iter += n;
}
return EXIT_SUCCESS;
}
/// @brief Read numeric values stored in a csv-format
/// @param filepath input file containing the numeric values
/// @param table table to be read into (must be empty)
/// @param del delimiter between individual values in a row
/// @return integral values signalling success or failure
static int iueio_loadtxt(const char* filepath, struct Table* table, char del, char comment) {
// check if table is empty
assert(table->n == 0);
// open file
FILE* stream = fopen(filepath, "r");
if (stream == NULL)
return EXIT_FAILURE;
// read from stream row-by-row (fixed size buffer)
static char line[1024];
// read each line into a row
while (fgets(&line[0], sizeof(line) / sizeof(char), stream)) {
if (line[0] == comment)
continue;
struct Row row = {NULL, 0};
int n = row_deserialize(&row, line, del);
table_append_move(table, &row);
}
fclose(stream);
return EXIT_SUCCESS;
}
+63
View File
@@ -0,0 +1,63 @@
#include "iue-io/ccsv.h"
#include <assert.h> // assert
#include <stddef.h> // size_t
int main(void) {
// prep input data
struct Table table1 = {NULL, 0};
{
double data[3] = {1, 2, 3};
table_append_copy(&table1, data, 3);
}
{
double data[4] = {10, 20, 30, 40};
table_append_copy(&table1, data, 4);
}
{
double data[4] = {1e3, 2e3, 3e3, 4e3};
table_append_copy(&table1, data, 4);
}
{ // write
int res = iueio_savetxt("ccsv_test.csv", &table1, ';', "this is a multiline \n header comment", '#');
if (res != EXIT_SUCCESS)
return EXIT_FAILURE;
}
struct Table table2 = {NULL, 0};
{ // read
int res = iueio_loadtxt("ccsv_test.csv", &table2, ';', '#');
if (res != EXIT_SUCCESS)
return EXIT_FAILURE;
for (size_t i = 0; i != table2.n; ++i) {
for (size_t j = 0; j != table2.rows[i].n; ++j)
printf("%lf ", table2.rows[i].values[j]);
printf("\n");
}
}
// compare
assert(table1.n == table2.n);
for (size_t r = 0; r != table1.n; ++r) {
assert(table1.rows[r].n == table2.rows[r].n);
for (size_t c = 0; c != table1.rows[r].n; ++c) {
assert(table1.rows[r].values[c] == table2.rows[r].values[c]);
}
}
table_clear(&table1);
table_clear(&table2);
return EXIT_SUCCESS;
}
+99
View File
@@ -0,0 +1,99 @@
#pragma once
#include <filesystem> // std::filesystem::path
#include <fstream> // std::ofstream|ifstream
#include <iomanip> // std::scientifc|setprecision
#include <iostream> // std::cout|endl
#include <limits> // std::numeric_limits
#include <sstream> // std::istringstream
#include <string> // std::string
#include <vector> // std::vector
namespace iue::io {
/// @brief Writes a numeric values to a file using a csv-format
/// @param filepath file to be written including the desired extension
/// @param table table with the rows to be written
/// @param del delimiter between individual values
/// @param header informative comment in the output file
/// @param comments character signalling that a line is a comment (if used as first character of the line )
inline void savetxt(std::filesystem::path filepath, const std::vector<std::vector<double>>& table, char del = ' ',
std::string header = "", char comments = '#') {
// setup row writer callable
auto serialize = [&del](const std::vector<double>& row) -> std::string {
std::ostringstream oss;
for (auto iter = row.begin(); iter != std::prev(row.end()); ++iter)
oss << std::scientific << std::setprecision(18) << *iter << del;
oss << std::scientific << std::setprecision(18) << *std::prev(row.end()); // avoid trailing return
oss << std::endl;
return oss.str();
};
// check if data is present
if (table.empty())
return;
// open file
std::ofstream stream;
stream.exceptions(std::ifstream::badbit);
stream.open(filepath);
// write header lines
if (!header.empty()) {
std::string line;
std::istringstream iss(header);
while (std::getline(iss, line)) {
stream << comments << ' ' << line << std::endl;
}
}
// write data
for (const auto& row : table)
stream << serialize(row);
}
/// @brief Read numeric values stored in a csv-format
/// @param filepath input file containing the numeric values
/// @param del delimiter between individual values
/// @param comments character signalling that a line is a comment (if used as first character of the line )
/// @return table which was read
inline std::vector<std::vector<double>> loadtxt(std::filesystem::path filepath, char del = ' ', char comments = '#') {
// setup row parser callable
auto deserialize = [&del](const std::string& line) -> std::vector<double> {
std::vector<double> res;
std::stringstream stream(line);
std::string item;
while (std::getline(stream, item, del)) {
std::stringstream ss(item);
ss.exceptions(std::ios::failbit);
auto value = std::numeric_limits<double>::quiet_NaN();
ss >> value;
res.push_back(value);
}
return res;
};
// setup table
std::vector<std::vector<double>> table;
// open file
std::ifstream stream;
stream.exceptions(std::ifstream::badbit);
stream.open(filepath);
// read from stream row-by-row
std::string line;
// read line to row vectors
while (std::getline(stream, line)) {
if (line.front() == comments)
continue;
table.push_back(deserialize(line));
}
return table;
}
} // namespace iue::io
+73
View File
@@ -0,0 +1,73 @@
#include "iue-io/csv.hpp"
#include <cassert> // assert
#include <cstddef> // std::size_t
#include <iostream> // std::cout|endl
#include <stdlib.h>
#include <vector> // std::vector
int main() {
// prep input data
std::vector<std::vector<double>> table1;
{
auto row = std::vector<double>{1, 2, 3};
table1.push_back(row);
}
{
auto row = std::vector<double>{10, 20, 30, 40};
table1.push_back(row);
}
{
auto row = std::vector<double>{1e3, 2e3, 3e3, 4e3};
table1.push_back(row);
}
// write
try {
for (const auto& row : table1) {
for (const auto& value : row)
std::cout << value << " ";
std::cout << std::endl;
}
iue::io::savetxt("csv_test.csv", table1, ';', "this is a multiline \n header comment");
} catch (const std::exception& e) {
std::cout << e.what();
}
std::vector<std::vector<double>> table2;
// read
try {
table2 = iue::io::loadtxt("csv_test.csv", ';');
for (const auto& row : table2) {
for (const auto& value : row)
std::cout << value << " ";
std::cout << std::endl;
}
} catch (const std::exception& e) {
std::cout << e.what();
}
// compare
assert(table1.size() == table2.size());
for (size_t r = 0; r != table1.size(); ++r) {
assert(table1[r].size() == table2[r].size());
for (size_t c = 0; c != table1[r].size(); ++c) {
assert(table1[r][c] == table2[r][c]);
}
}
return EXIT_SUCCESS;
}