OGS
FileIO::CsvInterface Class Reference

Detailed Description

Interface for reading CSV file formats.

Definition at line 40 of file CsvInterface.h.

#include <CsvInterface.h>

Inheritance diagram for FileIO::CsvInterface:
[legend]
Collaboration diagram for FileIO::CsvInterface:
[legend]

Public Member Functions

 CsvInterface ()
 Constructor (only needed for writing files)
 
std::size_t getNArrays () const
 Returns the number of vectors currently staged for writing.
 
void addIndexVectorForWriting (std::size_t s)
 Adds an index vector of size s to the CSV file.
 
void setCsvHeader (bool write_header)
 Stores if the CSV file to be written should include a header or not.
 
template<typename T >
bool addVectorForWriting (std::string const &vec_name, std::vector< T > const &vec)
 
bool write () override
 Writes the CSV file.
 
- Public Member Functions inherited from BaseLib::IO::Writer
 Writer ()
 
virtual ~Writer ()=default
 
std::string writeToString ()
 Writes the object to a string.
 

Static Public Member Functions

static std::vector< std::string > getColumnNames (std::string const &fname, char delim)
 
static int readPoints (std::string const &fname, char delim, std::vector< GeoLib::Point * > &points)
 
static int readPoints (std::string const &fname, char delim, std::vector< GeoLib::Point * > &points, std::string const &x_column_name, std::string const &y_column_name, std::string const &z_column_name="")
 
static int readPoints (std::string const &fname, char delim, std::vector< GeoLib::Point * > &points, std::size_t x_column_idx, std::size_t y_column_idx, std::size_t z_column_idx=std::numeric_limits< std::size_t >::max())
 
template<typename T >
static std::pair< int, std::vector< T > > readColumn (std::string const &fname, char delim, std::string const &column_name)
 
template<typename T >
static std::pair< int, std::vector< T > > readColumn (std::string const &fname, char delim, std::size_t column_idx)
 

Private Member Functions

std::size_t getVectorSize (std::size_t idx) const
 Returns the size of the vector with the given index.
 
void writeValue (std::size_t vec_idx, std::size_t in_vec_idx)
 

Static Private Member Functions

static int readPoints (std::ifstream &in, char delim, std::vector< GeoLib::Point * > &points, std::array< std::size_t, 3 > const &column_idx)
 Actual point reader for public methods.
 
template<typename T >
static std::pair< int, std::vector< T > > readColumn (std::ifstream &in, char delim, std::size_t column_idx)
 Actual column reader for public methods.
 
static std::size_t findColumn (std::string const &line, char delim, std::string const &column_name)
 

Private Attributes

bool _writeCsvHeader {true}
 
std::vector< std::string > _vec_names
 
std::vector< std::any > _data
 

Additional Inherited Members

- Protected Attributes inherited from BaseLib::IO::Writer
std::ostringstream out
 The stream to write to.
 

Constructor & Destructor Documentation

◆ CsvInterface()

FileIO::CsvInterface::CsvInterface ( )
default

Constructor (only needed for writing files)

Member Function Documentation

◆ addIndexVectorForWriting()

void FileIO::CsvInterface::addIndexVectorForWriting ( std::size_t s)

Adds an index vector of size s to the CSV file.

Definition at line 254 of file CsvInterface.cpp.

255{
256 std::vector<int> idx_vec(s);
257 std::iota(idx_vec.begin(), idx_vec.end(), 0);
258 addVectorForWriting("Index", idx_vec);
259}
bool addVectorForWriting(std::string const &vec_name, std::vector< T > const &vec)

References addVectorForWriting().

Referenced by FileIO::SwmmInterface::writeCsvForObject(), and FileIO::SwmmInterface::writeCsvForTimestep().

◆ addVectorForWriting()

template<typename T >
bool FileIO::CsvInterface::addVectorForWriting ( std::string const & vec_name,
std::vector< T > const & vec )
inline

Adds a data vector to the CSV file. All data vectors have to have the same size. Vectors will be written in the same sequence they have been added to the interface.

Definition at line 64 of file CsvInterface.h.

66 {
67 static_assert(
68 std::is_same_v<T, std::string> || std::is_same_v<T, double> ||
69 std::is_same_v<T, int>,
70 "CsvInterface can only write vectors of strings, doubles or ints.");
71
72 if (!_data.empty())
73 {
74 std::size_t const vec_size(getVectorSize(0));
75 if (vec_size != vec.size())
76 {
77 ERR("Vector size does not match existing data (should be "
78 "{:d}).",
79 vec_size);
80 return false;
81 }
82 }
83
84 _vec_names.push_back(vec_name);
85 _data.push_back(vec);
86 return true;
87 }
void ERR(fmt::format_string< Args... > fmt, Args &&... args)
Definition Logging.h:45
std::vector< std::any > _data
std::vector< std::string > _vec_names
std::size_t getVectorSize(std::size_t idx) const
Returns the size of the vector with the given index.

