GammaLib  1.7.0.dev
 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-2018 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.
182  * @param[in] col Table column.
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 || col < 0 || col >= m_cols) {
189  throw GException::out_of_range(G_ACCESS, row, col, m_rows-1, m_cols-1);
190  }
191  #endif
192 
193  // Return element
194  return m_data[row][col];
195 }
196 
197 
198 /***********************************************************************//**
199  * @brief Table element access operator (const version)
200  *
201  * @param[in] row Table row.
202  * @param[in] col Table column.
203  ***************************************************************************/
204 const std::string& GCsv::operator()(const int& row, const int& col) const
205 {
206  // Perform range check
207  #if defined(G_RANGE_CHECK)
208  if (row < 0 || row >= m_rows || col < 0 || col >= m_cols) {
209  throw GException::out_of_range(G_ACCESS, row, col, m_rows, m_cols);
210  }
211  #endif
212 
213  // Return element
214  return m_data[row][col];
215 }
216 
217 
218 /*==========================================================================
219  = =
220  = Public methods =
221  = =
222  ==========================================================================*/
223 
224 /***********************************************************************//**
225  * @brief Clear CSV table
226  *
227  * This method properly resets the object to an initial state.
228  ***************************************************************************/
229 void GCsv::clear(void)
230 {
231  // Free class members
232  free_members();
233 
234  // Initialise members
235  init_members();
236 
237  // Return
238  return;
239 }
240 
241 
242 /***********************************************************************//**
243  * @brief Clone CSV table
244  *
245  * @return Pointer to deep copy of CSV table.
246  **************************************************************************/
247 GCsv* GCsv::clone(void) const
248 {
249  return new GCsv(*this);
250 }
251 
252 
253 /***********************************************************************//**
254  * @brief Append list of strings
255  *
256  * @param[in] list List of strings.
257  *
258  * @exception GException::invalid_argument
259  * Invalid number of elements in list.
260  *
261  * Appends a list of strings to the CSV table. If the table is empty, the
262  * number of elements in the list will determine the number of columns of
263  * the table. If a table exists already, the method will throw an exception
264  * if the number of elements in the list does not correspond to the number
265  * of table columns.
266  ***************************************************************************/
267 void GCsv::append(const std::vector<std::string>& list)
268 {
269  // If we have already rows then check columns size ...
270  if (m_rows > 0) {
271  if (m_cols != list.size()) {
272  std::string msg = "Invalid attempt to append "+
273  gammalib::str(list.size())+" elements to a CSV "
274  "table with "+gammalib::str(m_cols)+" columns.";
276  }
277  }
278 
279  // ... otherwise store the number of columns
280  else {
281  m_cols = list.size();
282  }
283 
284  // Append
285  m_data.push_back(list);
286 
287  // Increment number of rows
288  m_rows++;
289 
290  // Return
291  return;
292 }
293 
294 
295 /***********************************************************************//**
296  * @brief Get string value
297  *
298  * @param[in] row Table row.
299  * @param[in] col Table column.
300  *
301  * Returns value of specified row and column as string.
302  ***************************************************************************/
303 std::string GCsv::string(const int& row, const int& col) const
304 {
305  // Return value
306  return (*this)(row, col);
307 }
308 
309 
310 /***********************************************************************//**
311  * @brief Get double precision value
312  *
313  * @param[in] row Table row.
314  * @param[in] col Table column.
315  *
316  * Returns value of specified row and column as double precision.
317  ***************************************************************************/
318 double GCsv::real(const int& row, const int& col) const
319 {
320  // Convert element into double
321  double value = gammalib::todouble((*this)(row, col));
322 
323  // Return value
324  return value;
325 }
326 
327 
328 /***********************************************************************//**
329  * @brief Get integer value
330  *
331  * @param[in] row Table row.
332  * @param[in] col Table column.
333  *
334  * Returns value of specified row and column as integer.
335  ***************************************************************************/
336 int GCsv::integer(const int& row, const int& col) const
337 {
338  // Convert element into int
339  int value = gammalib::toint((*this)(row, col));
340 
341  // Return value
342  return value;
343 }
344 
345 
346 /***********************************************************************//**
347  * @brief Get string value
348  *
349  * @param[in] row Table row.
350  * @param[in] col Table column.
351  * @param[in] value String value.
352  *
353  * Set value of specified row and column as string.
354  ***************************************************************************/
355 void GCsv::string(const int& row, const int& col, const std::string& value)
356 {
357  // Set value
358  (*this)(row, col) = value;
359 
360  // Return
361  return;
362 }
363 
364 
365 /***********************************************************************//**
366  * @brief Get double precision value
367  *
368  * @param[in] row Table row.
369  * @param[in] col Table column.
370  * @param[in] value Double precision floating point value.
371  *
372  * Set value of specified row and column as double precision floating point.
373  ***************************************************************************/
374 void GCsv::real(const int& row, const int& col, const double& value)
375 {
376  // Set value
377  (*this)(row, col) = gammalib::str(value, m_precision);
378 
379  // Return
380  return;
381 }
382 
383 
384 /***********************************************************************//**
385  * @brief Get integer value
386  *
387  * @param[in] row Table row.
388  * @param[in] col Table column.
389  * @param[in] value Integer value.
390  *
391  * Set value of specified row and column as integer.
392  ***************************************************************************/
393 void GCsv::integer(const int& row, const int& col, const int& value)
394 {
395  // Set value
396  (*this)(row, col) = gammalib::str(value);
397 
398  // Return
399  return;
400 }
401 
402 
403 /***********************************************************************//**
404  * @brief Load CSV table
405  *
406  * @param[in] filename Filename.
407  * @param[in] sep Column separator (default is whitespace).
408  *
409  * @exception GException::file_not_found
410  * CSV table file not found.
411  * @exception GException::csv_bad_columns
412  * Inconsistent columns encountered in CSV table file.
413  *
414  * Load CSV table from ASCII file. Any environment variable present in the
415  * filename will be expanded.
416  **************************************************************************/
417 void GCsv::load(const GFilename& filename, const std::string& sep)
418 {
419  // Clear instance
420  clear();
421 
422  // Allocate line buffer
423  const int n = 10000;
424  char line[n];
425 
426  // Expand environment variables
427  std::string fname = gammalib::expand_env(filename.url());
428 
429  // Open CSV table (read-only)
430  FILE* fptr = std::fopen(fname.c_str(), "r");
431  if (fptr == NULL) {
432  throw GException::file_not_found(G_LOAD, fname);
433  }
434 
435  // Read lines
436  int iline = 0;
437  while (std::fgets(line, n, fptr) != NULL) {
438 
439  // Increment line counter
440  iline++;
441 
442  // Get line with leading and trailing whitespace removed
443  std::string sline =
444  gammalib::strip_chars(gammalib::strip_whitespace(std::string(line)),"\n");
445 
446  // Skip empty lines
447  if (sline.length() == 0) {
448  continue;
449  }
450 
451  // Skip lines starting with a hash as those are comment lines
452  if (sline[0] == '#') {
453  continue;
454  }
455 
456  // Split line in elements
457  std::vector<std::string> elements = gammalib::split(sline, sep);
458  for (int i = 0; i < elements.size(); ++i) {
459  elements[i] = gammalib::strip_whitespace(elements[i]);
460  }
461 
462  // If this is the first valid line then simply store the elements
463  if (m_data.empty()) {
464  m_data.push_back(elements);
465  m_cols = elements.size();
466  }
467 
468  // ... otherwise check table consistency and add elements
469  else {
470  // Check table consistency
471  if (m_cols != elements.size()) {
472  throw GException::csv_bad_columns(G_LOAD, fname,
473  iline, m_cols, elements.size());
474  }
475 
476  // Append elements
477  m_data.push_back(elements);
478  }
479 
480  // Increment number of rows
481  m_rows++;
482 
483  } // endwhile: looped over lines
484 
485  // Close file
486  std::fclose(fptr);
487 
488  // Return
489  return;
490 }
491 
492 
493 /***********************************************************************//**
494  * @brief Save CSV table
495  *
496  * @param[in] filename Filename.
497  * @param[in] sep Column separator (default: whitespace).
498  * @param[in] clobber Overwrite existing file? (default: false).
499  *
500  * @exception GException::invalid_value
501  * Attempt to overwrite existing file.
502  * @exception GException::file_error
503  * Unable to create file.
504  *
505  * Save CSV table into ASCII file.
506  **************************************************************************/
507 void GCsv::save(const GFilename& filename,
508  const std::string& sep,
509  const bool& clobber) const
510 {
511  // Throw exception if file exists but clobber flag is false
512  if (!clobber && filename.exists()) {
513  std::string msg = "File \""+filename+"\" exists already but "
514  "the clobber flag is set to \"false\". Set the "
515  "clobber flag to true to overwrite the existing "
516  "file or specify another file name.";
517  throw GException::invalid_value(G_SAVE, msg);
518  }
519 
520  // Open CSV table (write-only)
521  FILE* fptr = std::fopen(filename.url().c_str(), "w");
522  if (fptr == NULL) {
523  std::string msg = "Unable to create file \""+filename+"\".";
524  throw GException::file_error(G_SAVE, msg);
525  }
526 
527  // Loop over the rows
528  for (int row = 0; row < m_rows; ++row) {
529 
530  // Write columns
531  for (int col = 0; col < m_cols; ++col) {
532  std::fputs(m_data[row][col].c_str(), fptr);
533  if (col < m_cols-1) {
534  std::fputs(sep.c_str(), fptr);
535  }
536  }
537 
538  // Write linebreak
539  std::fputs("\n", fptr);
540 
541  } // endfor: looped over rows
542 
543  // Close file
544  std::fclose(fptr);
545 
546  // Return
547  return;
548 }
549 
550 
551 /***********************************************************************//**
552  * @brief Print column separated values information
553  *
554  * @param[in] chatter Chattiness (defaults to NORMAL).
555  * @return String containing column separated values information.
556  ***************************************************************************/
557 std::string GCsv::print(const GChatter& chatter) const
558 {
559  // Initialise result string
560  std::string result;
561 
562  // Continue only if chatter is not silent
563  if (chatter != SILENT) {
564 
565  // Append header
566  result.append("=== GCsv ===");
567 
568  // Append information
569  result.append("\n"+gammalib::parformat("Number of columns"));
570  result.append(gammalib::str(m_cols));
571  result.append("\n"+gammalib::parformat("Number of rows"));
572  result.append(gammalib::str(m_rows));
573  result.append("\n"+gammalib::parformat("Floating point precision"));
574  if (m_precision == 0) {
575  result.append("default");
576  }
577  else {
578  result.append(gammalib::str(m_precision));
579  }
580 
581  } // endif: chatter was not silent
582 
583  // Return result
584  return result;
585 }
586 
587 
588 /*==========================================================================
589  = =
590  = Private methods =
591  = =
592  ==========================================================================*/
593 
594 /***********************************************************************//**
595  * @brief Initialise class members
596  ***************************************************************************/
598 {
599  // Initialise members
600  m_cols = 0;
601  m_rows = 0;
602  m_data.clear();
603  m_precision = 0;
604 
605  // Return
606  return;
607 }
608 
609 
610 /***********************************************************************//**
611  * @brief Copy class members
612  *
613  * @param[in] csv Comma-separated values table.
614  ***************************************************************************/
615 void GCsv::copy_members(const GCsv& csv)
616 {
617  // Copy members
618  m_cols = csv.m_cols;
619  m_rows = csv.m_rows;
620  m_data = csv.m_data;
621  m_precision = csv.m_precision;
622 
623  // Return
624  return;
625 }
626 
627 
628 /***********************************************************************//**
629  * @brief Delete class members
630  ***************************************************************************/
632 {
633  // Return
634  return;
635 }
void append(const std::vector< std::string > &list)
Append list of strings.
Definition: GCsv.cpp:267
void free_members(void)
Delete class members.
Definition: GCsv.cpp:631
void save(const GFilename &filename, const std::string &sep=" ", const bool &clobber=false) const
Save CSV table.
Definition: GCsv.cpp:507
#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:229
#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:87
std::vector< std::string > split(const std::string &s, const std::string &sep)
Split string.
Definition: GTools.cpp:862
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:73
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:303
const int & ncols(void) const
Return number of columns.
Definition: GCsv.hpp:137
void init_members(void)
Initialise class members.
Definition: GCsv.cpp:597
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:247
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:336
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:318
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:417
std::string expand_env(const std::string &arg)
Expand environment variables in string.
Definition: GTools.cpp:207
int toint(const std::string &arg)
Convert string into integer value.
Definition: GTools.cpp:700
std::string parformat(const std::string &s, const int &indent=0)
Convert string in parameter format.
Definition: GTools.cpp:1022
std::string print(const GChatter &chatter=NORMAL) const
Print column separated values information.
Definition: GCsv.cpp:557
#define G_SAVE
Definition: GCsv.cpp:41
void copy_members(const GCsv &csv)
Copy class members.
Definition: GCsv.cpp:615
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:805
std::string str(const unsigned short int &value)
Convert unsigned short integer value into string.
Definition: GTools.cpp:413