diff --git a/examples/CSVFiles/main.cpp b/examples/CSVFiles/main.cpp index 5fc6075..9b56dfd 100644 --- a/examples/CSVFiles/main.cpp +++ b/examples/CSVFiles/main.cpp @@ -1,360 +1,360 @@ //===-- examples/CSVFiles/main.cpp ------------------*- C++ -*-===// // // The RoSA Framework // // Distributed under the terms and conditions of the Boost Software License 1.0. // See accompanying file LICENSE. // // If you did not receive a copy of the license file, see // http://www.boost.org/LICENSE_1_0.txt. // //===----------------------------------------------------------------------===// /// /// \file examples/basic-system/basic-system.cpp /// /// \author Edwin Willegger (edwin.willegger@tuwien.ac.at) /// /// \date 2019 /// /// \brief A simple example on the basic \c rosa::csv, \c rosa::iterator and /// \c rosa::writer classes. Focus is on the tuple impementations. /// //===----------------------------------------------------------------------===// #include #include #include #include #include #include #include #include #include #include #include #include #include #include //includes for an complete example to read and write //with sensors and agents. #include "rosa/deluxe/DeluxeContext.hpp" #include "rosa/config/version.h" //includes to test the basic functionality //to read and write tuples. #include "rosa/support/csv/CSVReader.hpp" #include "rosa/support/csv/CSVWriter.hpp" #include "rosa/support/iterator/split_tuple_iterator.hpp" #include "rosa/support/writer/split_tuple_writer.hpp" /// the name of the example const std::string ExampleName = "csvfiles"; /// How many cycles of simulation to perform. const size_t NumberOfSimulationCycles = 10; /// Paths for the CSV files for simulation. /// input csv files const std::string csvPath = "../examples/CSVFiles/"; const std::string csvFileWithHeader = csvPath + "HR-New.csv"; const std::string csvFileNoHeader = csvPath + "HR.csv"; const std::string csvFileHeaderSemi = csvPath + "HR-New-Semicolon.csv"; /// output csv files const std::string csvFileWriteHea = csvPath + "csvwriter_noheader.csv"; const std::string csvFileWriteNoHeaSplit = csvPath + "csvSplitwriter_noheader.csv"; using namespace rosa; /// /// This function tests the basic CSVIterator capablities, and shows you /// how you could work with this class. /// void testtupleCSVReader(void){ //different streams to get the csv data out of the files //file contains header and valid data entries, delimter = ',' std::ifstream file_header_data(csvFileWithHeader); //file contains header and valid data entries, delimter = ',' std::ifstream file_header_data_2(csvFileWithHeader); //file contains header and valid data entries, delimter = ',' std::ifstream file_header_data_3(csvFileWithHeader); //file contains header and valid data entries, delimter = ',' std::ifstream file_header_data_4(csvFileWithHeader); //file contains header and valid data entries, delimter = ',' std::ifstream file_header_data_5(csvFileWithHeader); //file contains header and valid data entries, delimter = ',' std::ifstream file_header_data_6(csvFileWithHeader); //file contains no header an valid data entries, delimter = ',' std::ifstream file2(csvFileNoHeader); //file contains header and valid data entries, delimter = ';' std::ifstream file3(csvFileHeaderSemi); csv::CSVIterator it(file_header_data); - it.setDelimeter(','); + it.setDelimiter(','); (void)++it; (void)++it; //if you iterate over the end of file, the last values //of the file will remain in the data structure but no //error occurs. (void)++it; (void)++it; //------------------------------------------------------------------- // a possiblity to get the data out of the iterator std::tuple value = *it; // // Show the value of one iterator // LOG_INFO( "Values are: "); LOG_INFO(std::get<0>(value) ); LOG_INFO(std::get<1>(value) ); //-------------------------------------------------------------------- //testing differnet parameters to the constructor //uncomment to see that it is not possible to iterate over an vector in the tuple. //rosa::csv::CSVIterator> it2(file, 1); //try to skip a valid number of lines after the header csv::CSVIterator it2_0(file_header_data_2, 1); //try to skip a valid number of lines after the header, but you assume that the file has no header //uncomment this line to crash the programm //csv::CSVIterator it2_1(file_header_data_3, 0, csv::HeaderInformation::HasNoHeader); //try to skip a valid number of lines after the header, but you assume that the file has no header //uncomment this line to crash the program //csv::CSVIterator it2_2(file_header_data_4, 1, csv::HeaderInformation::HasNoHeader); //try to skip a valid number of lines of a file without header csv::CSVIterator it2_3(file2, 1, csv::HeaderInformation::HasNoHeader); - //try to skip a valid number of lines after the header, but with different delimeter + //try to skip a valid number of lines after the header, but with different delimiter csv::CSVIterator it2_4(file3, 2, csv::HeaderInformation::HasHeader, ';'); // if you skip more lines than valid, you generate an infinte loop //csv::CSVIterator it3(file_header_data_5, 500); //if you don't need data from all columns just select the number of columns you //need. You get the data back from the first column (index 0) to the fourth column //all values from the fifth column are ignored. csv::CSVIterator it4(file_header_data_6); } /// /// This function tests the basic CSVTupleWriter capablities, and shows you /// how you could work with this class. /// void testtupleCSVWriter(void){ // // Create output writer with an file // std::ofstream file_header_out(csvFileWriteHea); csv::CSVTupleWriter wri(file_header_out); // // Create test tuples // std::tuple values(5, 8.3f, "hallo"); std::tuple values2(3, 8.3f, "end"); // // Create test header lines for the test tuples // std::array header{ {"zero column", "first column", "second column"}}; std::array headerWrong{ {"zero column", "first column", "second column", "third column"}}; std::array headerWrongShort{ {"zero column", "first column"}}; //if you uncomment this line than it would be possible for you to write the header into the stream //in the next line. //wri.write(values); wri.writeHeader(header); wri.write(values); wri.write(values); // it is not possible to write an additional header into the stream. wri.writeHeader(header); wri.write(values); wri << values; wri << values2; //uncomment this line to see, that you can't write a header with the too many elements. //wri.writeHeader(headerWrong); //uncomment this line to see, that you can't write a header with the too few elements. //wri.writeHeader(headerWrongShort); } /// /// This function tests the basic splitTupleIterator capablities, and shows you /// how you could work with this class, this class is used if you want to split /// a CSVIterator in separate parts. /// void testsplitTupleIterator(void) { // // Create deluxe context // std::unique_ptr C = deluxe::DeluxeContext::create(ExampleName); // // Create deluxe sensors. // LOG_INFO("Creating sensors."); // All sensors are created without defining a normal generator function, but // with the default value of the second argument. That, however, requires the // data type to be explicitly defined. This is good for simulation only. // Three different sensors were created, this is just a random number taken. AgentHandle Elem0Sensor = C->createSensor("Element1 Sensor"); AgentHandle Elem1Sensor = C->createSensor("Element2 Sensor"); AgentHandle Elem2Sensor = C->createSensor("Element3 Sensor"); // // Initialize deluxe context for simulation. // C->initializeSimulation(); // Type aliases for iterators using Iterator = rosa::csv::CSVIterator; using IteratorValue = std::tuple; static_assert (std::is_same::value, "Iterator must provide tuples" ); // // Open CSV file and register the columns to the corresponding sensors. // std::ifstream TestCSV(csvFileWithHeader); // // Test data looks like: // Element1, Element2, Element3, Element4, Element5 -- is the header line // 3, 5, 8, 9.5, 17 -- first line of values // 100, -8, 30, 18.8, 29 -- other line of values were also in the file // 5, 20, -100, -200.1, -30 -- if you have less number of values than simulation rounds all values // -- beyond your last value will be zero. //get element iterator ranges auto [Elem0Range, Elem1Range, Elem2Range] = iterator::splitTupleIterator(Iterator(TestCSV), Iterator()); //dissect a range into begin and end iterators by structred bindings auto[Elem0Begin, Elem0End] = Elem0Range; //deissect a range with functions auto Elem1Begin = iterator::begin(Elem1Range); auto Elem1End = iterator::end(Elem1Range); C->registerSensorValues(Elem0Sensor, std::move(Elem0Begin), Elem0End); C->registerSensorValues(Elem1Sensor, std::move(Elem1Begin), Elem1End); C->registerSensorValues(Elem2Sensor, std::move(iterator::begin(Elem2Range)), iterator::end(Elem2Range)); // // Simulate. // C->simulate(NumberOfSimulationCycles); } /// /// This function tests the basic splitTupleWriter capablities, and shows you /// how you could work with this class, this class is used if you want to split /// a CSVWriter in separate parts. /// void testsplitTupleWriter(void){ // // Create output writer with an file // std::ofstream file_header_out(csvFileWriteNoHeaSplit); csv::CSVTupleWriter wri(file_header_out); // if you omit, the type definition in the template, than auto generated types were used, // and they may not fit to the used CSVTupleWriter. wri << std::make_tuple(1000, 50.6f, "tuple_created"); auto [T0Writer, T1Writer, T2Writer] = writer::splitTupleWriter(std::move(wri), writer::IncompleteTuplePolicy::Ignore); //writing elements in sequential order into the writer classes, but you can write the values into the writers in //a random order. T0Writer << (500); T1Writer << (3.0); T2Writer << "splitted writter"; T2Writer << "splitting is cool"; T0Writer << (-30); T1Writer << (-0.004f); // you can also write more often values into a writer and later into the other writers // all data will be processed correctly into the right order. T0Writer << (1); T0Writer << (2); T1Writer << (-0.4f); T0Writer << (3); T2Writer << "again"; T0Writer << (4); T1Writer << (-0.1f); T1Writer << (-0.2f); T2Writer << "and"; T1Writer << (-0.3f); T2Writer << "splitting"; T2Writer << "once again"; // again writing data of one tuple entry to the different writers in a random fashion. T1Writer << (-0.004f); T2Writer << "splitting is cool"; T0Writer << (-30); } int main(void){ LOG_INFO_STREAM << library_string() << " -- " << terminal::Color::Red << ExampleName << " example" << terminal::Color::Default << '\n'; // // Testing CSVWriter. // LOG_INFO("Testing CSVWriter CSVTupleItrator implementation: "); testtupleCSVWriter(); // // Testing CSVReader. // LOG_INFO("Testing CSVReader CSVTupleIterator implementation: "); testtupleCSVReader(); // // Testing SplitTupleIterator. // LOG_INFO("Testing SplitTupleIterator: "); testsplitTupleIterator(); // // Testing SplitTupleWriter. // LOG_INFO("Testing SplitTupleWriter: "); testsplitTupleWriter(); // // info that user knows programm has finished. // LOG_INFO( "All tests finished."); return 0; } diff --git a/include/rosa/support/csv/CSVReader.hpp b/include/rosa/support/csv/CSVReader.hpp index 18bd220..98970ed 100755 --- a/include/rosa/support/csv/CSVReader.hpp +++ b/include/rosa/support/csv/CSVReader.hpp @@ -1,494 +1,494 @@ //===-- rosa/support/csv/CSVReader.hpp --------------------------*- C++ -*-===// // // The RoSA Framework // // Distributed under the terms and conditions of the Boost Software License 1.0. // See accompanying file LICENSE. // // If you did not receive a copy of the license file, see // http://www.boost.org/LICENSE_1_0.txt. // //===----------------------------------------------------------------------===// /// /// \file rosa/support/csv/CSVReader.hpp /// /// \authors David Juhasz (david.juhasz@tuwien.ac.at), Edwin Willegger (edwin.willegger@tuwien.ac.at) /// /// \date 2017-2019 /// /// \brief Facitilities to read CSV files. /// /// \note The implementation is based on the solution at /// https://stackoverflow.com/a/1120224 /// //===----------------------------------------------------------------------===// #ifndef ROSA_SUPPORT_CSV_CSVREADER_HPP #define ROSA_SUPPORT_CSV_CSVREADER_HPP #include "rosa/support/debug.hpp" #include "rosa/support/sequence.hpp" #include #include #include #include #include #include namespace rosa { namespace csv { /// Indicating it the CSV file contains any header or not enum class HeaderInformation { HasHeader, HasNoHeader }; /// Anonymous namespace providing implementation details for /// \c rosa::csv::CSVIterator, consider it private. namespace { /// Provides facility for parsing one value from a string. /// /// \tparam T type of value to parse /// \tparam IsSignedInt if \p T is a signed integral type, always use default /// \tparam IsUnsignedInt if \p T is an unsigned integral type, always use /// default /// \tparam IsFloat if \p T is a floating-point type, always use default /// \tparam IsString if \p T is \c std::string, always use default /// /// \note Specializations of this struct are provided for arithmentic types /// and \c std::string. template ::value && std::is_signed::value), bool IsUnsignedInt = (std::is_integral::value && std::is_unsigned::value), bool IsFloat = std::is_floating_point::value, bool IsString = std::is_same::value> struct ValueParser { /// /// /// \param Cell the \c std::string to parse /// /// \return the parsed value /// /// \note The function silently fails if cannot parse \p Cell for type \p T. static T parse(const std::string &Cell) noexcept; }; template struct ValueParser { STATIC_ASSERT((std::is_integral::value && std::is_signed::value), "wrong type"); // Sanity check. static T parse(const std::string &Cell) noexcept { return static_cast(std::stoll(Cell)); } }; template struct ValueParser { STATIC_ASSERT((std::is_integral::value && std::is_unsigned::value), "wrong type"); // Sanity check. static T parse(const std::string &Cell) noexcept { return static_cast(std::stoull(Cell)); } }; template struct ValueParser { STATIC_ASSERT((std::is_floating_point::value), "wrong type"); // Sanity check. static T parse(const std::string &Cell) noexcept { return static_cast(std::stold(Cell)); } }; template struct ValueParser { STATIC_ASSERT((std::is_same::value), "wrong type"); // Sanity check. static T parse(const std::string &Cell) noexcept { return Cell; } }; /// Parses and stores entries from a row of CSV data. /// /// \tparam Ts types of values to parse and store, i.e. entries in the row /// /// \note The implementation relies on \c rosa::csv::CSVRowParser, which is /// implemented only for `arithmetic` types -- signed and unsigned integral /// and floating-point types -- and for \c std::string. Those are the valid /// values for \p Ts. template class CSVRow { private: /// Parses a given row of CSV data into \c CSVRow::Data. /// /// \ CSVRow::Data is filled with values parsed from \p LineStream. Entries /// in the line are to be separated by commas, the character `,`. /// /// \note Parsed values are silently converted to types \p Ts. /// /// \note Parsing silently fails if values do not match \p Ts. /// /// \tparam S0 indices to access tuple elements. /// /// \param [in,out] LineStream the line to parse /// /// \note The last argument is used only to get \p S0, the actual value of /// the parameter is ignored. template - void parseRow(std::stringstream &LineStream, char Delimeter, Seq) { + void parseRow(std::stringstream &LineStream, char Delimiter, Seq) { STATIC_ASSERT(sizeof...(Ts) == sizeof...(S0), "Not matching template arguments."); std::string Cell; // Get fields and parse the values into the proper element of the tuple // one by one in a fold expression. - ((std::getline(LineStream, Cell, Delimeter), + ((std::getline(LineStream, Cell, Delimiter), std::get(Data) = ValueParser::parse(Cell)), ...); } public: /// Constructor with all possible parameters /// /// The function creates an instance of an CSVRow object and sets the attributes of the /// object to the values of the parameters. /// /// \param SkipRows the number of data rows to skip, not taking header into account. /// \param HeaderInfo is the first line of the file a header row or not. - /// \param Delimeter to seperate between the data entries within one row. + /// \param Delimiter to seperate between the data entries within one row. CSVRow(const size_t SkipRows = 0, const HeaderInformation HeaderInfo = HeaderInformation::HasHeader, - const char Delimeter = ',') : - SkipRows(SkipRows), HeaderInfo(HeaderInfo), Delimeter(Delimeter), + const char Delimiter = ',') : + SkipRows(SkipRows), HeaderInfo(HeaderInfo), Delimiter(Delimiter), RowNumber(0), IsHeaderRead(false) { } /// Parses and stores one row of CSV data. /// /// The function reads one line from \p Str and parses it into /// \c rosa::csv::CSVRow::Data using \c rosa::csv::CSVRowParser. /// /// \param [in,out] Str input stream of a CSV file void readNextRow(std::istream &Str) noexcept { std::string Line; std::getline(Str, Line); if(Line.size() > 0){ std::stringstream LineStream(Line); - parseRow(LineStream, Delimeter, seq_t()); + parseRow(LineStream, Delimiter, seq_t()); RowNumber = RowNumber + 1; } } /// Read header row and stores it as \p std::string. /// /// The function reads the first line of the csv file and stores the entries /// in a vector. /// /// \param [in,out] Str input stream of a CSV file void readHeader(std::istream &Str) noexcept { std::string Line; std::getline(Str, Line); std::stringstream LineStream(Line); std::string Value; - while( getline(LineStream, Value, Delimeter) ){ + while( getline(LineStream, Value, Delimiter) ){ Header.push_back(Value); } IsHeaderRead = true; } /// The number of rows to skip once. /// /// This function returns the number of data rows to skip /// at the beginning of the file. /// /// \return The number of rows to skip at the beginning of a csv file. inline size_t SkipNumRows() const noexcept { return this->SkipRows; } /// The current row number within the csv file. /// /// This function returns the current row number. The header /// row is not counted as a row. /// /// \returns the current row number within the csv file. inline size_t CurRow() const noexcept { return this->RowNumber; } /// Indiciates if the header was already read. /// /// This function returns true, if the header of a csv file which contains /// a header file is already read. /// The user has to pass in the attribute HeaderInfo the information if the /// file has in the first row the header row or not. /// /// \return if the header of a file is already read. inline bool IsHeaderReadDone() const noexcept{ return this->IsHeaderRead; } /// Indicates if the file contains a header row in the first row. /// /// This function returns if the file contains a header row. /// The information if the file contains a header row or not, has to be passed by the user. /// The standard value is HeaderInformation::HasHeader /// /// \return if the csv file contains a header row in the first line of the file. inline HeaderInformation HasFileHeader() const noexcept { return this->HeaderInfo; } /// Set the number of rows to skip. /// /// This function sets the number of rows to skip at the beginning of /// the reading of the file. /// /// \param SkipRowsBeginning the number of rows you want to skip at the beginning of the file. inline void SetSkipRows(const size_t SkipRowsBeginning) noexcept { this->SkipRows = SkipRowsBeginning; } /// Is the first row a header row or not. /// /// This function sets the information, if the first row of the csv file /// is a header line or not. /// /// \param HeaderInf if the first row is a header row or not. inline void SetHeaderInfo(const HeaderInformation HeaderInf) noexcept { this->HeaderInfo = HeaderInf; } /// Set the seperator between data entries. /// /// This funcction sets the separator between the data entries of the csv file. /// /// \param separator the character that separates the data values. - inline void SetDelimeter(char separator) { - this->Delimeter = separator; + inline void SetDelimiter(char separator) { + this->Delimiter = separator; } /// Gives a constant references for the \c std::tuple containing the values /// read by \p this object. /// /// \return \c CSVRow::Data const std::tuple &tuple(void) const noexcept { return Data; } private: std::tuple Data; ///< Stores parsed entries size_t SkipRows; ///< The number of rows to skip at the very beginning of the file. ///< This number only applies on the number of data rows. ///< If your file contains a header row and data rows, the skiping ///< of the header row is not taken into account. HeaderInformation HeaderInfo; ///< If the file contains a header row or not. - char Delimeter; ///< The seperator between the data entries. + char Delimiter; ///< The seperator between the data entries. size_t RowNumber; ///< Current row number, counts all row numbers including the header row. bool IsHeaderRead; ///< Was the header read or not. std::vector Header; ///< The content of the header row. }; /// Reads a row of CSV data into \c rosa::csv::CSVRow. /// /// The next line is read from \p Str by calling /// \c rosa::csv::CSVRow::readNextRow on \p Data until all lines are /// skipped. /// /// If the function is called for the first time and the file contains /// a header than is the header and the first data row read in after the /// number of rows that the user wants to skip. /// /// \tparam Ts type of values to read from the row /// /// \note The CSV file should contain a line with fields matching \p Ts... /// /// \param [in,out] Str input stream of a CSV file /// \param [in,out] Data object to read the next line into /// /// \return \p Str after reading one line from it template std::istream &operator>>(std::istream &Str, CSVRow &Data) { if( Data.HasFileHeader() == HeaderInformation::HasHeader && !Data.IsHeaderReadDone() ) { Data.readHeader(Str); } while(Data.CurRow() < (Data.SkipNumRows())){ Data.readNextRow(Str); } //read the lines after you skipped the number of rows you want to skip Data.readNextRow(Str); return Str; } } // End namespace /// Provides `InputIterator` features for iterating over a CSV file. /// /// The iterator parses rows into `std::tuple` values and iterates over the /// file row by row. /// /// \tparam Ts types of values stored in one row of the CSV file /// /// \note The iterator expects each row to consists of fields matching \p Ts. /// /// \note The implementation relies on \c rosa::csv::CSVRow, which in turn /// relies on \c rosa::csv::CSVRowParser, which is implemented only for /// `arithmetic` types -- signed and unsigned integral types and floating-point /// types -- and for \c std::string. Those are the valid values for \p Ts template class CSVIterator { public: /// \defgroup CSVIteratorTypedefs Typedefs of rosa::csv::CSVIterator /// /// Standard `typedef`s for iterators. /// ///@{ typedef std::input_iterator_tag iterator_category; ///< Category of the iterator. typedef std::tuple value_type; ///< Type of values iterated over. typedef std::size_t difference_type; ///< Type to identify distance. typedef std::tuple *pointer; ///< Pointer to the type iterated over. typedef std::tuple &reference; ///< Reference to the type iterated over. ///@} /// Creates a new instance. /// /// \param [in,out] S input stream to iterate over /// \param SkipRows the number of rows you want to skip only once at the beginning of the file. /// If you have an header in the file, it is supposed to be the first row, and it will be always read out. /// But after this header the next number of Rows will be skipped. /// \param HeaderInfo is used to know wheter the file contains an header row or not. /// The header has to be in the first row. - /// \param Delimeter is the separator between the differnt values of the csv file. + /// \param Delimiter is the separator between the differnt values of the csv file. CSVIterator(std::istream &S, const size_t SkipRows = 0, const HeaderInformation HeaderInfo = HeaderInformation::HasHeader, - const char Delimeter = ',') : Str(S.good() ? &S : nullptr), - SkipRows(SkipRows), HeaderInfo(HeaderInfo), Delimeter(Delimeter), Row(){ + const char Delimiter = ',') : Str(S.good() ? &S : nullptr), + SkipRows(SkipRows), HeaderInfo(HeaderInfo), Delimiter(Delimiter), Row(){ Row.SetSkipRows(SkipRows); Row.SetHeaderInfo(HeaderInfo); - Row.SetDelimeter(Delimeter); + Row.SetDelimiter(Delimiter); // \c rosa::csv::CSVIterator::Row is initialized empty so the first // incrementation here will read the first row. ++(*this); } /// Creates an empty new instance. CSVIterator(void) noexcept : Str(nullptr), SkipRows(0), HeaderInfo(HeaderInformation::HasHeader), - Delimeter(','), Row() {} + Delimiter(','), Row() {} /// Pre-increment operator. /// /// The implementation reads the next row. If the end of the input stream is /// reached, the operator becomes empty and has no further effect. /// /// \return \p this object after incrementing it. CSVIterator &operator++() { if (Str) { if (!((*Str) >> Row)) { Str = nullptr; } } return *this; } /// Post-increment operator. /// /// The implementation uses the pre-increment operator and returns a copy of /// the original state of \p this object. /// /// \return \p this object before incrementing it. CSVIterator operator++(int) { CSVIterator Tmp(*this); ++(*this); return Tmp; } /// Returns a constant reference to the current entry. /// /// \note Should not dereference the iterator when it is empty. /// /// \return constant reference to the current entry. const std::tuple &operator*(void)const noexcept { return Row.tuple(); } /// Returns a constant pointer to the current entry. /// /// \note Should not dereference the iterator when it is empty. /// /// \return constant pointer to the current entry. const std::tuple *operator->(void)const noexcept { return &Row.tuple(); } /// Tells if \p this object is equal to another one. /// /// Two \c rosa::csv::CSVReader instances are equal if and only if they are /// the same or both are empty. /// /// \param RHS other object to compare to /// /// \return whether \p this object is equal with \p RHS bool operator==(const CSVIterator &RHS) const noexcept { return ((this == &RHS) || ((this->Str == nullptr) && (RHS.Str == nullptr))); } /// Tells if \p this object is not equal to another one. /// /// \see rosa::csv::CSVReader::operator== /// /// \param RHS other object to compare to /// /// \return whether \p this object is not equal with \p RHS. bool operator!=(const CSVIterator &RHS) const noexcept { return !((*this) == RHS); } - /// Set the delimeter used in the csv file. + /// Set the delimiter used in the csv file. /// \param Separator the character which separates the values in the csv file. - inline void setDelimeter(char Separator) noexcept { - this->Delimeter = Separator; + inline void setDelimiter(char Separator) noexcept { + this->Delimiter = Separator; } - /// get the delimeter currently set to separate the values in the csv file. + /// get the delimiter currently set to separate the values in the csv file. /// \return the current character, which is used to separte teh values in the csv file. - inline char getDelimeter() const noexcept { - return this->Delimeter; + inline char getDelimiter() const noexcept { + return this->Delimiter; } private: std::istream *Str; ///< Input stream of a CSV file to iterate over. size_t SkipRows; ///< Number of Rows to skip only once at the beginning of the file. HeaderInformation HeaderInfo; ///< does the csv file contain a header or not, if this information is ///< not given correclty, the reading of the header would result in ///< in an error. - char Delimeter; ///< Delimeter between the entries in the csv file. + char Delimiter; ///< Delimiter between the entries in the csv file. CSVRow Row; ///< Content of the current row }; } // End namespace csv } // End namespace rosa #endif // ROSA_SUPPORT_CSV_CSVREADER_HPP diff --git a/include/rosa/support/csv/CSVWriter.hpp b/include/rosa/support/csv/CSVWriter.hpp index 9dd6b9f..f621cbd 100755 --- a/include/rosa/support/csv/CSVWriter.hpp +++ b/include/rosa/support/csv/CSVWriter.hpp @@ -1,227 +1,227 @@ //===-- rosa/support/csv/CSVWriter.hpp --------------------------*- C++ -*-===// // // The RoSA Framework // // Distributed under the terms and conditions of the Boost Software License 1.0. // See accompanying file LICENSE. // // If you did not receive a copy of the license file, see // http://www.boost.org/LICENSE_1_0.txt. // //===----------------------------------------------------------------------===// /// /// \file rosa/support/csv/CSVWriter.hpp /// /// \authors David Juhasz (david.juhasz@tuwien.ac.at) /// Edwin Willegger (edwin.willegger@tuwien.ac.at) /// /// \date 2017-2019 /// /// \brief Facitilities to write CSV files. /// //===----------------------------------------------------------------------===// #ifndef ROSA_SUPPORT_CSV_CSVWRITER_HPP #define ROSA_SUPPORT_CSV_CSVWRITER_HPP #include #include #include #include #include #include "rosa/support/log.h" namespace rosa { namespace csv { /// Provides facilities to write values into a CSV file. /// /// The writer emits a comma, the character `,`, between each written values. /// The resulted stream is a flat CSV file as it consists of onlyone row, no new /// line is emitted. /// /// \tparam T type of values to write template class CSVWriter { public: /// Creates a new instance. /// /// \param [in,out] S output stream to write to /// /// \note The writer operates on non-binary outputs as long as \p S is in /// good state. CSVWriter(std::ostream &S) : Str(S.good() && !(S.flags() & std::ios::binary) ? &S : nullptr), IsFirst(true) {} /// Tells if the last operation was successful. /// /// \return if the last operation was successful bool good(void) const noexcept { return Str != nullptr; } /// Writes an entry to the output stream. /// /// The implementation does anything only if the last operation was /// successful. If so, \p V is written to \c rosa::csv::CSVWriter::Str. /// The emitted value is preceded with a comma if the actual call is not the /// first one for \p this object. Success of the operation is checked at the /// end. /// /// \param V value to write void write(const T &V) { if (Str) { if (!IsFirst) { *Str << ','; } else { IsFirst = false; } *Str << V; if (!Str->good()) { Str = nullptr; } } } private: std::ostream *Str; ///< Output stream to write to. bool IsFirst; ///< Denotes if the next write would be the first one. }; /// Writes a tuple of values into a CSV file /// /// \tparam Ts types of values to write template class CSVTupleWriter { public: // typedef value_type ; ///< Type of values written. typedef std::tuple value_type; /// Creates a new instance. /// /// \param [in,out] S output stream to write to /// /// \note The writer operates on non-binary outputs as long as \p S is in /// good state. CSVTupleWriter(std::ostream &S) : Str(S.good() && !(S.flags() & std::ios::binary) ? &S : nullptr), IsHeaderWritten(false), IsDataWritten(false) {} /// Tells if the last operation was successful. /// /// \return if the last operation was successful bool good(void) const noexcept { return Str != nullptr; } /// Write the values of a tuple to a CSV file with \c rosa::csv::CSVTupleWriter. /// /// \see rosa::csv::CSVTupleWriter /// /// /// \param [in,out] values tuple, which values are written in a recusive fashion into a stream. template void write(const std::tuple &values) { constexpr size_t size = sizeof...(Ts); LOG_TRACE_STREAM << "Writing tuple values into file \n"; LOG_TRACE_STREAM << " Tuple has " << std::to_string(size) << " elements. \n"; LOG_TRACE_STREAM << " Value is " << std::get(values); if(Str){ /// Write the current element of the tuple into the stream and add a separtor after it, /// and call the function for the next element in the tuple. if constexpr(i+1 != sizeof...(Ts)){ *Str << std::get(values) << ", "; write(values); /// If the last element is written into the stream than begin a new line. }else if constexpr(i + 1 == sizeof...(Ts)){ *Str << std::get(values) << '\n'; /// every time the last data value of a line is written, the flag indicates that data was already written into the file. IsDataWritten = true; } } } /// Write the header values to a CSV file with \c rosa::csv::CSVTupleWriter. /// /// \note The function has no effect if anything has already been written /// to the output stream either by \c /// rosa::csv::CSVTupleWriter::writeHeader() or \c /// rosa::csv::CSVTupleWriter::write(). /// /// \see rosa::csv::CSVTupleWriter /// /// \param header the content of the header line. void writeHeader(const std::array &header){ size_t index = 0; /// write into the stream only, if it is not a nullptr, and if no data and no header was already written into it. if(Str && IsDataWritten == false && IsHeaderWritten == false){ index = 0; for (auto i = header.begin(); i != header.end(); ++i){ index = index + 1; - /// write into the stream every entry with a delimeter, in this case ", " until + /// write into the stream every entry with a delimiter, in this case ", " until /// the last entry if(index != header.size()){ *Str << *i << ", "; - /// write the last entry into the stream, without any delimeter + /// write the last entry into the stream, without any delimiter }else { *Str << *i; } } /// finish the header line and start a new line. *Str << '\n'; /// now it is not possible to write additional header lines. IsHeaderWritten = true; } } private: std::ostream *Str; ///< Output stream to write to. bool IsHeaderWritten; ///< If an header line was already written into the stream. If set than no additional header could be written. bool IsDataWritten; ///< If one line of data has already been written into the stream, than no headerline could be added. }; /// Writes all values of a tuple to a CSV file with \c rosa::csv::CSVTupleWriter. /// /// \see rosa::csv::CSVTupleWriter /// /// \tparam Ts types of values to write /// /// \param [in,out] W object to write with /// \param V values to write /// /// \return \p W after writing \p V with it template CSVTupleWriter &operator<<(CSVTupleWriter &W, const std::tuple &V) { W.write(V); return W; } /// Writes a value to a CSV file with \c rosa::csv::CSVWriter. /// /// \see rosa::csv::CSVWriter /// /// \tparam T type of value to write /// /// \param [in,out] W object to write with /// \param V value to write /// /// \return \p W after writing \p V with it template CSVWriter &operator<<(CSVWriter &W, const T& V) { W.write(V); return W; } } // End namespace csv } // End namespace rosa #endif // ROSA_SUPPORT_CSV_CSVWRITER_HPP