A Discrete-Event Network Simulator
API
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 
40 NS_LOG_COMPONENT_DEFINE("CsvReader");
41 
42 namespace
43 {
44 
57 template <typename T>
58 bool
59 GenericTransform(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 
72 namespace ns3
73 {
74 
75 CsvReader::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 
84 CsvReader::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 
97 std::size_t
99 {
100  NS_LOG_FUNCTION(this);
101 
102  return m_columns.size();
103 }
104 
105 std::size_t
107 {
108  NS_LOG_FUNCTION(this);
109 
110  return m_rowsRead;
111 }
112 
113 char
115 {
116  NS_LOG_FUNCTION(this);
117 
118  return m_delimiter;
119 }
120 
121 bool
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 
152 bool
154 {
155  return m_blankRow;
156 }
157 
158 bool
159 CsvReader::GetValueAs(std::string input, double& value) const
160 {
161  NS_LOG_FUNCTION(this << input);
162 
163  return GenericTransform(std::move(input), value);
164 }
165 
166 bool
167 CsvReader::GetValueAs(std::string input, float& value) const
168 {
169  NS_LOG_FUNCTION(this << input);
170 
171  return GenericTransform(std::move(input), value);
172 }
173 
174 bool
175 CsvReader::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 
199 bool
200 CsvReader::GetValueAs(std::string input, short& value) const
201 {
202  NS_LOG_FUNCTION(this << input);
203 
204  return GenericTransform(std::move(input), value);
205 }
206 
207 bool
208 CsvReader::GetValueAs(std::string input, int& value) const
209 {
210  NS_LOG_FUNCTION(this << input);
211 
212  return GenericTransform(std::move(input), value);
213 }
214 
215 bool
216 CsvReader::GetValueAs(std::string input, long& value) const
217 {
218  NS_LOG_FUNCTION(this << input);
219 
220  return GenericTransform(std::move(input), value);
221 }
222 
223 bool
224 CsvReader::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 
231 bool
232 CsvReader::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 
241 bool
242 CsvReader::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 
266 bool
267 CsvReader::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 
274 bool
275 CsvReader::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 
282 bool
283 CsvReader::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 
290 bool
291 CsvReader::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 
298 bool
300 {
301  NS_LOG_FUNCTION(this << c);
302 
303  return c == m_delimiter;
304 }
305 
306 void
307 CsvReader::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 
336 std::tuple<std::string, std::string::const_iterator>
337 CsvReader::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
#define min(a, b)
Definition: 80211b.c:41
#define max(a, b)
Definition: 80211b.c:42
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.