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 
41 NS_LOG_COMPONENT_DEFINE ("CsvReader");
42 
43 namespace {
44 
57 template<typename T>
58 bool 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 
71 namespace ns3 {
72 
73 CsvReader::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 
82 CsvReader::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 
94 std::size_t
96 {
97  NS_LOG_FUNCTION (this);
98 
99  return m_columns.size ();
100 }
101 
102 std::size_t
104 {
105  NS_LOG_FUNCTION (this);
106 
107  return m_rowsRead;
108 }
109 
110 char
112 {
113  NS_LOG_FUNCTION (this);
114 
115  return m_delimiter;
116 }
117 
118 bool
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 
149 bool
151 {
152  return m_blankRow;
153 }
154 
155 bool
156 CsvReader::GetValueAs (std::string input, double& value) const
157 {
158  NS_LOG_FUNCTION (this << input);
159 
160  return GenericTransform (std::move (input), value);
161 }
162 
163 bool
164 CsvReader::GetValueAs (std::string input, float& value) const
165 {
166  NS_LOG_FUNCTION (this << input);
167 
168  return GenericTransform (std::move (input), value);
169 }
170 
171 bool
172 CsvReader::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 
198 bool
199 CsvReader::GetValueAs (std::string input, short& value) const
200 {
201  NS_LOG_FUNCTION (this << input);
202 
203  return GenericTransform (std::move (input), value);
204 }
205 
206 bool
207 CsvReader::GetValueAs (std::string input, int& value) const
208 {
209  NS_LOG_FUNCTION (this << input);
210 
211  return GenericTransform (std::move (input), value);
212 }
213 
214 bool
215 CsvReader::GetValueAs (std::string input, long& value) const
216 {
217  NS_LOG_FUNCTION (this << input);
218 
219  return GenericTransform (std::move (input), value);
220 }
221 
222 bool
223 CsvReader::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 
230 bool
231 CsvReader::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 
240 bool
241 CsvReader::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 
269 bool
270 CsvReader::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 
277 bool
278 CsvReader::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 
285 bool
286 CsvReader::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 
293 bool
294 CsvReader::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 
301 bool
303 {
304  NS_LOG_FUNCTION (this << c);
305 
306  return c == m_delimiter;
307 }
308 
309 void
310 CsvReader::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 
339 std::tuple<std::string, std::string::const_iterator>
340 CsvReader::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.