A Discrete-Event Network Simulator
API
Loading...
Searching...
No Matches
csv-reader.cc
Go to the documentation of this file.
1/*
2 * Copyright (c) 2019 Lawrence Livermore National Laboratory
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 as
6 * published by the Free Software Foundation;
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program; if not, write to the Free Software
15 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
16 *
17 * Author: Mathew Bielejeski <bielejeski1@llnl.gov>
18 */
19
20#include "csv-reader.h"
21
22#include "ns3/log.h"
23
24#include <algorithm>
25#include <cctype>
26#include <fstream>
27#include <iterator>
28#include <limits>
29#include <sstream>
30#include <vector>
31
40NS_LOG_COMPONENT_DEFINE("CsvReader");
41
42namespace
43{
44
57template <typename T>
58bool
59GenericTransform(std::string input, T& output)
60{
61 NS_LOG_FUNCTION(input);
62
63 std::istringstream stream(input);
64
65 stream >> output;
66
67 return static_cast<bool>(stream);
68}
69
70} // unnamed namespace
71
72namespace ns3
73{
74
75CsvReader::CsvReader(const std::string& filepath, char delimiter /* =',' */)
76 : m_delimiter(delimiter),
77 m_rowsRead(0),
78 m_fileStream(filepath),
79 m_stream(&m_fileStream)
80{
81 NS_LOG_FUNCTION(this << filepath);
82}
83
84CsvReader::CsvReader(std::istream& stream, char delimiter /* =',' */)
85 : m_delimiter(delimiter),
86 m_rowsRead(0),
87 m_fileStream(),
88 m_stream(&stream)
89{
90 NS_LOG_FUNCTION(this);
91}
92
94{
95}
96
97std::size_t
99{
100 NS_LOG_FUNCTION(this);
101
102 return m_columns.size();
103}
104
105std::size_t
107{
108 NS_LOG_FUNCTION(this);
109
110 return m_rowsRead;
111}
112
113char
115{
116 NS_LOG_FUNCTION(this);
117
118 return m_delimiter;
119}
120
121bool
123{
124 NS_LOG_FUNCTION(this);
125
126 std::string line;
127
128 if (m_stream->eof())
129 {
130 NS_LOG_LOGIC("Reached end of stream");
131 return false;
132 }
133
134 NS_LOG_LOGIC("Reading line " << m_rowsRead + 1);
135
136 std::getline(*m_stream, line);
137
138 if (m_stream->fail())
139 {
140 NS_LOG_ERROR("Reading line " << m_rowsRead + 1 << " failed");
141
142 return false;
143 }
144
145 ++m_rowsRead;
146
147 ParseLine(line);
148
149 return true;
150}
151
152bool
154{
155 return m_blankRow;
156}
157
158bool
159CsvReader::GetValueAs(std::string input, double& value) const
160{
161 NS_LOG_FUNCTION(this << input);
162
163 return GenericTransform(std::move(input), value);
164}
165
166bool
167CsvReader::GetValueAs(std::string input, float& value) const
168{
169 NS_LOG_FUNCTION(this << input);
170
171 return GenericTransform(std::move(input), value);
172}
173
174bool
175CsvReader::GetValueAs(std::string input, signed char& value) const
176{
177 typedef signed char byte_type;
178
179 NS_LOG_FUNCTION(this << input);
180
181 std::istringstream tempStream(input);
182
183 int16_t tempOutput = 0;
184 tempStream >> tempOutput;
185
186 if (tempOutput >= std::numeric_limits<byte_type>::min() &&
187 tempOutput <= std::numeric_limits<byte_type>::max())
188 {
189 value = static_cast<byte_type>(tempOutput);
190 }
191
192 bool success = static_cast<bool>(tempStream);
193
194 NS_LOG_DEBUG("Input='" << input << "', output=" << tempOutput << ", result=" << success);
195
196 return success;
197}
198
199bool
200CsvReader::GetValueAs(std::string input, short& value) const
201{
202 NS_LOG_FUNCTION(this << input);
203
204 return GenericTransform(std::move(input), value);
205}
206
207bool
208CsvReader::GetValueAs(std::string input, int& value) const
209{
210 NS_LOG_FUNCTION(this << input);
211
212 return GenericTransform(std::move(input), value);
213}
214
215bool
216CsvReader::GetValueAs(std::string input, long& value) const
217{
218 NS_LOG_FUNCTION(this << input);
219
220 return GenericTransform(std::move(input), value);
221}
222
223bool
224CsvReader::GetValueAs(std::string input, long long& value) const
225{
226 NS_LOG_FUNCTION(this << input);
227
228 return GenericTransform(std::move(input), value);
229}
230
231bool
232CsvReader::GetValueAs(std::string input, std::string& value) const
233{
234 NS_LOG_FUNCTION(this << input);
235
236 value = input;
237
238 return true;
239}
240
241bool
242CsvReader::GetValueAs(std::string input, unsigned char& value) const
243{
244 typedef unsigned char byte_type;
245
246 NS_LOG_FUNCTION(this << input);
247
248 std::istringstream tempStream(input);
249
250 uint16_t tempOutput = 0;
251 tempStream >> tempOutput;
252
253 if (tempOutput >= std::numeric_limits<byte_type>::min() &&
254 tempOutput <= std::numeric_limits<byte_type>::max())
255 {
256 value = static_cast<byte_type>(tempOutput);
257 }
258
259 bool success = static_cast<bool>(tempStream);
260
261 NS_LOG_DEBUG("Input='" << input << "', output=" << tempOutput << ", result=" << success);
262
263 return success;
264}
265
266bool
267CsvReader::GetValueAs(std::string input, unsigned short& value) const
268{
269 NS_LOG_FUNCTION(this << input);
270
271 return GenericTransform(std::move(input), value);
272}
273
274bool
275CsvReader::GetValueAs(std::string input, unsigned int& value) const
276{
277 NS_LOG_FUNCTION(this << input);
278
279 return GenericTransform(std::move(input), value);
280}
281
282bool
283CsvReader::GetValueAs(std::string input, unsigned long& value) const
284{
285 NS_LOG_FUNCTION(this << input);
286
287 return GenericTransform(std::move(input), value);
288}
289
290bool
291CsvReader::GetValueAs(std::string input, unsigned long long& value) const
292{
293 NS_LOG_FUNCTION(this << input);
294
295 return GenericTransform(std::move(input), value);
296}
297
298bool
300{
301 NS_LOG_FUNCTION(this << c);
302
303 return c == m_delimiter;
304}
305
306void
307CsvReader::ParseLine(const std::string& line)
308{
309 NS_LOG_FUNCTION(this << line);
310
311 std::string value;
312 m_columns.clear();
313
314 auto start_col = line.begin();
315 auto end_col = line.end();
316
317 while (start_col != line.end())
318 {
319 std::tie(value, end_col) = ParseColumn(start_col, line.end());
320
321 NS_LOG_DEBUG("ParseColumn() returned: " << value);
322
323 m_columns.push_back(std::move(value));
324
325 if (end_col != line.end())
326 {
327 ++end_col;
328 }
329
330 start_col = end_col;
331 }
332 m_blankRow = (m_columns.size() == 1) && (m_columns[0].empty());
333 NS_LOG_LOGIC("blank row: " << m_blankRow);
334}
335
336std::tuple<std::string, std::string::const_iterator>
337CsvReader::ParseColumn(std::string::const_iterator begin, std::string::const_iterator end)
338{
339 NS_LOG_FUNCTION(this << std::string(begin, end));
340
341 enum class State
342 {
343 BEGIN,
344 END_QUOTE,
345 FIND_DELIMITER,
346 QUOTED_STRING,
347 UNQUOTED_STRING,
348 END
349 };
350
351 State state = State::BEGIN;
352 std::string buffer;
353 auto iter = begin;
354
355 while (state != State::END)
356 {
357 if (iter == end)
358 {
359 NS_LOG_DEBUG("Found end iterator, switching to END state");
360
361 state = State::END;
362 continue;
363 }
364
365 auto c = *iter;
366
367 NS_LOG_DEBUG("Next character: '" << c << "'");
368
369 // handle common cases here to avoid duplicating logic
370 if (state != State::QUOTED_STRING)
371 {
372 if (IsDelimiter(c))
373 {
374 NS_LOG_DEBUG("Found field delimiter, switching to END state");
375
376 if (state == State::UNQUOTED_STRING)
377 {
378 NS_LOG_DEBUG("Removing trailing whitespace from unquoted field: '" << buffer
379 << "'");
380 auto len = buffer.size();
381
382 // remove trailing whitespace from the field
383 while (!buffer.empty() &&
384 std::isspace(static_cast<unsigned char>(buffer.back())))
385 {
386 buffer.pop_back();
387 }
388
389 auto finalLen = buffer.size();
390
391 NS_LOG_DEBUG("Removed " << (len - finalLen)
392 << " trailing whitespace characters");
393 }
394
395 state = State::END;
396
397 continue;
398 }
399 else if (c == '#')
400 {
401 NS_LOG_DEBUG("Found start of comment, switching to END state");
402
403 // comments consume the rest of the line, set iter to end
404 // to reflect that fact.
405 iter = end;
406 state = State::END;
407
408 continue;
409 }
410 }
411
412 switch (state)
413 {
414 case State::BEGIN: {
415 if (c == '"')
416 {
417 NS_LOG_DEBUG("Switching state: BEGIN -> QUOTED_STRING");
418
419 state = State::QUOTED_STRING;
420 }
421 else if (!std::isspace(c))
422 {
423 NS_LOG_DEBUG("Switching state: BEGIN -> UNQUOTED_STRING");
424
425 state = State::UNQUOTED_STRING;
426 buffer.push_back(c);
427 }
428 }
429 break;
430 case State::QUOTED_STRING: {
431 if (c == '"')
432 {
433 NS_LOG_DEBUG("Switching state: QUOTED_STRING -> END_QUOTE");
434 state = State::END_QUOTE;
435 }
436 else
437 {
438 buffer.push_back(c);
439 }
440 }
441 break;
442 case State::END_QUOTE: {
443 if (c == '"')
444 {
445 NS_LOG_DEBUG("Switching state: END_QUOTE -> QUOTED_STRING");
446
447 // an escape quote instead of an end quote
448 state = State::QUOTED_STRING;
449 buffer.push_back(c);
450 }
451 else
452 {
453 NS_LOG_DEBUG("Switching state: END_QUOTE -> FIND_DELIMITER");
454 state = State::FIND_DELIMITER;
455 }
456 }
457 break;
458 case State::UNQUOTED_STRING: {
459 buffer.push_back(c);
460 }
461 break;
462 case State::FIND_DELIMITER:
463 break;
464 case State::END:
465 break;
466 }
467
468 ++iter;
469 }
470
471 NS_LOG_DEBUG("Field value: " << buffer);
472
473 return std::make_tuple(buffer, iter);
474}
475
476} // namespace ns3
virtual ~CsvReader()
Destructor.
Definition: csv-reader.cc:93
std::size_t RowNumber() const
The number of lines that have been read.
Definition: csv-reader.cc:106
char Delimiter() const
Returns the delimiter character specified during object construction.
Definition: csv-reader.cc:114
std::istream * m_stream
Pointer to the input stream containing the data.
Definition: csv-reader.h:402
bool IsDelimiter(char c) const
Returns true if the supplied character matches the delimiter.
Definition: csv-reader.cc:299
CsvReader(const std::string &filepath, char delimiter=',')
Constructor.
Definition: csv-reader.cc:75
void ParseLine(const std::string &line)
Scans the string and splits it into individual columns based on the delimiter.
Definition: csv-reader.cc:307
std::size_t ColumnCount() const
Returns the number of columns in the csv data.
Definition: csv-reader.cc:98
std::size_t m_rowsRead
Number of lines processed.
Definition: csv-reader.h:394
bool m_blankRow
Line contains no data (blank line or comment only).
Definition: csv-reader.h:396
bool FetchNextRow()
Reads one line from the input until a new line is encountered.
Definition: csv-reader.cc:122
bool IsBlankRow() const
Check if the current row is blank.
Definition: csv-reader.cc:153
Columns m_columns
Fields extracted from the current line.
Definition: csv-reader.h:395
bool GetValueAs(std::string input, double &value) const
Attempt to convert from the string data stored at the specified column index into the specified type.
Definition: csv-reader.cc:159
char m_delimiter
Character used to separate fields.
Definition: csv-reader.h:393
std::tuple< std::string, std::string::const_iterator > ParseColumn(std::string::const_iterator begin, std::string::const_iterator end)
Extracts the data for one column in a csv row.
Definition: csv-reader.cc:337
ns3::CsvReader declaration
#define NS_LOG_ERROR(msg)
Use NS_LOG to output a message of level LOG_ERROR.
Definition: log.h:254
#define NS_LOG_COMPONENT_DEFINE(name)
Define a Log component with a specific name.
Definition: log.h:202
#define NS_LOG_DEBUG(msg)
Use NS_LOG to output a message of level LOG_DEBUG.
Definition: log.h:268
#define NS_LOG_LOGIC(msg)
Use NS_LOG to output a message of level LOG_LOGIC.
Definition: log.h:282
#define NS_LOG_FUNCTION(parameters)
If log level LOG_FUNCTION is enabled, this macro will output all input parameters separated by ",...
bool GenericTransform(std::string input, T &output)
Convert a string into another type.
Definition: csv-reader.cc:59
Every class exported by the ns3 library is enclosed in the ns3 namespace.
#define END
End of a line.