GammaLib  2.0.0
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
GCsv.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  * GCsv.cpp - Comma-separated values table class *
3  * ----------------------------------------------------------------------- *
4  * copyright (C) 2010-2021 by Juergen Knoedlseder *
5  * ----------------------------------------------------------------------- *
6  * *
7  * This program is free software: you can redistribute it and/or modify *
8  * it under the terms of the GNU General Public License as published by *
9  * the Free Software Foundation, either version 3 of the License, or *
10  * (at your option) any later version. *
11  * *
12  * This program is distributed in the hope that it will be useful, *
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
15  * GNU General Public License for more details. *
16  * *
17  * You should have received a copy of the GNU General Public License *
18  * along with this program. If not, see <http://www.gnu.org/licenses/>. *
19  * *
20  ***************************************************************************/
21 /**
22  * @file GCsv.cpp
23  * @brief Comma-separated values table class implementation
24  * @author Juergen Knoedlseder
25  */
26 
27 /* __ Includes ___________________________________________________________ */
28 #ifdef HAVE_CONFIG_H
29 #include <config.h>
30 #endif
31 #include <cstdio> // std::fopen, std::fgets, std::fclose, etc...
32 #include "GCsv.hpp"
33 #include "GTools.hpp"
34 #include "GFilename.hpp"
35 #include "GException.hpp"
36 
37 /* __ Method name definitions ____________________________________________ */
38 #define G_ACCESS "GCsv::operator()(int&, int&)"
39 #define G_APPEND "GCsv::append(std::vector<std::string>&)"
40 #define G_LOAD "GCsv::load(GFilename&, std::string&)"
41 #define G_SAVE "GCsv::save(GFilename&, std::string&, bool&)"
42 
43 /* __ Macros _____________________________________________________________ */
44 
45 /* __ Coding definitions _________________________________________________ */
46 
47 /* __ Debug definitions __________________________________________________ */
48 
49 
50 /*==========================================================================
51  = =
52  = Constructors/destructors =
53  = =
54  ==========================================================================*/
55 
56 /***********************************************************************//**
57  * @brief Void constructor
58  ***************************************************************************/
60 {
61  // Initialise private members
62  init_members();
63 
64  // Return
65  return;
66 }
67 
68 
69 /***********************************************************************//**
70  * @brief Table constructor
71  *
72  * @param[in] nrows Number of rows.
73  * @param[in] ncols Number of columns.
74  ***************************************************************************/
75 GCsv::GCsv(const int& nrows, const int& ncols)
76 {
77  // Initialise private members
78  init_members();
79 
80  // Allocate empty row
81  std::vector<std::string> row(ncols, "");
82 
83  // Allocate table
84  m_data.assign(nrows, row);
85 
86  // Set table dimensions
87  m_cols = ncols;
88  m_rows = nrows;
89 
90  // Return
91  return;
92 }
93 
94 
95 /***********************************************************************//**
96  * @brief File constructor
97  *
98  * @param[in] filename Filename.
99  * @param[in] sep Column separator (default is whitespace).
100  ***************************************************************************/
101 GCsv::GCsv(const GFilename& filename, const std::string& sep)
102 {
103  // Initialise private
104  init_members();
105 
106  // Load CSV table
107  load(filename, sep);
108 
109  // Return
110  return;
111 }
112 
113 
114 /***********************************************************************//**
115  * @brief Copy constructor
116  *
117  * @param[in] csv Comma-separated values table.
118  ***************************************************************************/
119 GCsv::GCsv(const GCsv& csv)
120 {
121  // Initialise private
122  init_members();
123 
124  // Copy members
125  copy_members(csv);
126 
127  // Return
128  return;
129 }
130 
131 
132 /***********************************************************************//**
133  * @brief Destructor
134  ***************************************************************************/
136 {
137  // Free members
138  free_members();
139 
140  // Return
141  return;
142 }
143 
144 
145 /*==========================================================================
146  = =
147  = Operators =
148  = =
149  ==========================================================================*/
150 
151 /***********************************************************************//**
152  * @brief Assignment operator
153  *
154  * @param[in] csv Comma-separated values table.
155  * @return Comma-separated values table.
156  ***************************************************************************/
158 {
159  // Execute only if object is not identical
160  if (this != &csv) {
161 
162  // Free members
163  free_members();
164 
165  // Initialise private members for clean destruction
166  init_members();
167 
168  // Copy members
169  copy_members(csv);
170 
171  } // endif: object was not identical
172 
173  // Return
174  return *this;
175 }
176 
177 
178 /***********************************************************************//**
179  * @brief Table element access operator
180  *
181  * @param[in] row Table row [0,...,m_rows[.
182  * @param[in] col Table column [0,...,m_cols[.
183  ***************************************************************************/
184 std::string& GCsv::operator()(const int& row, const int& col)
185 {
186  // Perform range check
187  #if defined(G_RANGE_CHECK)
188  if (row < 0 || row >= m_rows) {
189  throw GException::out_of_range(G_ACCESS, "Table row", row, m_rows);
190  }
191  if (col < 0 || col >= m_cols) {
192  throw GException::out_of_range(G_ACCESS, "Table column", col, m_cols);
193  }
194  #endif
195 
196  // Return element
197  return m_data[row][col];
198 }
199 
200 
201 /***********************************************************************//**
202  * @brief Table element access operator (const version)
203  *
204  * @param[in] row Table row [0,...,m_rows[.
205  * @param[in] col Table column [0,...,m_cols[.
206  ***************************************************************************/
207 const std::string& GCsv::operator()(const int& row, const int& col) const
208 {
209  // Perform range check
210  #if defined(G_RANGE_CHECK)
211  if (row < 0 || row >= m_rows) {
212  throw GException::out_of_range(G_ACCESS, "Table row", row, m_rows);
213  }
214  if (col < 0 || col >= m_cols) {
215  throw GException::out_of_range(G_ACCESS, "Table column", col, m_cols);
216  }
217  #endif
218 
219  // Return element
220  return m_data[row][col];
221 }
222 
223 
224 /*==========================================================================
225  = =
226  = Public methods =
227  = =
228  ==========================================================================*/
229 
230 /***********************************************************************//**
231  * @brief Clear CSV table
232  *
233  * This method properly resets the object to an initial state.
234  ***************************************************************************/
235 void GCsv::clear(void)
236 {
237  // Free class members
238  free_members();
239 
240  // Initialise members
241  init_members();
242 
243  // Return
244  return;
245 }
246 
247 
248 /***********************************************************************//**
249  * @brief Clone CSV table
250  *
251  * @return Pointer to deep copy of CSV table.
252  **************************************************************************/
253 GCsv* GCsv::clone(void) const
254 {
255  return new GCsv(*this);
256 }
257 
258 
259 /***********************************************************************//**
260  * @brief Append list of strings
261  *
262  * @param[in] list List of strings.
263  *
264  * @exception GException::invalid_argument
265  * Invalid number of elements in list.
266  *
267  * Appends a list of strings to the CSV table. If the table is empty, the
268  * number of elements in the list will determine the number of columns of
269  * the table. If a table exists already, the method will throw an exception
270  * if the number of elements in the list does not correspond to the number
271  * of table columns.
272  ***************************************************************************/
273 void GCsv::append(const std::vector<std::string>& list)
274 {
275  // If we have already rows then check columns size ...
276  if (m_rows > 0) {
277  if (m_cols != list.size()) {
278  std::string msg = "Invalid attempt to append "+
279  gammalib::str(list.size())+" elements to a CSV "
280  "table with "+gammalib::str(m_cols)+" columns.";
282  }
283  }
284 
285  // ... otherwise store the number of columns
286  else {
287  m_cols = list.size();
288  }
289 
290  // Append
291  m_data.push_back(list);
292 
293  // Increment number of rows
294  m_rows++;
295 
296  // Return
297  return;
298 }
299 
300 
301 /***********************************************************************//**
302  * @brief Get string value
303  *
304  * @param[in] row Table row.
305  * @param[in] col Table column.
306  *
307  * Returns value of specified row and column as string.
308  ***************************************************************************/
309 std::string GCsv::string(const int& row, const int& col) const
310 {
311  // Return value
312  return (*this)(row, col);
313 }
314 
315 
316 /***********************************************************************//**
317  * @brief Get double precision value
318  *
319  * @param[in] row Table row.
320  * @param[in] col Table column.
321  *
322  * Returns value of specified row and column as double precision.
323  ***************************************************************************/
324 double GCsv::real(const int& row, const int& col) const
325 {
326  // Convert element into double
327  double value = gammalib::todouble((*this)(row, col));
328 
329  // Return value
330  return value;
331 }
332 
333 
334 /***********************************************************************//**
335  * @brief Get integer value
336  *
337  * @param[in] row Table row.
338  * @param[in] col Table column.
339  *
340  * Returns value of specified row and column as integer.
341  ***************************************************************************/
342 int GCsv::integer(const int& row, const int& col) const
343 {
344  // Convert element into int
345  int value = gammalib::toint((*this)(row, col));
346 
347  // Return value
348  return value;
349 }
350 
351 
352 /***********************************************************************//**
353  * @brief Get string value
354  *
355  * @param[in] row Table row.
356  * @param[in] col Table column.
357  * @param[in] value String value.
358  *
359  * Set value of specified row and column as string.
360  ***************************************************************************/
361 void GCsv::string(const int& row, const int& col, const std::string& value)
362 {
363  // Set value
364  (*this)(row, col) = value;
365 
366  // Return
367  return;
368 }
369 
370 
371 /***********************************************************************//**
372  * @brief Get double precision value
373  *
374  * @param[in] row Table row.
375  * @param[in] col Table column.
376  * @param[in] value Double precision floating point value.
377  *
378  * Set value of specified row and column as double precision floating point.
379  ***************************************************************************/
380 void GCsv::real(const int& row, const int& col, const double& value)
381 {
382  // Set value
383  (*this)(row, col) = gammalib::str(value, m_precision);
384 
385  // Return
386  return;
387 }
388 
389 
390 /***********************************************************************//**
391  * @brief Get integer value
392  *
393  * @param[in] row Table row.
394  * @param[in] col Table column.
395  * @param[in] value Integer value.
396  *
397  * Set value of specified row and column as integer.
398  ***************************************************************************/
399 void GCsv::integer(const int& row, const int& col, const int& value)
400 {
401  // Set value
402  (*this)(row, col) = gammalib::str(value);
403 
404  // Return
405  return;
406 }
407 
408 
409 /***********************************************************************//**
410  * @brief Load CSV table
411  *
412  * @param[in] filename Filename.
413  * @param[in] sep Column separator (default is whitespace).
414  *
415  * @exception GException::file_error
416  * CSV table file not found.
417  * @exception GException::csv_bad_columns
418  * Inconsistent columns encountered in CSV table file.
419  *
420  * Load CSV table from ASCII file. Any environment variable present in the
421  * filename will be expanded.
422  **************************************************************************/
423 void GCsv::load(const GFilename& filename, const std::string& sep)
424 {
425  // Clear instance
426  clear();
427 
428  // Allocate line buffer
429  const int n = 10000;
430  char line[n];
431 
432  // Expand environment variables
433  std::string fname = gammalib::expand_env(filename.url());
434 
435  // Open CSV table (read-only)
436  FILE* fptr = std::fopen(fname.c_str(), "r");
437  if (fptr == NULL) {
438  std::string msg = "Unable to open file \""+fname+"\" for read access. "
439  "Please specify a readable file.";
440  throw GException::file_error(G_LOAD, msg);
441  }
442 
443  // Read lines
444  int iline = 0;
445  while (std::fgets(line, n, fptr) != NULL) {
446 
447  // Increment line counter
448  iline++;
449 
450  // Get line with leading and trailing whitespace removed
451  std::string sline =
452  gammalib::strip_chars(gammalib::strip_whitespace(std::string(line)),"\n");
453 
454  // Skip empty lines
455  if (sline.length() == 0) {
456  continue;
457  }
458 
459  // Skip lines starting with a hash as those are comment lines
460  if (sline[0] == '#') {
461  continue;
462  }
463 
464  // Split line in elements
465  std::vector<std::string> elements = gammalib::split(sline, sep);
466  for (int i = 0; i < elements.size(); ++i) {
467  elements[i] = gammalib::strip_whitespace(elements[i]);
468  }
469 
470  // If this is the first valid line then simply store the elements
471  if (m_data.empty()) {
472  m_data.push_back(elements);
473  m_cols = elements.size();
474  }
475 
476  // ... otherwise check table consistency and add elements
477  else {
478  // Check table consistency
479  if (m_cols != elements.size()) {
480  std::string msg = "Number of "+gammalib::str(elements.size())+
481  " columns in row "+gammalib::str(iline)+
482  " is not consistent with number of "+
483  gammalib::str(m_cols)+" in first row. Please "
484  "specify a correctly formatted CSV file.";
485  throw GException::invalid_value(G_LOAD, msg);
486  }
487 
488  // Append elements
489  m_data.push_back(elements);
490  }
491 
492  // Increment number of rows
493  m_rows++;
494 
495  } // endwhile: looped over lines
496 
497  // Close file
498  std::fclose(fptr);
499 
500  // Return
501  return;
502 }
503 
504 
505 /***********************************************************************//**
506  * @brief Save CSV table
507  *
508  * @param[in] filename Filename.
509  * @param[in] sep Column separator.
510  * @param[in] clobber Overwrite existing file?
511  *
512  * @exception GException::invalid_value
513  * Attempt to overwrite existing file.
514  * @exception GException::file_error
515  * Unable to create file.
516  *
517  * Save CSV table into ASCII file.
518  **************************************************************************/
519 void GCsv::save(const GFilename& filename,
520  const std::string& sep,
521  const bool& clobber) const
522 {
523  // Throw exception if file exists but clobber flag is false
524  if (!clobber && filename.exists()) {
525  std::string msg = "File \""+filename+"\" exists already but "
526  "the clobber flag is set to \"false\". Set the "
527  "clobber flag to true to overwrite the existing "
528  "file or specify another file name.";
529  throw GException::invalid_value(G_SAVE, msg);
530  }
531 
532  // Open CSV table (write-only)
533  FILE* fptr = std::fopen(filename.url().c_str(), "w");
534  if (fptr == NULL) {
535  std::string msg = "Unable to create file \""+filename+"\".";
536  throw GException::file_error(G_SAVE, msg);
537  }
538 
539  // Loop over the rows
540  for (int row = 0; row < m_rows; ++row) {
541 
542  // Write columns
543  for (int col = 0; col < m_cols; ++col) {
544  std::fputs(m_data[row][col].c_str(), fptr);
545  if (col < m_cols-1) {
546  std::fputs(sep.c_str(), fptr);
547  }
548  }
549 
550  // Write linebreak
551  std::fputs("\n", fptr);
552 
553  } // endfor: looped over rows
554 
555  // Close file
556  std::fclose(fptr);
557 
558  // Return
559  return;
560 }
561 
562 
563 /***********************************************************************//**
564  * @brief Print column separated values information
565  *
566  * @param[in] chatter Chattiness (defaults to NORMAL).
567  * @return String containing column separated values information.
568  ***************************************************************************/
569 std::string GCsv::print(const GChatter& chatter) const
570 {
571  // Initialise result string
572  std::string result;
573 
574  // Continue only if chatter is not silent
575  if (chatter != SILENT) {
576 
577  // Append header
578  result.append("=== GCsv ===");
579 
580  // Append information
581  result.append("\n"+gammalib::parformat("Number of columns"));
582  result.append(gammalib::str(m_cols));
583  result.append("\n"+gammalib::parformat("Number of rows"));
584  result.append(gammalib::str(m_rows));
585  result.append("\n"+gammalib::parformat("Floating point precision"));
586  if (m_precision == 0) {
587  result.append("default");
588  }
589  else {
590  result.append(gammalib::str(m_precision));
591  }
592 
593  } // endif: chatter was not silent
594 
595  // Return result
596  return result;
597 }
598 
599 
600 /*==========================================================================
601  = =
602  = Private methods =
603  = =
604  ==========================================================================*/
605 
606 /***********************************************************************//**
607  * @brief Initialise class members
608  ***************************************************************************/
610 {
611  // Initialise members
612  m_cols = 0;
613  m_rows = 0;
614  m_data.clear();
615  m_precision = 0;
616 
617  // Return
618  return;
619 }
620 
621 
622 /***********************************************************************//**
623  * @brief Copy class members
624  *
625  * @param[in] csv Comma-separated values table.
626  ***************************************************************************/
627 void GCsv::copy_members(const GCsv& csv)
628 {
629  // Copy members
630  m_cols = csv.m_cols;
631  m_rows = csv.m_rows;
632  m_data = csv.m_data;
633  m_precision = csv.m_precision;
634 
635  // Return
636  return;
637 }
638 
639 
640 /***********************************************************************//**
641  * @brief Delete class members
642  ***************************************************************************/
644 {
645  // Return
646  return;
647 }
void append(const std::vector< std::string > &list)
Append list of strings.
Definition: GCsv.cpp:273
void free_members(void)
Delete class members.
Definition: GCsv.cpp:643
void save(const GFilename &filename, const std::string &sep=" ", const bool &clobber=false) const
Save CSV table.
Definition: GCsv.cpp:519
#define G_LOAD
Definition: GCsv.cpp:40
int m_precision
Precision for floats.
Definition: GCsv.hpp:103
GCsv(void)
Void constructor.
Definition: GCsv.cpp:59
GCsv & operator=(const GCsv &csv)
Assignment operator.
Definition: GCsv.cpp:157
void clear(void)
Clear CSV table.
Definition: GCsv.cpp:235
#define G_ACCESS
Definition: GCsv.cpp:38
Comma-separated values table class.
Definition: GCsv.hpp:57
std::string strip_chars(const std::string &arg, const std::string &chars)
Strip leading and trailing character from string.
Definition: GTools.cpp:94
std::vector< std::string > split(const std::string &s, const std::string &sep)
Split string.
Definition: GTools.cpp:983
virtual ~GCsv(void)
Destructor.
Definition: GCsv.cpp:135
Gammalib tools definition.
std::string strip_whitespace(const std::string &arg)
Strip leading and trailing whitespace from string.
Definition: GTools.cpp:80
int m_cols
Number of columns.
Definition: GCsv.hpp:100
std::string string(const int &row, const int &col) const
Get string value.
Definition: GCsv.cpp:309
const int & ncols(void) const
Return number of columns.
Definition: GCsv.hpp:137
void init_members(void)
Initialise class members.
Definition: GCsv.cpp:609
Filename class.
Definition: GFilename.hpp:62
std::string & operator()(const int &row, const int &col)
Table element access operator.
Definition: GCsv.cpp:184
GCsv * clone(void) const
Clone CSV table.
Definition: GCsv.cpp:253
bool exists(void) const
Checks whether file exists.
Definition: GFilename.cpp:223
#define G_APPEND
Definition: GCsv.cpp:39
int integer(const int &row, const int &col) const
Get integer value.
Definition: GCsv.cpp:342
GChatter
Definition: GTypemaps.hpp:33
const int & nrows(void) const
Return number of rows.
Definition: GCsv.hpp:149
double real(const int &row, const int &col) const
Get double precision value.
Definition: GCsv.cpp:324
int m_rows
Number of rows.
Definition: GCsv.hpp:101
std::string url(void) const
Return Uniform Resource Locator (URL)
Definition: GFilename.hpp:189
std::vector< std::vector< std::string > > m_data
CSV table data.
Definition: GCsv.hpp:102
Exception handler interface definition.
void load(const GFilename &filename, const std::string &sep=" ")
Load CSV table.
Definition: GCsv.cpp:423
std::string expand_env(const std::string &arg)
Expand environment variables in string.
Definition: GTools.cpp:214
int toint(const std::string &arg)
Convert string into integer value.
Definition: GTools.cpp:821
std::string parformat(const std::string &s, const int &indent=0)
Convert string in parameter format.
Definition: GTools.cpp:1143
std::string print(const GChatter &chatter=NORMAL) const
Print column separated values information.
Definition: GCsv.cpp:569
#define G_SAVE
Definition: GCsv.cpp:41
void copy_members(const GCsv &csv)
Copy class members.
Definition: GCsv.cpp:627
Filename class interface definition.
Comma-separated values table class definition.
double todouble(const std::string &arg)
Convert string into double precision value.
Definition: GTools.cpp:926
std::string str(const unsigned short int &value)
Convert unsigned short integer value into string.
Definition: GTools.cpp:489