Skip to content
Snippets Groups Projects
Commit a5f268a7 authored by Dmitri Naumov's avatar Dmitri Naumov Committed by GitHub
Browse files

Merge pull request #1332 from rinkk/CsvWriter

Functionality for writing CSV files
parents 33dd4a42 d1ee6ff2
No related branches found
No related tags found
No related merge requests found
...@@ -14,12 +14,20 @@ ...@@ -14,12 +14,20 @@
#include "CsvInterface.h" #include "CsvInterface.h"
#include <algorithm> #include <algorithm>
#include <iostream>
#include <numeric>
#include <stdexcept> #include <stdexcept>
#include "GeoLib/Point.h" #include "GeoLib/Point.h"
namespace FileIO { namespace FileIO {
CsvInterface::CsvInterface()
: _writeCsvHeader(true)
{
}
int CsvInterface::readPoints(std::string const& fname, char delim, int CsvInterface::readPoints(std::string const& fname, char delim,
std::vector<GeoLib::Point*> &points) std::vector<GeoLib::Point*> &points)
{ {
...@@ -181,4 +189,64 @@ std::size_t CsvInterface::findColumn(std::string const& line, char delim, std::s ...@@ -181,4 +189,64 @@ std::size_t CsvInterface::findColumn(std::string const& line, char delim, std::s
return count; return count;
} }
void CsvInterface::addIndexVectorForWriting(std::size_t s)
{
std::vector<int> idx_vec(s);
std::iota(idx_vec.begin(), idx_vec.end(), 0);
addVectorForWriting("Index", idx_vec);
}
bool CsvInterface::write()
{
if (_data.empty())
{
ERR ("CsvInterface::write() - No data to write.");
return false;
}
std::size_t const n_vecs (_data.size());
std::size_t const vec_size (getVectorSize(0));
if (_writeCsvHeader)
{
_out << _vec_names[0];
for (std::size_t i=1; i<n_vecs; ++i)
_out << "\t" << _vec_names[i];
_out << "\n";
}
for (std::size_t j=0; j<vec_size; ++j)
{
writeValue(0,j);
for (std::size_t i=1; i<n_vecs; ++i)
{
_out << "\t";
writeValue(i,j);
}
_out << "\n";
}
return true;
}
std::size_t CsvInterface::getVectorSize(std::size_t idx) const
{
if (_data[idx].type() == typeid(std::vector<std::string>))
return boost::any_cast<std::vector<std::string>>(_data[idx]).size();
else if (_data[idx].type() == typeid(std::vector<double>))
return boost::any_cast<std::vector<double>>(_data[idx]).size();
else if (_data[idx].type() == typeid(std::vector<int>))
return boost::any_cast<std::vector<int>>(_data[idx]).size();
return 0;
}
void CsvInterface::writeValue(std::size_t vec_idx, std::size_t in_vec_idx)
{
if (_data[vec_idx].type() == typeid(std::vector<std::string>))
_out << boost::any_cast<std::vector<std::string>>(_data[vec_idx])[in_vec_idx];
else if (_data[vec_idx].type() == typeid(std::vector<double>))
_out << boost::any_cast<std::vector<double>>(_data[vec_idx])[in_vec_idx];
else if (_data[vec_idx].type() == typeid(std::vector<int>))
_out << boost::any_cast<std::vector<int>>(_data[vec_idx])[in_vec_idx];
}
} // end namespace FileIO } // end namespace FileIO
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
#define CSVINTERFACE_H_ #define CSVINTERFACE_H_
#include <logog/include/logog.hpp> #include <logog/include/logog.hpp>
#include <boost/any.hpp>
#include <array> #include <array>
#include <fstream> #include <fstream>
...@@ -22,10 +23,12 @@ ...@@ -22,10 +23,12 @@
#include <limits> #include <limits>
#include <list> #include <list>
#include <string> #include <string>
#include <typeinfo>
#include <vector> #include <vector>
#include "BaseLib/StringTools.h" #include "BaseLib/StringTools.h"
#include "BaseLib/IO/Writer.h"
namespace GeoLib { namespace GeoLib {
class Point; class Point;
...@@ -36,9 +39,50 @@ namespace FileIO { ...@@ -36,9 +39,50 @@ namespace FileIO {
/** /**
* Interface for reading CSV file formats. * Interface for reading CSV file formats.
*/ */
class CsvInterface { class CsvInterface : public BaseLib::IO::Writer
{
public: public:
/// Contructor (only needed for writing files)
CsvInterface();
/// Returns the number of vectors currently staged for writing.
std::size_t getNArrays() const { return _vec_names.size(); }
/// Adds an index vector of size s to the CSV file
void addIndexVectorForWriting(std::size_t s);
/// Stores if the CSV file to be written should include a header or not.
void setCsvHeader(bool write_header) { _writeCsvHeader = write_header; }
/// Adds a data vector to the CSV file. All data vectors have to have the same size.
/// Vectors will be written in the same sequence they have been added to the interface.
template<typename T>
bool addVectorForWriting(std::string const& vec_name, std::vector<T> const& vec)
{
static_assert(std::is_same<T, std::string>::value
|| std::is_same<T, double>::value
|| std::is_same<T, int>::value,
"CsvInterface can only write vectors of strings, doubles or ints.");
if (!_data.empty())
{
std::size_t const vec_size (getVectorSize(0));
if (vec_size != vec.size())
{
ERR ("Vector size does not match existing data (should be %d).", vec_size);
return false;
}
}
_vec_names.push_back(vec_name);
_data.push_back(vec);
return true;
}
/// Writes the CSV file.
bool write();
/** /**
* Reads 3D points from a CSV file. It is assumed that the file has a header * Reads 3D points from a CSV file. It is assumed that the file has a header
* specifying a name for each of the columns. The first three columns will be * specifying a name for each of the columns. The first three columns will be
...@@ -178,6 +222,20 @@ private: ...@@ -178,6 +222,20 @@ private:
/// Returns the number of the column with column_name (or std::numeric_limits::max() if no such column has been found). /// Returns the number of the column with column_name (or std::numeric_limits::max() if no such column has been found).
static std::size_t findColumn(std::string const& line, char delim, std::string const& column_name); static std::size_t findColumn(std::string const& line, char delim, std::string const& column_name);
/// Returns the size of the vector with the given index
std::size_t getVectorSize(std::size_t idx) const;
/**
* Writes a value from a vector to the file.
* \param vec_idx Index of the vector
* \param in_vec_idx Entry in the selected vector
*/
void writeValue(std::size_t vec_idx, std::size_t in_vec_idx);
bool _writeCsvHeader;
std::vector<std::string> _vec_names;
std::vector< boost::any > _data;
}; };
} // FileIO } // FileIO
......
/**
* \copyright
* Copyright (c) 2012-2016, OpenGeoSys Community (http://www.opengeosys.org)
* Distributed under a Modified BSD License.
* See accompanying file LICENSE.txt or
* http://www.opengeosys.org/project/license
*
*
*/
#include <cstdio>
#include "gtest/gtest.h"
#include <string>
#include <vector>
#include "BaseLib/BuildInfo.h"
#include "Applications/FileIO/CsvInterface.h"
TEST(CsvWriter, WriteReadTest)
{
std::string test_file(BaseLib::BuildInfo::tests_tmp_path + "TestData.csv");
std::vector<std::string> str_vec {"Red", "Orange", "Yellow", "Green", "Blue", "Indigo", "Violet" };
std::vector<int> int_vec { 1, 2, 4, 8, 16, 32, 64 };
std::vector<double> dbl_vec;
std::srand ( static_cast<unsigned>(std::time(nullptr)) );
for (std::size_t i=0; i<int_vec.size(); ++i)
dbl_vec.push_back(static_cast<double>(std::rand()) / RAND_MAX);
FileIO::CsvInterface csv;
bool added;
std::vector<std::string> vec_names { "String Vector", "Int Vector", "Double Vector"};
added = csv.addVectorForWriting(vec_names[0], str_vec);
ASSERT_EQ(true, added);
added = csv.addVectorForWriting(vec_names[1], int_vec);
ASSERT_EQ(true, added);
added = csv.addVectorForWriting(vec_names[2], dbl_vec);
ASSERT_EQ(true, added);
int_vec.push_back(128);
added = csv.addVectorForWriting(vec_names[1], int_vec);
ASSERT_EQ(false, added);
ASSERT_EQ(3, csv.getNArrays());
csv.addIndexVectorForWriting(str_vec.size());
ASSERT_EQ(4, csv.getNArrays());
int result = csv.writeToFile(test_file);
ASSERT_EQ(1, result);
std::vector<std::string> str_result;
result = FileIO::CsvInterface::readColumn<std::string>(test_file, '\t', str_result, vec_names[0]);
ASSERT_EQ(0, result);
ASSERT_EQ(str_vec.size(), str_result.size());
std::vector<int> idx_result;
result = FileIO::CsvInterface::readColumn<int>(test_file, '\t', idx_result, "Index");
ASSERT_EQ(0, result);
ASSERT_EQ(dbl_vec.size(), idx_result.size());
std::vector<int> int_result;
result = FileIO::CsvInterface::readColumn<int>(test_file, '\t', int_result, vec_names[1]);
ASSERT_EQ(0, result);
// testing for vector length -1 because it had increased previously when testing size requirements
ASSERT_EQ(int_vec.size()-1, int_result.size());
std::vector<double> dbl_result;
result = FileIO::CsvInterface::readColumn<double>(test_file, '\t', dbl_result, vec_names[2]);
ASSERT_EQ(0, result);
ASSERT_EQ(dbl_vec.size(), dbl_result.size());
for (std::size_t i=0; i<str_vec.size(); ++i)
{
ASSERT_EQ(str_vec[i], str_result[i]);
ASSERT_EQ(int_vec[i], int_result[i]);
ASSERT_NEAR(dbl_vec[i], dbl_result[i], 10 * std::numeric_limits<double>::epsilon());
ASSERT_EQ(static_cast<int>(i), idx_result[i]);
}
std::remove(test_file.c_str());
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment