threed-beam-fea/include/csv_parser.h

168 lines
6.5 KiB
C++

// 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 <boost/format.hpp>
#include <boost/tokenizer.hpp>
#include <cstdlib>
#include <exception>
#include <fstream>
#include <iomanip>
#include <iostream>
#include <sstream>
#include <string>
#include <vector>
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<T>`. Vector where the data is placed.
*/
template<typename T>
std::istream &operator>>(std::istream &ins, std::vector<T> &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<char> separators(empty, separator_characters, empty);
boost::tokenizer<boost::escaped_list_separator<char>> tk(line, separators);
for (boost::tokenizer<boost::escaped_list_separator<char>>::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<typename T>
inline std::istream &operator>>(std::istream &ins, std::vector<std::vector<T> > &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<T> 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<T>`. Variable updated in place that will fold the data of the specified file.
*/
template<typename T>
void parseToVector(std::string filename, std::vector<T> &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<typename T>
void write(const std::string &filename,
const std::vector<std::vector<T> > &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