References _data, _vec_names, ERR(), and getVectorSize().

Referenced by addIndexVectorForWriting(), FileIO::SwmmInterface::writeCsvForObject(), and FileIO::SwmmInterface::writeCsvForTimestep().

◆ findColumn()

std::size_t FileIO::CsvInterface::findColumn ( std::string const & line,
char delim,
std::string const & column_name )
staticprivate

Returns the number of the column with column_name (or std::numeric_limits::max() if no such column has been found).

Definition at line 226 of file CsvInterface.cpp.

228{
229 std::list<std::string> const fields = BaseLib::splitString(line, delim);
230 if (fields.empty())
231 {
232 return std::numeric_limits<std::size_t>::max();
233 }
234
235 std::size_t count(0);
236 for (const auto& field : fields)
237 {
238 if (field == column_name)
239 {
240 break;
241 }
242
243 count++;
244 }
245
246 if (count == fields.size())
247 {
248 return std::numeric_limits<std::size_t>::max();
249 }
250
251 return count;
252}
std::vector< std::string > splitString(std::string const &str)

References BaseLib::splitString().

Referenced by readColumn(), and readPoints().

◆ getColumnNames()

std::vector< std::string > FileIO::CsvInterface::getColumnNames ( std::string const & fname,
char delim )
static

Returns a vector containing the names of columns in the file (assuming the file has a header)

Definition at line 25 of file CsvInterface.cpp.

27{
28 std::ifstream in(fname.c_str());
29
30 if (!in.is_open())
31 {
32 ERR("CsvInterface::getColumnNames(): Could not open file {:s}.", fname);
33 return std::vector<std::string>();
34 }
35 std::string line;
36 if (!std::getline(in, line))
37 {
38 ERR("CsvInterface::getColumnNames(): Could not read line from file "
39 "{:s}. Is it empty?",
40 fname);
41 return {};
42 }
43
44 if (delim == '\n')
45 {
46 return {};
47 }
48
49 std::list<std::string> fields = BaseLib::splitString(line, delim);
50 if (fields.size() < 2)
51 {
52 for (char const d : {'\t', ';', ','})
53 {
54 fields = BaseLib::splitString(line, d);
55 if (fields.size() > 1)
56 {
57 break;
58 }
59 }
60 }
61 return {begin(fields), end(fields)};
62}

References ERR(), and BaseLib::splitString().

◆ getNArrays()

std::size_t FileIO::CsvInterface::getNArrays ( ) const
inline

Returns the number of vectors currently staged for writing.

Definition at line 47 of file CsvInterface.h.

47{ return _vec_names.size(); }

References _vec_names.

Referenced by FileIO::SwmmInterface::writeCsvForObject(), and FileIO::SwmmInterface::writeCsvForTimestep().

◆ getVectorSize()

std::size_t FileIO::CsvInterface::getVectorSize ( std::size_t idx) const
private

Returns the size of the vector with the given index.

Definition at line 295 of file CsvInterface.cpp.

296{
297 if (_data[idx].type() == typeid(std::vector<std::string>))
298 {
299 return std::any_cast<std::vector<std::string>>(_data[idx]).size();
300 }
301 if (_data[idx].type() == typeid(std::vector<double>))
302 {
303 return std::any_cast<std::vector<double>>(_data[idx]).size();
304 }
305 if (_data[idx].type() == typeid(std::vector<int>))
306 {
307 return std::any_cast<std::vector<int>>(_data[idx]).size();
308 }
309 return 0;
310}
constexpr int size(int const displacement_dim)
Vectorized tensor size for given displacement dimension.

References _data.

Referenced by addVectorForWriting(), and write().

◆ readColumn() [1/3]

template<typename T >
static std::pair< int, std::vector< T > > FileIO::CsvInterface::readColumn ( std::ifstream & in,
char delim,
std::size_t column_idx )
inlinestaticprivate

Actual column reader for public methods.

Definition at line 199 of file CsvInterface.h.

