// Copyright 2015. All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are met: // // * Redistributions of source code must retain the above copyright notice, // this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above copyright notice, // this list of conditions and the following disclaimer in the documentation // and/or other materials provided with the distribution. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE // POSSIBILITY OF SUCH DAMAGE. // // Author: ryan.latture@gmail.com (Ryan Latture) #ifndef CSV_PARSER_H #define CSV_PARSER_H #include #include #include #include #include #include #include #include #include #include namespace fea { /** * Takes a line from the input stream and appends it to the record. * * @param ins `std::istream`. The file where data is read from. * @param record `std::vector`. Vector where the data is placed. */ template std::istream &operator>>(std::istream &ins, std::vector &record) { // make sure that the returned record contains only the stuff we read now record.clear(); // read the entire line into a string (a CSV record is terminated by a newline) std::string line; std::getline(ins, line); std::string empty; std::string separator_characters(", \t"); boost::escaped_list_separator separators(empty, separator_characters, empty); boost::tokenizer> tk(line, separators); for (boost::tokenizer>::iterator i(tk.begin()); i != tk.end(); ++i) { T f = std::strtod(i->c_str(), 0); record.push_back(f); } return ins; } /** * Parses the file into a 2D vector. Each sub-vector is a line from the input file. * * @param ins `std::istream`. The file where data is read from. Each line will become a vector in `data` * @param data `std::vector< std::vector< T > >`. 2D Vector where the data is placed. */ template inline std::istream &operator>>(std::istream &ins, std::vector > &data) { // make sure that the returned data only contains the CSV data we read here data.clear(); // For every record we can read from the file, append it to our resulting data std::vector record; while (ins >> record) { data.push_back(record); } // Again, return the argument stream as required for this kind of input stream overload. return ins; } /** * @brief Reads data from a csv file into an `std::vector` and writes the contents of an `std::vector` to a file. * \note The parser assumes the data is comma delimited when reading data from a file. */ class CSVParser { public: /** * @brief parses the contents of `file_name` into `data`. * * @param[in] file_name `std::string`. The file specified is opened and the contained information is parsed into `data`. * @param data `std::vector`. Variable updated in place that will fold the data of the specified file. */ template void parseToVector(std::string filename, std::vector &data) { std::ifstream infile; infile.open(filename); if (infile.is_open()) { try { infile >> data; } catch (std::exception &e) { throw std::runtime_error( (boost::format("Error when parsing csv file %s.\nDetails from tokenizer:\n\t%s") % filename % e.what()).str() ); } infile.close(); } else { throw std::runtime_error( (boost::format("Error opening file %s") % filename).str() ); } } /** * Writes the 2D vector `data` to the file specified by `filename`. * * @param[in] filename `std::string`. The file to write data to. * @param[in] data `std::vector< std::vector< T > >`. Data to write to file. * @param[in] precision `unsigned int`. The number of decimal places to use when writing the data to file. * @param[in] demlimiter `std::string`. The delimiter to use between data entries. */ template void write(const std::string &filename, const std::vector > &data, unsigned int precision, const std::string &delimiter) { std::ofstream output_file; output_file.open(filename); size_t num_cols; if (!output_file.is_open()) { throw std::runtime_error( (boost::format("Error opening file %s") % filename).str() ); } else { for (size_t i = 0; i < data.size(); ++i) { num_cols = data[i].size(); for (size_t j = 0; j < num_cols; ++j) { output_file << std::fixed << std::setprecision(precision) << data[i][j]; if (j < num_cols - 1) { output_file << delimiter; } } output_file << "\n"; } output_file.close(); } } }; } // namespace fea #endif //CSV_PARSER_H