/*************************************************************************** * GCsv.cpp - Comma-separated values table class * * ----------------------------------------------------------------------- * * copyright (C) 2010-2015 by Juergen Knoedlseder * * ----------------------------------------------------------------------- * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 3 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * * * ***************************************************************************/ /** * @file GCsv.cpp * @brief Comma-separated values table class implementation * @author Juergen Knoedlseder */ /* __ Includes ___________________________________________________________ */ #ifdef HAVE_CONFIG_H #include #endif #include // std::fopen, std::fgets, std::fclose, etc... #include "GCsv.hpp" #include "GTools.hpp" #include "GException.hpp" /* __ Method name definitions ____________________________________________ */ #define G_ACCESS "GCsv::operator()(int&, int&)" #define G_LOAD "GCsv::load(std::string&, std::string&)" #define G_SAVE "GCsv::save(std::string&, std::string&, bool&)" /* __ Macros _____________________________________________________________ */ /* __ Coding definitions _________________________________________________ */ /* __ Debug definitions __________________________________________________ */ /*========================================================================== = = = Constructors/destructors = = = ==========================================================================*/ /***********************************************************************//** * @brief Void constructor ***************************************************************************/ GCsv::GCsv(void) { // Initialise private members init_members(); // Return return; } /***********************************************************************//** * @brief Table constructor * * @param[in] nrows Number of rows. * @param[in] ncols Number of columns. ***************************************************************************/ GCsv::GCsv(const int& nrows, const int& ncols) { // Initialise private members init_members(); // Allocate empty row std::vector row(ncols, ""); // Allocate table m_data.assign(nrows, row); // Set table dimensions m_cols = ncols; m_rows = nrows; // Return return; } /***********************************************************************//** * @brief File constructor * * @param[in] filename Filename. * @param[in] sep Column separator (default is whitespace). ***************************************************************************/ GCsv::GCsv(const std::string& filename, const std::string& sep) { // Initialise private init_members(); // Load CSV table load(filename, sep); // Return return; } /***********************************************************************//** * @brief Copy constructor * * @param[in] csv Comma-separated values table. ***************************************************************************/ GCsv::GCsv(const GCsv& csv) { // Initialise private init_members(); // Copy members copy_members(csv); // Return return; } /***********************************************************************//** * @brief Destructor ***************************************************************************/ GCsv::~GCsv(void) { // Free members free_members(); // Return return; } /*========================================================================== = = = Operators = = = ==========================================================================*/ /***********************************************************************//** * @brief Assignment operator * * @param[in] csv Comma-separated values table. * @return Comma-separated values table. ***************************************************************************/ GCsv& GCsv::operator=(const GCsv& csv) { // Execute only if object is not identical if (this != &csv) { // Free members free_members(); // Initialise private members for clean destruction init_members(); // Copy members copy_members(csv); } // endif: object was not identical // Return return *this; } /***********************************************************************//** * @brief Table element access operator * * @param[in] row Table row. * @param[in] col Table column. ***************************************************************************/ std::string& GCsv::operator()(const int& row, const int& col) { // Perform range check #if defined(G_RANGE_CHECK) if (row < 0 || row >= m_rows || col < 0 || col >= m_cols) { throw GException::out_of_range(G_ACCESS, row, col, m_rows-1, m_cols-1); } #endif // Return element return m_data[row][col]; } /***********************************************************************//** * @brief Table element access operator (const version) * * @param[in] row Table row. * @param[in] col Table column. ***************************************************************************/ const std::string& GCsv::operator()(const int& row, const int& col) const { // Perform range check #if defined(G_RANGE_CHECK) if (row < 0 || row >= m_rows || col < 0 || col >= m_cols) { throw GException::out_of_range(G_ACCESS, row, col, m_rows, m_cols); } #endif // Return element return m_data[row][col]; } /*========================================================================== = = = Public methods = = = ==========================================================================*/ /***********************************************************************//** * @brief Clear CSV table * * This method properly resets the object to an initial state. ***************************************************************************/ void GCsv::clear(void) { // Free class members free_members(); // Initialise members init_members(); // Return return; } /***********************************************************************//** * @brief Clone CSV table * * @return Pointer to deep copy of CSV table. **************************************************************************/ GCsv* GCsv::clone(void) const { return new GCsv(*this); } /***********************************************************************//** * @brief Get string value * * @param[in] row Table row. * @param[in] col Table column. * * Returns value of specified row and column as string. ***************************************************************************/ std::string GCsv::string(const int& row, const int& col) const { // Return value return (*this)(row, col); } /***********************************************************************//** * @brief Get double precision value * * @param[in] row Table row. * @param[in] col Table column. * * Returns value of specified row and column as double precision. ***************************************************************************/ double GCsv::real(const int& row, const int& col) const { // Convert element into double double value = gammalib::todouble((*this)(row, col)); // Return value return value; } /***********************************************************************//** * @brief Get integer value * * @param[in] row Table row. * @param[in] col Table column. * * Returns value of specified row and column as integer. ***************************************************************************/ int GCsv::integer(const int& row, const int& col) const { // Convert element into int int value = gammalib::toint((*this)(row, col)); // Return value return value; } /***********************************************************************//** * @brief Get string value * * @param[in] row Table row. * @param[in] col Table column. * @param[in] value String value. * * Set value of specified row and column as string. ***************************************************************************/ void GCsv::string(const int& row, const int& col, const std::string& value) { // Set value (*this)(row, col) = value; // Return return; } /***********************************************************************//** * @brief Get double precision value * * @param[in] row Table row. * @param[in] col Table column. * @param[in] value Double precision floating point value. * * Set value of specified row and column as double precision floating point. ***************************************************************************/ void GCsv::real(const int& row, const int& col, const double& value) { // Set value (*this)(row, col) = gammalib::str(value, m_precision); // Return return; } /***********************************************************************//** * @brief Get integer value * * @param[in] row Table row. * @param[in] col Table column. * @param[in] value Integer value. * * Set value of specified row and column as integer. ***************************************************************************/ void GCsv::integer(const int& row, const int& col, const int& value) { // Set value (*this)(row, col) = gammalib::str(value); // Return return; } /***********************************************************************//** * @brief Load CSV table * * @param[in] filename Filename. * @param[in] sep Column separator (default is whitespace). * * @exception GException::file_not_found * CSV table file not found. * @exception GException::csv_bad_columns * Inconsistent columns encountered in CSV table file. * * Load CSV table from ASCII file. Any environment variable present in the * filename will be expanded. **************************************************************************/ void GCsv::load(const std::string& filename, const std::string& sep) { // Clear instance clear(); // Allocate line buffer const int n = 10000; char line[n]; // Expand environment variables std::string fname = gammalib::expand_env(filename); // Open CSV table (read-only) FILE* fptr = std::fopen(fname.c_str(), "r"); if (fptr == NULL) { throw GException::file_not_found(G_LOAD, fname); } // Read lines int iline = 0; while (std::fgets(line, n, fptr) != NULL) { // Increment line counter iline++; // Get line with leading and trailing whitespace removed std::string sline = gammalib::strip_chars(gammalib::strip_whitespace(std::string(line)),"\n"); // Skip line if empty if (sline.length() == 0) { continue; } // Split line in elements std::vector elements = gammalib::split(sline, sep); for (int i = 0; i < elements.size(); ++i) { elements[i] = gammalib::strip_whitespace(elements[i]); } // If this is the first valid line then simply store the elements if (m_data.empty()) { m_data.push_back(elements); m_cols = elements.size(); } // ... otherwise check table consistency and add elements else { // Check table consistency if (m_cols != elements.size()) { throw GException::csv_bad_columns(G_LOAD, fname, iline, m_cols, elements.size()); } // Append elements m_data.push_back(elements); } // Increment number of rows m_rows++; } // endwhile: looped over lines // Close file std::fclose(fptr); // Return return; } /***********************************************************************//** * @brief Save CSV table * * @param[in] filename Filename. * @param[in] sep Column separator (default: whitespace). * @param[in] clobber Overwrite existing file? (default: false). * * @exception GException::invalid_value * Attempt to overwrite existing file. * @exception GException::file_error * Unable to create file. * * Save CSV table into ASCII file. Any environment variable present in the * filename will be expanded. **************************************************************************/ void GCsv::save(const std::string& filename, const std::string& sep, const bool& clobber) const { // Throw exception if file exists but clobber flag is false if (!clobber && gammalib::file_exists(filename)) { std::string msg = "File \""+filename+"\" exists already but the " "clobber flag is set to \"false\". Set the " "clobber flag to true to overwrite the existing " "file or specify another file name."; throw GException::invalid_value(G_SAVE, msg); } // Expand environment variables std::string fname = gammalib::expand_env(filename); // Open CSV table (write-only) FILE* fptr = std::fopen(fname.c_str(), "w"); if (fptr == NULL) { std::string msg = "Unable to create file \""+filename+"\"."; throw GException::file_error(G_SAVE, msg); } // Loop over the rows for (int row = 0; row < m_rows; ++row) { // Write columns for (int col = 0; col < m_cols; ++col) { std::fputs(m_data[row][col].c_str(), fptr); std::fputs(sep.c_str(), fptr); } // Write linebreak std::fputs("\n", fptr); } // endfor: looped over rows // Close file std::fclose(fptr); // Return return; } /***********************************************************************//** * @brief Print column separated values information * * @param[in] chatter Chattiness (defaults to NORMAL). * @return String containing column separated values information. ***************************************************************************/ std::string GCsv::print(const GChatter& chatter) const { // Initialise result string std::string result; // Continue only if chatter is not silent if (chatter != SILENT) { // Append header result.append("=== GCsv ==="); // Append information result.append("\n"+gammalib::parformat("Number of columns")); result.append(gammalib::str(m_cols)); result.append("\n"+gammalib::parformat("Number of rows")); result.append(gammalib::str(m_rows)); result.append("\n"+gammalib::parformat("Floating point precision")); if (m_precision == 0) { result.append("default"); } else { result.append(gammalib::str(m_precision)); } } // endif: chatter was not silent // Return result return result; } /*========================================================================== = = = Private methods = = = ==========================================================================*/ /***********************************************************************//** * @brief Initialise class members ***************************************************************************/ void GCsv::init_members(void) { // Initialise members m_cols = 0; m_rows = 0; m_data.clear(); m_precision = 0; // Return return; } /***********************************************************************//** * @brief Copy class members * * @param[in] csv Comma-separated values table. ***************************************************************************/ void GCsv::copy_members(const GCsv& csv) { // Copy members m_cols = csv.m_cols; m_rows = csv.m_rows; m_data = csv.m_data; m_precision = csv.m_precision; // Return return; } /***********************************************************************//** * @brief Delete class members ***************************************************************************/ void GCsv::free_members(void) { // Return return; }