202 {
203 std::vector<T> data_array;
204 std::string line;
205 std::size_t line_count(0);
206 int error_count(0);
207 while (std::getline(in, line))
208 {
209 line_count++;
210 std::list<std::string> const fields =
211 BaseLib::splitString(line, delim);
212
213 if (fields.size() < column_idx + 1)
214 {
215 ERR("Line {:d} contains not enough columns of data. Skipping "
216 "line...",
217 line_count);
218 error_count++;
219 continue;
220 }
221 auto it = fields.begin();
222 std::advance(it, column_idx);
223
224 std::istringstream stream(*it);
225 T value;
226 if (!(stream >> value))
227 {
228 ERR("Error reading value in line {:d}.", line_count);
229 error_count++;
230 continue;
231 }
232
233 data_array.push_back(value);
234 }
235 return {error_count, data_array};
236 }

References ERR(), and BaseLib::splitString().

◆ readColumn() [2/3]

template<typename T >
static std::pair< int, std::vector< T > > FileIO::CsvInterface::readColumn ( std::string const & fname,
char delim,
std::size_t column_idx )
inlinestatic

Definition at line 178 of file CsvInterface.h.

181 {
182 std::ifstream in(fname.c_str());
183 if (!in.is_open())
184 {
185 ERR("CsvInterface::readColumn(): Could not open file {:s}.", fname);
186 return {-1, {}};
187 }
188 return readColumn<T>(in, delim, column_idx);
189 }

References ERR().

◆ readColumn() [3/3]

template<typename T >
static std::pair< int, std::vector< T > > FileIO::CsvInterface::readColumn ( std::string const & fname,
char delim,
std::string const & column_name )
inlinestatic

Reads a column of the given name from a CSV file.

Parameters
fnameName of the file to be read
delimDeliminator, default is ','
column_nameThe column's name to read
Returns
An error code (0 = ok, 0<i<max = number of skipped lines, -1 error reading file) and an possibly empty vector containing the read values

Definition at line 155 of file CsvInterface.h.

157 {
158 std::ifstream in(fname.c_str());
159 if (!in.is_open())
160 {
161 ERR("CsvInterface::readColumn(): Could not open file {:s}.", fname);
162 return {-1, {}};
163 }
164
165 std::string line;
166 std::getline(in, line);
167 std::size_t const column_idx =
168 CsvInterface::findColumn(line, delim, column_name);
169 if (column_idx == std::numeric_limits<std::size_t>::max())
170 {
171 ERR("Column '{:s}' not found in file header.", column_name);
172 return {-1, {}};
173 }
174 return readColumn<T>(in, delim, column_idx);
175 }
static std::size_t findColumn(std::string const &line, char delim, std::string const &column_name)

References ERR(), and findColumn().

◆ readPoints() [1/4]

int FileIO::CsvInterface::readPoints ( std::ifstream & in,
char delim,
std::vector< GeoLib::Point * > & points,
std::array< std::size_t, 3 > const & column_idx )
staticprivate

Actual point reader for public methods.

Definition at line 172 of file CsvInterface.cpp.

175{
176 std::array<std::size_t, 3> order = {{0, 1, 2}};
177 std::sort(order.begin(), order.end(),
178 [&column_idx](std::size_t idx1, std::size_t idx2)
179 { return column_idx[idx1] < column_idx[idx2]; });
180 std::array<std::size_t, 3> const column_advance = {
181 {column_idx[order[0]], column_idx[order[1]] - column_idx[order[0]],
182 column_idx[order[2]] - column_idx[order[1]]}};
183
184 std::string line;
185 std::size_t line_count(0);
186 std::size_t error_count(0);
187 std::list<std::string>::const_iterator it;
188
189 while (std::getline(in, line))
190 {
191 line_count++;
192 std::list<std::string> const fields = BaseLib::splitString(line, delim);
193
194 if (fields.size() < column_idx[order[2]] + 1)
195 {
196 ERR("Line {:d} contains not enough columns of data. Skipping "
197 "line...",
198 line_count);
199 error_count++;
200 continue;
201 }
202
203 it = fields.begin();
204 try
205 {
206 std::advance(it, column_advance[0]);
207 std::array<double, 3> point{};
208 point[order[0]] = std::stod(*it);
209 std::advance(it, column_advance[1]);
210 point[order[1]] = std::stod(*it);
211 std::advance(it, column_advance[2]);
212 point[order[2]] =
213 (column_idx[1] == column_idx[2]) ? 0 : std::stod(*it);
214 points.push_back(new GeoLib::Point(point[0], point[1], point[2]));
215 }
216 catch (const std::invalid_argument&)
217 {
218 ERR("Error converting data to coordinates in line {:d}.",
219 line_count);
220 error_count++;
221 }
222 }
223 return error_count;
224}

