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
32/**
33 * \file
34 * \ingroup csvreader
35 *
36 * ns3::CsvReader implementation
37 */
38
39NS_LOG_COMPONENT_DEFINE("CsvReader");
40
41namespace
42{
43
44/**
45 * Convert a string into another type.
46 *
47 * Uses a stringstream to deserialize the value stored in \p input
48 * to a value of type T and writes the deserialized value to \p output.
49 *
50 * \tparam T Data type of output.
51 * \param input String containing serialized data.
52 * \param output Place to store deserialized value.
53 *
54 * \return \c true if deserialization was successful, \c false otherwise.
55 */
56template <typename T>
57bool
58GenericTransform(std::string input, T& output)
59{
60 NS_LOG_FUNCTION(input);
61
62 std::istringstream stream(input);
63
64 stream >> output;
65
66 return static_cast<bool>(stream);
67}
68
69} // unnamed namespace
70
71namespace ns3
72{
73
74CsvReader::CsvReader(const std::string& filepath, char delimiter /* =',' */)
75 : m_delimiter(delimiter),
76 m_rowsRead(0),
77 m_fileStream(filepath),
78 m_stream(&m_fileStream)
79{
80 NS_LOG_FUNCTION(this << filepath);
81}
82
83CsvReader::CsvReader(std::istream& stream, char delimiter /* =',' */)
84 : m_delimiter(delimiter),
85 m_rowsRead(0),
86 m_fileStream(),
87 m_stream(&stream)
88{
89 NS_LOG_FUNCTION(this);
90}
91
93{
94}
95
96std::size_t
98{
99 NS_LOG_FUNCTION(this);
100
101 return m_columns.size();
102}
103
104std::size_t
106{
107 NS_LOG_FUNCTION(this);
108
109 return m_rowsRead;
110}
111
112char
114{
115 NS_LOG_FUNCTION(this);
116
117 return m_delimiter;
118}
119
120bool
122{
123 NS_LOG_FUNCTION(this);
124
125 std::string line;
126
127 if (m_stream->eof())
128 {
129 NS_LOG_LOGIC("Reached end of stream");
130 return false;
131 }
132
133 NS_LOG_LOGIC("Reading line " << m_rowsRead + 1);
134
135 std::getline(*m_stream, line);
136
137 if (m_stream->fail())
138 {
139 NS_LOG_ERROR("Reading line " << m_rowsRead + 1 << " failed");
140
141 return false;
142 }
143
144 ++m_rowsRead;
145
146 ParseLine(line);
147
148 return true;
149}
150
151bool
153{
154 return m_blankRow;
155}
156
157bool
158CsvReader::GetValueAs(std::string input, double& value) const
159{
160 NS_LOG_FUNCTION(this << input);
161
162 return GenericTransform(std::move(input), value);
163}
164
165bool
166CsvReader::GetValueAs(std::string input, float& value) const
167{
168 NS_LOG_FUNCTION(this << input);
169
170 return GenericTransform(std::move(input), value);
171}
172
173bool
174CsvReader::GetValueAs(std::string input, signed char& value) const
175{
176 typedef signed char byte_type;
177
178 NS_LOG_FUNCTION(this << input);
179
180 std::istringstream tempStream(input);
181
182 int16_t tempOutput = 0;
183 tempStream >> tempOutput;
184
185 if (tempOutput >= std::numeric_limits<byte_type>::min() &&
186 tempOutput <= std::numeric_limits<byte_type>::max())
187 {
188 value = static_cast<byte_type>(tempOutput);
189 }
190
191 bool success = static_cast<bool>(tempStream);
192
193 NS_LOG_DEBUG("Input='" << input << "', output=" << tempOutput << ", result=" << success);
194
195 return success;
196}
197
198bool
199CsvReader::GetValueAs(std::string input, short& value) const
200{
201 NS_LOG_FUNCTION(this << input);
202
203 return GenericTransform(std::move(input), value);
204}
205
206bool
207CsvReader::GetValueAs(std::string input, int& value) const
208{
209 NS_LOG_FUNCTION(this << input);
210
211 return GenericTransform(std::move(input), value);
212}
213
214bool
215CsvReader::GetValueAs(std::string input, long& value) const
216{
217 NS_LOG_FUNCTION(this << input);
218
219 return GenericTransform(std::move(input), value);
220}
221
222bool
223CsvReader::GetValueAs(std::string input, long long& value) const
224{
225 NS_LOG_FUNCTION(this << input);
226
227 return GenericTransform(std::move(input), value);
228}
229
230bool
231CsvReader::GetValueAs(std::string input, std::string& value) const
232{
233 NS_LOG_FUNCTION(this << input);
234
235 value = input;
236
237 return true;
238}
239
240bool
241CsvReader::GetValueAs(std::string input, unsigned char& value) const
242{
243 typedef unsigned char byte_type;
244
245 NS_LOG_FUNCTION(this << input);
246
247 std::istringstream tempStream(input);
248
249 uint16_t tempOutput = 0;
250 tempStream >> tempOutput;
251
252 if (tempOutput >= std::numeric_limits<byte_type>::min() &&
253 tempOutput <= std::numeric_limits<byte_type>::max())
254 {
255 value = static_cast<byte_type>(tempOutput);
256 }
257
258 bool success = static_cast<bool>(tempStream);
259
260 NS_LOG_DEBUG("Input='" << input << "', output=" << tempOutput << ", result=" << success);
261
262 return success;
263}
264
265bool
266CsvReader::GetValueAs(std::string input, unsigned short& value) const
267{
268 NS_LOG_FUNCTION(this << input);
269
270 return GenericTransform(std::move(input), value);
271}
272
273bool
274CsvReader::GetValueAs(std::string input, unsigned int& value) const
275{
276 NS_LOG_FUNCTION(this << input);
277
278 return GenericTransform(std::move(input), value);
279}
280
281bool
282CsvReader::GetValueAs(std::string input, unsigned long& value) const
283{
284 NS_LOG_FUNCTION(this << input);
285
286 return GenericTransform(std::move(input), value);
287}
288
289bool
290CsvReader::GetValueAs(std::string input, unsigned long long& value) const
291{
292 NS_LOG_FUNCTION(this << input);
293
294 return GenericTransform(std::move(input), value);
295}
296
297bool
299{
300 NS_LOG_FUNCTION(this << c);
301
302 return c == m_delimiter;
303}
304
305void
306CsvReader::ParseLine(const std::string& line)
307{
308 NS_LOG_FUNCTION(this << line);
309
310 std::string value;
311 m_columns.clear();
312
313 auto start_col = line.begin();
314 auto end_col = line.end();
315
316 while (start_col != line.end())
317 {
318 std::tie(value, end_col) = ParseColumn(start_col, line.end());
319
320 NS_LOG_DEBUG("ParseColumn() returned: " << value);
321
322 m_columns.push_back(std::move(value));
323
324 if (end_col != line.end())
325 {
326 ++end_col;
327 }
328
329 start_col = end_col;
330 }
331 m_blankRow = (m_columns.size() == 1) && (m_columns[0].empty());
332 NS_LOG_LOGIC("blank row: " << m_blankRow);
333}
334
335std::tuple<std::string, std::string::const_iterator>
336CsvReader::ParseColumn(std::string::const_iterator begin, std::string::const_iterator end)
337{
338 NS_LOG_FUNCTION(this << std::string(begin, end));
339
340 enum class State
341 {
342 BEGIN,
343 END_QUOTE,
344 FIND_DELIMITER,
345 QUOTED_STRING,
346 UNQUOTED_STRING,
347 END
348 };
349
350 State state = State::BEGIN;
351 std::string buffer;
352 auto iter = begin;
353
354 while (state != State::END)
355 {
356 if (iter == end)
357 {
358 NS_LOG_DEBUG("Found end iterator, switching to END state");
359
360 state = State::END;
361 continue;
362 }
363
364 auto c = *iter;
365
366 NS_LOG_DEBUG("Next character: '" << c << "'");
367
368 // handle common cases here to avoid duplicating logic
369 if (state != State::QUOTED_STRING)
370 {
371 if (IsDelimiter(c))
372 {
373 NS_LOG_DEBUG("Found field delimiter, switching to END state");
374
375 if (state == State::UNQUOTED_STRING)
376 {
377 NS_LOG_DEBUG("Removing trailing whitespace from unquoted field: '" << buffer
378 << "'");
379 auto len = buffer.size();
380
381 // remove trailing whitespace from the field
382 while (!buffer.empty() &&
383 std::isspace(static_cast<unsigned char>(buffer.back())))
384 {
385 buffer.pop_back();
386 }
387
388 auto finalLen = buffer.size();
389
390 NS_LOG_DEBUG("Removed " << (len - finalLen)
391 << " trailing whitespace characters");
392 }
393
394 state = State::END;
395
396 continue;
397 }
398 else if (c == '#')
399 {
400 NS_LOG_DEBUG("Found start of comment, switching to END state");
401
402 // comments consume the rest of the line, set iter to end
403 // to reflect that fact.
404 iter = end;
405 state = State::END;
406
407 continue;
408 }
409 }
410
411 switch (state)
412 {
413 case State::BEGIN: {
414 if (c == '"')
415 {
416 NS_LOG_DEBUG("Switching state: BEGIN -> QUOTED_STRING");
417
418 state = State::QUOTED_STRING;
419 }
420 else if (!std::isspace(c))
421 {
422 NS_LOG_DEBUG("Switching state: BEGIN -> UNQUOTED_STRING");
423
424 state = State::UNQUOTED_STRING;
425 buffer.push_back(c);
426 }
427 }
428 break;
429 case State::QUOTED_STRING: {
430 if (c == '"')
431 {
432 NS_LOG_DEBUG("Switching state: QUOTED_STRING -> END_QUOTE");
433 state = State::END_QUOTE;
434 }
435 else
436 {
437 buffer.push_back(c);
438 }
439 }
440 break;
441 case State::END_QUOTE: {
442 if (c == '"')
443 {
444 NS_LOG_DEBUG("Switching state: END_QUOTE -> QUOTED_STRING");
445
446 // an escape quote instead of an end quote
447 state = State::QUOTED_STRING;
448 buffer.push_back(c);
449 }
450 else
451 {
452 NS_LOG_DEBUG("Switching state: END_QUOTE -> FIND_DELIMITER");
453 state = State::FIND_DELIMITER;
454 }
455 }
456 break;
457 case State::UNQUOTED_STRING: {
458 buffer.push_back(c);
459 }
460 break;
461 case State::FIND_DELIMITER:
462 break;
463 case State::END:
464 break;
465 }
466
467 ++iter;
468 }
469
470 NS_LOG_DEBUG("Field value: " << buffer);
471
472 return std::make_tuple(buffer, iter);
473}
474
475} // namespace ns3
virtual ~CsvReader()
Destructor.
Definition: csv-reader.cc:92
std::size_t RowNumber() const
The number of lines that have been read.
Definition: csv-reader.cc:105
char Delimiter() const
Returns the delimiter character specified during object construction.
Definition: csv-reader.cc:113
std::istream * m_stream
Pointer to the input stream containing the data.
Definition: csv-reader.h:401
bool IsDelimiter(char c) const
Returns true if the supplied character matches the delimiter.
Definition: csv-reader.cc:298
CsvReader(const std::string &filepath, char delimiter=',')
Constructor.
Definition: csv-reader.cc:74
void ParseLine(const std::string &line)
Scans the string and splits it into individual columns based on the delimiter.
Definition: csv-reader.cc:306
std::size_t ColumnCount() const
Returns the number of columns in the csv data.
Definition: csv-reader.cc:97
std::size_t m_rowsRead
Number of lines processed.
Definition: csv-reader.h:393
bool m_blankRow
Line contains no data (blank line or comment only).
Definition: csv-reader.h:395
bool FetchNextRow()
Reads one line from the input until a new line is encountered.
Definition: csv-reader.cc:121
bool IsBlankRow() const
Check if the current row is blank.
Definition: csv-reader.cc:152
Columns m_columns
Fields extracted from the current line.
Definition: csv-reader.h:394
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:158
char m_delimiter
Character used to separate fields.
Definition: csv-reader.h:392
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:336
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:58
Every class exported by the ns3 library is enclosed in the ns3 namespace.
#define END
End of a line.