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