References ERR(), and BaseLib::splitString().

◆ readPoints() [2/4]

int FileIO::CsvInterface::readPoints ( std::string const & fname,
char delim,
std::vector< GeoLib::Point * > & points )
static

Reads 3D points from a CSV file. It is assumed that the file has a header specifying a name for each of the columns. The first three columns will be interpreted as x-, y- and z-coordinate, respectively.

Parameters
fnameName of the file to be read
delimDeliminator, default is ','
pointsA vector containing the 3D points read from the file
Returns
An error code (0 = ok, 0<i<max = number of skipped lines, -1 error reading file)

Definition at line 64 of file CsvInterface.cpp.

66{
67 std::ifstream in(fname.c_str());
68
69 if (!in.is_open())
70 {
71 ERR("CsvInterface::readPoints(): Could not open file {:s}.", fname);
72 return -1;
73 }
74
75 std::string line;
76 std::getline(in, line);
77
78 std::size_t line_count(0);
79 std::size_t error_count(0);
80 std::list<std::string>::const_iterator it;
81 while (std::getline(in, line))
82 {
83 line_count++;
84 std::list<std::string> const fields = BaseLib::splitString(line, delim);
85
86 if (fields.size() < 3)
87 {
88 ERR("Line {:d} contains not enough columns of data. Skipping "
89 "line...",
90 line_count);
91 error_count++;
92 continue;
93 }
94 it = fields.begin();
95 try
96 {
97 std::array<double, 3> point{};
98 point[0] = std::stod(*it);
99 point[1] = std::stod(*(++it));
100 point[2] = std::stod(*(++it));
101 points.push_back(new GeoLib::Point(point[0], point[1], point[2]));
102 }
103 catch (const std::invalid_argument&)
104 {
105 ERR("Error converting data to coordinates in line {:d}.",
106 line_count);
107 }
108 }
109 return error_count;
110}

References ERR(), and BaseLib::splitString().

Referenced by readPoints(), and readPoints().

◆ readPoints() [3/4]

int FileIO::CsvInterface::readPoints ( std::string const & fname,
char delim,
std::vector< GeoLib::Point * > & points,
std::size_t x_column_idx,
std::size_t y_column_idx,
std::size_t z_column_idx = std::numeric_limits<std::size_t>::max() )
static

Reads 3D points from a headerless CSV file, so columns for x-, y- and z-coordinates have to be specified using indices (starting with 0). If z_column_idx is not given (or set to numeric_limits::max()), all z-coordinates will be set to zero.

Parameters
fnameName of the file to be read
delimDeliminator, default is ','
pointsA vector containing the 3D points read from the file
x_column_idxIndex of the column to be interpreted as x-coordinate
y_column_idxIndex of the column to be interpreted as y-coordinate
z_column_idxIndex of the column to be interpreted as z-coordinate
Returns
An error code (0 = ok, 0<i<max = number of skipped lines, -1 error reading file)

Definition at line 149 of file CsvInterface.cpp.

153{
154 std::ifstream in(fname.c_str());
155
156 if (!in.is_open())
157 {
158 ERR("CsvInterface::readPoints(): Could not open file {:s}.", fname);
159 return -1;
160 }
161
162 if (z_column_idx == std::numeric_limits<std::size_t>::max())
163 {
164 z_column_idx = y_column_idx;
165 }
166 std::array<std::size_t, 3> const column_idx = {
167 {x_column_idx, y_column_idx, z_column_idx}};
168
169 return readPoints(in, delim, points, column_idx);
170}
static int readPoints(std::string const &fname, char delim, std::vector< GeoLib::Point * > &points)

References ERR(), and readPoints().

◆ readPoints() [4/4]

int FileIO::CsvInterface::readPoints ( std::string const & fname,
char delim,
std::vector< GeoLib::Point * > & points,
std::string const & x_column_name,
std::string const & y_column_name,
std::string const & z_column_name = "" )
static

Reads 3D points from a CSV file. It is assumed that the file has a header specifying a name for each of the columns. The columns specified in the function call will be used for reading x-, y- and z-coordinates, respectively If z_column_name is an empty string or not given at all, all z-coordinates will be set to zero.

Parameters
fnameName of the file to be read
delimDeliminator, default is ','
pointsA vector containing the 3D points read from the file
x_column_nameName of the column to be interpreted as x-coordinate
y_column_nameName of the column to be interpreted as y-coordinate
z_column_nameName of the column to be interpreted as z-coordinate
Returns
An error code (0 = ok, 0<i<max = number of skipped lines, -1 error reading file)

