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