GammaLib 2.0.0
Loading...
Searching...
No Matches
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
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 ***************************************************************************/
75GCsv::GCsv(const int& nrows, const int& ncols)
76{
77 // Initialise private 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 ***************************************************************************/
101GCsv::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 ***************************************************************************/
119GCsv::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 ***************************************************************************/
184std::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 ***************************************************************************/
207const 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 ***************************************************************************/
235void 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 **************************************************************************/
253GCsv* 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 ***************************************************************************/
273void 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 ***************************************************************************/
309std::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 ***************************************************************************/
324double 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 ***************************************************************************/
342int 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 ***************************************************************************/
361void 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 ***************************************************************************/
380void 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 ***************************************************************************/
399void 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 **************************************************************************/
423void 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.";
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 **************************************************************************/
519void 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.";
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 ***************************************************************************/
569std::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 ***************************************************************************/
627void 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;
634
635 // Return
636 return;
637}
638
639
640/***********************************************************************//**
641 * @brief Delete class members
642 ***************************************************************************/
644{
645 // Return
646 return;
647}
#define G_SAVE
#define G_APPEND
#define G_ACCESS
Comma-separated values table class definition.
Exception handler interface definition.
Filename class interface definition.
#define G_LOAD
Gammalib tools definition.
GChatter
Definition GTypemaps.hpp:33
@ SILENT
Definition GTypemaps.hpp:34
Comma-separated values table class.
Definition GCsv.hpp:57
void copy_members(const GCsv &csv)
Copy class members.
Definition GCsv.cpp:627
std::string string(const int &row, const int &col) const
Get string value.
Definition GCsv.cpp:309
const int & nrows(void) const
Return number of rows.
Definition GCsv.hpp:149
std::vector< std::vector< std::string > > m_data
CSV table data.
Definition GCsv.hpp:102
virtual ~GCsv(void)
Destructor.
Definition GCsv.cpp:135
void load(const GFilename &filename, const std::string &sep=" ")
Load CSV table.
Definition GCsv.cpp:423
void init_members(void)
Initialise class members.
Definition GCsv.cpp:609
int integer(const int &row, const int &col) const
Get integer value.
Definition GCsv.cpp:342
std::string print(const GChatter &chatter=NORMAL) const
Print column separated values information.
Definition GCsv.cpp:569
void clear(void)
Clear CSV table.
Definition GCsv.cpp:235
int m_rows
Number of rows.
Definition GCsv.hpp:101
double real(const int &row, const int &col) const
Get double precision value.
Definition GCsv.cpp:324
void free_members(void)
Delete class members.
Definition GCsv.cpp:643
int m_precision
Precision for floats.
Definition GCsv.hpp:103
void save(const GFilename &filename, const std::string &sep=" ", const bool &clobber=false) const
Save CSV table.
Definition GCsv.cpp:519
std::string & operator()(const int &row, const int &col)
Table element access operator.
Definition GCsv.cpp:184
int m_cols
Number of columns.
Definition GCsv.hpp:100
void append(const std::vector< std::string > &list)
Append list of strings.
Definition GCsv.cpp:273
GCsv * clone(void) const
Clone CSV table.
Definition GCsv.cpp:253
const int & ncols(void) const
Return number of columns.
Definition GCsv.hpp:137
GCsv & operator=(const GCsv &csv)
Assignment operator.
Definition GCsv.cpp:157
GCsv(void)
Void constructor.
Definition GCsv.cpp:59
Filename class.
Definition GFilename.hpp:62
std::string url(void) const
Return Uniform Resource Locator (URL)
bool exists(void) const
Checks whether file exists.
std::string parformat(const std::string &s, const int &indent=0)
Convert string in parameter format.
Definition GTools.cpp:1143
std::string str(const unsigned short int &value)
Convert unsigned short integer value into string.
Definition GTools.cpp:489
double todouble(const std::string &arg)
Convert string into double precision value.
Definition GTools.cpp:926
int toint(const std::string &arg)
Convert string into integer value.
Definition GTools.cpp:821
std::string strip_whitespace(const std::string &arg)
Strip leading and trailing whitespace from string.
Definition GTools.cpp:80
std::string expand_env(const std::string &arg)
Expand environment variables in string.
Definition GTools.cpp:214
std::vector< std::string > split(const std::string &s, const std::string &sep)
Split string.
Definition GTools.cpp:983
std::string strip_chars(const std::string &arg, const std::string &chars)
Strip leading and trailing character from string.
Definition GTools.cpp:94