Definition at line 112 of file CsvInterface.cpp.

117{
118 std::ifstream in(fname.c_str());
119 std::array<std::string, 3> const column_names = {
120 {x_column_name, y_column_name, z_column_name}};
121
122 if (!in.is_open())
123 {
124 ERR("CsvInterface::readPoints(): Could not open file {:s}.", fname);
125 return -1;
126 }
127
128 std::string line;
129 std::getline(in, line);
130 std::array<std::size_t, 3> const column_idx = {
131 {CsvInterface::findColumn(line, delim, x_column_name),
132 CsvInterface::findColumn(line, delim, y_column_name),
133 (z_column_name.empty())
134 ? CsvInterface::findColumn(line, delim, y_column_name)
135 : CsvInterface::findColumn(line, delim, z_column_name)}};
136
137 for (std::size_t i = 0; i < 3; ++i)
138 {
139 if (column_idx[i] == std::numeric_limits<std::size_t>::max())
140 {
141 ERR("Column '{:s}' not found in file header.", column_names[i]);
142 return -1;
143 }
144 }
145
146 return readPoints(in, delim, points, column_idx);
147}

References ERR(), findColumn(), and readPoints().

◆ setCsvHeader()

void FileIO::CsvInterface::setCsvHeader ( bool write_header)
inline

Stores if the CSV file to be written should include a header or not.

Definition at line 58 of file CsvInterface.h.

58{ _writeCsvHeader = write_header; }

References _writeCsvHeader.

◆ write()

bool FileIO::CsvInterface::write ( )
overridevirtual

Writes the CSV file.

Implements BaseLib::IO::Writer.

Definition at line 261 of file CsvInterface.cpp.

262{
263 if (_data.empty())
264 {
265 ERR("CsvInterface::write() - No data to write.");
266 return false;
267 }
268
269 std::size_t const n_vecs(_data.size());
270 std::size_t const vec_size(getVectorSize(0));
271
272 if (_writeCsvHeader)
273 {
274 out << _vec_names[0];
275 for (std::size_t i = 1; i < n_vecs; ++i)
276 {
277 out << "\t" << _vec_names[i];
278 }
279 out << "\n";
280 }
281
282 for (std::size_t j = 0; j < vec_size; ++j)
283 {
284 writeValue(0, j);
285 for (std::size_t i = 1; i < n_vecs; ++i)
286 {
287 out << "\t";
288 writeValue(i, j);
289 }
290 out << "\n";
291 }
292 return true;
293}
std::ostringstream out
The stream to write to.
Definition Writer.h:47
void writeValue(std::size_t vec_idx, std::size_t in_vec_idx)

References _data, _vec_names, _writeCsvHeader, ERR(), getVectorSize(), BaseLib::IO::Writer::out, and writeValue().

◆ writeValue()

void FileIO::CsvInterface::writeValue ( std::size_t vec_idx,
std::size_t in_vec_idx )
private

Writes a value from a vector to the file.

Parameters
vec_idxIndex of the vector
in_vec_idxEntry in the selected vector

Definition at line 312 of file CsvInterface.cpp.

313{
314 if (_data[vec_idx].type() == typeid(std::vector<std::string>))
315 {
316 out << std::any_cast<std::vector<std::string>>(
317 _data[vec_idx])[in_vec_idx];
318 }
319 else if (_data[vec_idx].type() == typeid(std::vector<double>))
320 {
321 out << std::any_cast<std::vector<double>>(_data[vec_idx])[in_vec_idx];
322 }
323 else if (_data[vec_idx].type() == typeid(std::vector<int>))
324 {
325 out << std::any_cast<std::vector<int>>(_data[vec_idx])[in_vec_idx];
326 }
327}

References _data.

Referenced by write().

Member Data Documentation

◆ _data

std::vector<std::any> FileIO::CsvInterface::_data
private

Definition at line 255 of file CsvInterface.h.

Referenced by addVectorForWriting(), getVectorSize(), write(), and writeValue().

◆ _vec_names

std::vector<std::string> FileIO::CsvInterface::_vec_names
private

Definition at line 254 of file CsvInterface.h.

Referenced by addVectorForWriting(), getNArrays(), and write().

◆ _writeCsvHeader

bool FileIO::CsvInterface::_writeCsvHeader {true}
private

Definition at line 253 of file CsvInterface.h.

253{true};

Referenced by setCsvHeader(), and write().


The documentation for this class was generated from the following files: