OGS
CsvInterface.h
Go to the documentation of this file.
1// SPDX-FileCopyrightText: Copyright (c) OpenGeoSys Community (opengeosys.org)
2// SPDX-License-Identifier: BSD-3-Clause
3
4#pragma once
5
6#include <any>
7#include <array>
8#include <fstream>
9#include <iterator>
10#include <limits>
11#include <list>
12#include <string>
13#include <typeinfo>
14#include <vector>
15
16#include "BaseLib/IO/Writer.h"
17#include "BaseLib/Logging.h"
18#include "BaseLib/StringTools.h"
19
20namespace GeoLib
21{
22class Point;
23}
24
25namespace FileIO
26{
31{
32public:
35
37 std::size_t getNArrays() const { return _vec_names.size(); }
38
41 static std::vector<std::string> getColumnNames(std::string const& fname,
42 char delim);
43
45 void addIndexVectorForWriting(std::size_t s);
46
48 void setCsvHeader(bool write_header) { _writeCsvHeader = write_header; }
49
53 template <typename T>
54 bool addVectorForWriting(std::string const& vec_name,
55 std::vector<T> const& vec)
56 {
57 static_assert(
58 std::is_same_v<T, std::string> || std::is_same_v<T, double> ||
59 std::is_same_v<T, int>,
60 "CsvInterface can only write vectors of strings, doubles or ints.");
61
62 if (!_data.empty())
63 {
64 std::size_t const vec_size(getVectorSize(0));
65 if (vec_size != vec.size())
66 {
67 ERR("Vector size does not match existing data (should be "
68 "{:d}).",
69 vec_size);
70 return false;
71 }
72 }
73
74 _vec_names.push_back(vec_name);
75 _data.push_back(vec);
76 return true;
77 }
78
80 bool write() override;
81
91 static int readPoints(std::string const& fname, char delim,
92 std::vector<GeoLib::Point*>& points);
93
109 static int readPoints(std::string const& fname, char delim,
110 std::vector<GeoLib::Point*>& points,
111 std::string const& x_column_name,
112 std::string const& y_column_name,
113 std::string const& z_column_name = "");
114
129 static int readPoints(
130 std::string const& fname, char delim,
131 std::vector<GeoLib::Point*>& points, std::size_t x_column_idx,
132 std::size_t y_column_idx,
133 std::size_t z_column_idx = std::numeric_limits<std::size_t>::max());
134
144 template <typename T>
145 static std::pair<int, std::vector<T>> readColumn(
146 std::string const& fname, char delim, std::string const& column_name)
147 {
148 std::ifstream in(fname.c_str());
149 if (!in.is_open())
150 {
151 ERR("CsvInterface::readColumn(): Could not open file {:s}.", fname);
152 return {-1, {}};
153 }
154
155 std::string line;
156 std::getline(in, line);
157 std::size_t const column_idx =
158 CsvInterface::findColumn(line, delim, column_name);
159 if (column_idx == std::numeric_limits<std::size_t>::max())
160 {
161 ERR("Column '{:s}' not found in file header.", column_name);
162 return {-1, {}};
163 }
164 return readColumn<T>(in, delim, column_idx);
165 }
166
167 template <typename T>
168 static std::pair<int, std::vector<T>> readColumn(std::string const& fname,
169 char delim,
170 std::size_t column_idx)
171 {
172 std::ifstream in(fname.c_str());
173 if (!in.is_open())
174 {
175 ERR("CsvInterface::readColumn(): Could not open file {:s}.", fname);
176 return {-1, {}};
177 }
178 return readColumn<T>(in, delim, column_idx);
179 }
180
181private:
183 static int readPoints(std::ifstream& in, char delim,
184 std::vector<GeoLib::Point*>& points,
185 std::array<std::size_t, 3> const& column_idx);
186
188 template <typename T>
189 static std::pair<int, std::vector<T>> readColumn(std::ifstream& in,
190 char delim,
191 std::size_t column_idx)
192 {
193 std::vector<T> data_array;
194 std::string line;
195 std::size_t line_count(0);
196 int error_count(0);
197 while (std::getline(in, line))
198 {
199 line_count++;
200 std::list<std::string> const fields =
201 BaseLib::splitString(line, delim);
202
203 if (fields.size() < column_idx + 1)
204 {
205 ERR("Line {:d} contains not enough columns of data. Skipping "
206 "line...",
207 line_count);
208 error_count++;
209 continue;
210 }
211 auto it = fields.begin();
212 std::advance(it, column_idx);
213
214 std::istringstream stream(*it);
215 T value;
216 if (!(stream >> value))
217 {
218 ERR("Error reading value in line {:d}.", line_count);
219 error_count++;
220 continue;
221 }
222
223 data_array.push_back(value);
224 }
225 return {error_count, data_array};
226 }
227
230 static std::size_t findColumn(std::string const& line, char delim,
231 std::string const& column_name);
232
234 std::size_t getVectorSize(std::size_t idx) const;
235
241 void writeValue(std::size_t vec_idx, std::size_t in_vec_idx);
242
243 bool _writeCsvHeader{true};
244 std::vector<std::string> _vec_names;
245 std::vector<std::any> _data;
246};
247
248} // namespace FileIO
void ERR(fmt::format_string< Args... > fmt, Args &&... args)
Definition Logging.h:40
Base class which enables writing an object to string, stringstream or file.
Definition Writer.h:21
std::vector< std::any > _data
void writeValue(std::size_t vec_idx, std::size_t in_vec_idx)
std::vector< std::string > _vec_names
static std::vector< std::string > getColumnNames(std::string const &fname, char delim)
static std::pair< int, std::vector< T > > readColumn(std::string const &fname, char delim, std::size_t column_idx)
bool addVectorForWriting(std::string const &vec_name, std::vector< T > const &vec)
void setCsvHeader(bool write_header)
Stores if the CSV file to be written should include a header or not.
static std::size_t findColumn(std::string const &line, char delim, std::string const &column_name)
void addIndexVectorForWriting(std::size_t s)
Adds an index vector of size s to the CSV file.
static std::pair< int, std::vector< T > > readColumn(std::ifstream &in, char delim, std::size_t column_idx)
Actual column reader for public methods.
std::size_t getVectorSize(std::size_t idx) const
Returns the size of the vector with the given index.
std::size_t getNArrays() const
Returns the number of vectors currently staged for writing.
CsvInterface()
Constructor (only needed for writing files)
bool write() override
Writes the CSV file.
static std::pair< int, std::vector< T > > readColumn(std::string const &fname, char delim, std::string const &column_name)
static int readPoints(std::string const &fname, char delim, std::vector< GeoLib::Point * > &points)
std::vector< std::string > splitString(std::string const &str)