#pragma once #include // assert #include // NAN #include // size_t #include // FILE|snprintf #include // EXIT_FAILURE|EXIT_SUCCESS #include // 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; }