diff --git a/examples/CSVFiles/main.cpp b/examples/CSVFiles/main.cpp index 23f3955..2f74305 100644 --- a/examples/CSVFiles/main.cpp +++ b/examples/CSVFiles/main.cpp @@ -1,328 +1,126 @@ -//===-- apps/sa-ews1/sa-ews1.cpp --------------------------------*- C++ -*-===// +//===-- examples/CSVFiles/main.cpp --------------------------------*- C++ -*-===// // -// The RoSA Framework -- Application SA-EWS1 +// The RoSA Framework -- Example CSVFiles // //===----------------------------------------------------------------------===// /// -/// \file apps/sa-ews1/sa-ews1.cpp +/// \file examples/CSVFiles/main.cpp /// -/// \author David Juhasz (david.juhasz@tuwien.ac.at) +/// \author Edwin Willegger (edwin.willegger@tuwien.ac.at) /// -/// \date 2017-2019 +/// \date 2019 /// -/// \brief The application SA-EWS1 implements the case study from the paper: -/// M. Götzinger, N. Taherinejad, A. M. Rahmani, P. Liljeberg, A. Jantsch, and -/// H. Tenhunen: Enhancing the Early Warning Score System Using Data Confidence -/// DOI: 10.1007/978-3-319-58877-3_12 +/// \brief This example shows how you could use the CSVIterator class to +/// get data into RoSA from a CSV-file. //===----------------------------------------------------------------------===// -#include "rosa/agent/Abstraction.hpp" -#include "rosa/agent/Confidence.hpp" - -#include "rosa/config/version.h" - -#include "rosa/deluxe/DeluxeContext.hpp" - -#include "rosa/support/csv/CSVReader.hpp" -#include "rosa/support/csv/CSVWriter.hpp" +// To compile with dumping record layout: +// $ clang++ -o variadic-tuple -Xclang -fdump-record-layouts variadic-tuple.cpp +// -Wall -g -std=c++11 +#include +#include +#include +#include +#include #include +#include +#include +#include +#include +#include +#include +#include +#include +#include -using namespace rosa; -using namespace rosa::agent; -using namespace rosa::deluxe; -using namespace rosa::terminal; - -const std::string AppName = "Test-CSVFiles"; - -/// Paths for the CSV files for simulation. -/// -///@{ -//const std::string HRCSVPath = "HR.csv"; -const std::string HRCSVPathOld = "HR-New.csv"; -const std::string HRCSVPath = "HR-New-Semicolon.csv"; -const std::string BRCSVPath = "BR.csv"; -const std::string SpO2CSVPath = "SpO2.csv"; -const std::string BPSysCSVPath = "BPSys.csv"; -const std::string BodyTempCSVPath = "BodyTemp.csv"; -const std::string ScoreCSVPath = "Score.csv"; -///@} - -/// How many cycles of simulation to perform. -const size_t NumberOfSimulationCycles = 16; - -/// Warning levels for abstraction. -enum WarningScore { No = 0, Low = 1, High = 2, Emergency = 3 }; - -/// Helper function creating a deluxe agent for pre-processing sensory values. -/// -/// Received values are first validated for confidence. Values which the -/// validator does not mark confident are ignored. Confident values are -/// abstracted into a \c WarningScore value, which is the result of the -/// processing function. -/// -/// \note The result, \c WarningScore, is returned as \c uint32_t because -/// enumeration types are not integrated into built-in types. Hence, a master -/// to these agents receives its input as \c uint32_t values, and may cast them -/// to \c WarningScore explicitly. -/// -/// \tparam T type of values to receive from the sensor -/// -/// \param C the deluxe context to create the agent in -/// \param Name name of the new agent -/// \param CC confidence validator to use -/// \param A abstraction to use -/// -/// \return handle for the new agent -template -AgentHandle createLowLevelAgent(std::unique_ptr &C, - const std::string &Name, - const Confidence &CC, - const Abstraction &A) { - using handler = DeluxeAgent::D; - using result = Optional; - return C->createAgent( - Name, handler([&, Name](std::pair I) -> result { - LOG_INFO_STREAM << "\n******\n" - << Name << " " << (I.second ? "" : "") - << " value: " << I.first << "\n******\n"; - return (I.second && CC(I.first)) ? result(A(I.first)) : result(); - })); -} - -int main(void) { - LOG_INFO_STREAM - << '\n' - << library_string() << " -- " << Color::Red << AppName << "app" - << Color::Default << '\n' - << Color::Yellow - << "CSV files are read from and written to the current working directory." - << Color::Default << '\n'; - - std::unique_ptr C = DeluxeContext::create(AppName); - - // - // 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. - AgentHandle HRSensor = C->createSensor("HR Sensor"); - AgentHandle BRSensor = C->createSensor("BR Sensor"); - AgentHandle SpO2Sensor = C->createSensor("SpO2 Sensor"); - AgentHandle BPSysSensor = C->createSensor("BPSys Sensor"); - AgentHandle BodyTempSensor = C->createSensor("BodyTemp Sensor"); - - // - // Create functionalities. - // - LOG_INFO("Creating Functionalities for Agents."); - - // - // Define confidence validators. - // - // Lower bounds are inclusive and upper bounds are exclusive. - - Confidence HRConfidence(0, 501); - Confidence BRConfidence(0, 301); - Confidence SpO2Confidence(0, 101); - Confidence BPSysConfidence(0,501); - Confidence BodyTempConfidence(-60, - nextRepresentableFloatingPoint(50.0f)); - - // - // Define abstractions. - // - - RangeAbstraction HRAbstraction( - {{{0, 40}, Emergency}, - {{40, 51}, High}, - {{51, 60}, Low}, - {{60, 100}, No}, - {{100, 110}, Low}, - {{110, 129}, High}, - {{129, 200}, Emergency}}, - Emergency); - - RangeAbstraction BRAbstraction({{{0, 9}, High}, - {{9, 14}, No}, - {{14, 20}, Low}, - {{20, 29}, High}, - {{29, 50}, Emergency}}, - Emergency); - - RangeAbstraction SpO2Abstraction({{{1, 85}, Emergency}, - {{85, 90}, High}, - {{90, 95}, Low}, - {{95, 100}, No}}, - Emergency); - - RangeAbstraction BPSysAbstraction( - {{{0, 70}, Emergency}, - {{70, 81}, High}, - {{81, 101}, Low}, - {{101, 149}, No}, - {{149, 169}, Low}, - {{169, 179}, High}, - {{179, 200}, Emergency}}, - Emergency); - - RangeAbstraction BodyTempAbstraction( - {{{0.f, 28.f}, Emergency}, - {{28.f, 32.f}, High}, - {{32.f, 35.f}, Low}, - {{35.f, 38.f}, No}, - {{38.f, 39.5f}, High}, - {{39.5f, 100.f}, Emergency}}, - Emergency); - - // - // Create low-level deluxe agents with \c createLowLevelAgent. - // - LOG_INFO("Creating low-level agents."); - - AgentHandle HRAgent = - createLowLevelAgent(C, "HR Agent", HRConfidence, HRAbstraction); - AgentHandle BRAgent = - createLowLevelAgent(C, "BR Agent", BRConfidence, BRAbstraction); - AgentHandle SpO2Agent = - createLowLevelAgent(C, "SpO2 Agent", SpO2Confidence, SpO2Abstraction); - AgentHandle BPSysAgent = - createLowLevelAgent(C, "BPSys Agent", BPSysConfidence, BPSysAbstraction); - AgentHandle BodyTempAgent = createLowLevelAgent( - C, "BodyTemp Agent", BodyTempConfidence, BodyTempAbstraction); - - // - // Connect sensors to low-level agents. - // - LOG_INFO("Connect sensors to their corresponding low-level agents."); - - C->connectSensor(HRAgent, 0, HRSensor, "HR Sensor Channel"); - C->connectSensor(BRAgent, 0, BRSensor, "BR Sensor Channel"); - C->connectSensor(SpO2Agent, 0, SpO2Sensor, "SpO2 Sensor Channel"); - C->connectSensor(BPSysAgent, 0, BPSysSensor, "BPSys Sensor Channel"); - C->connectSensor(BodyTempAgent, 0, BodyTempSensor, "BodyTemp Sensor Channel"); - - // - // Create a high-level deluxe agent. - // - LOG_INFO("Create high-level agent."); - - // The new agent logs its input values and results in the the sum of them. - AgentHandle BodyAgent = C->createAgent( - "Body Agent", - DeluxeAgent::D( - [](std::pair HR, std::pair BR, - std::pair SpO2, std::pair BPSys, - std::pair BodyTemp) -> Optional { - LOG_INFO_STREAM << "\n*******\nBody Agent trigged with values:\n" - << (HR.second ? "" : "") - << " HR warning score: " << HR.first << "\n" - << (BR.second ? "" : "") - << " BR warning score: " << BR.first << "\n" - << (SpO2.second ? "" : "") - << " SpO2 warning score: " << SpO2.first << "\n" - << (BPSys.second ? "" : "") - << " BPSys warning score: " << BPSys.first << "\n" - << (BodyTemp.second ? "" : "") - << " BodyTemp warning score: " << BodyTemp.first - << "\n******\n"; - return {HR.first + BR.first + SpO2.first + BPSys.first + - BodyTemp.first}; - })); - - // - // Connect low-level agents to the high-level agent. - // - LOG_INFO("Connect low-level agents to the high-level agent."); - - C->connectAgents(BodyAgent, 0, HRAgent, "HR Agent Channel"); - C->connectAgents(BodyAgent, 1, BRAgent, "BR Agent Channel"); - C->connectAgents(BodyAgent, 2, SpO2Agent, "SpO2 Agent Channel"); - C->connectAgents(BodyAgent, 3, BPSysAgent, "BPSys Agent Channel"); - C->connectAgents(BodyAgent, 4, BodyTempAgent, "BodyTemp Agent Channel"); +#include "rosa/support/csv/CSVReader.hpp" - // - // For simulation output, create a logger agent writing the output of the - // high-level agent into a CSV file. - // - LOG_INFO("Create a logger agent."); +const std::string AppName = "test-csvfiles"; - // Create CSV writer. - std::ofstream ScoreCSV(ScoreCSVPath); - csv::CSVWriter ScoreWriter(ScoreCSV); +//the example will be executed in the bin directory, but the data files are located in another the examples folder - // The agent writes each new input value into a CSV file and produces nothing. - AgentHandle LoggerAgent = C->createAgent( - "Logger Agent", - DeluxeAgent::D( - [&ScoreWriter](std::pair Score) -> Optional { - if (Score.second) { - // The state of \p ScoreWriter is not checked, expecting good. - ScoreWriter << Score.first; - } - return {}; - })); +const std::string Path = "../examples/CSVFiles/"; - // - // Connect the high-level agent to the logger agent. - //parseparse - LOG_INFO("Connect the high-level agent to the logger agent."); +void testtuple(){ - C->connectAgents(LoggerAgent, 0, BodyAgent, "Body Agent Channel"); + //Different stream objects to work on CSV data and show the limitations of the current implementation. + //If you don't find any of these files, just create your own data files. + //file contains header and valid data entries, delimter = ',' + std::ifstream file_header_data(Path + "HR-New.csv"); + //file contains header and valid data entries, delimter = ',' + std::ifstream file_header_data_2(Path + "HR-New.csv"); + //file contains header and valid data entries, delimter = ',' + std::ifstream file_header_data_3(Path + "HR-New.csv"); + //file contains header and valid data entries, delimter = ',' + std::ifstream file_header_data_4(Path + "HR-New.csv"); + //file contains header and valid data entries, delimter = ',' + std::ifstream file_header_data_5(Path + "HR-New.csv"); + //file contains header and valid data entries, delimter = ',' + std::ifstream file_header_data_6(Path + "HR-New.csv"); + //file contains no header an valid data entries, delimter = ',' + std::ifstream file2(Path + "HR.csv"); + //file contains header and valid data entries, delimter = ';' + std::ifstream file3(Path + "HR-New-Semicolon.csv"); - // - // Do simulation. - // - LOG_INFO("Setting up and performing simulation."); + rosa::csv::CSVIterator it(file_header_data); - // - // Initialize deluxe context for simulation. - // + it.setDelimeter(','); - C->initializeSimulation(); + it++; + 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. + it++; + it++; - // - // Open CSV files and register them for their corresponding sensors. - // + //------------------------------------------------------------------- + // a possiblity to get the data out of the iterator + std::tuple value = *it; - // Type aliases for iterators. - using CSVInt = csv::CSVIterator; - using CSVFloat = csv::CSVIterator; - //std::ifstream HRCSV(HRCSVPath); + std::cout << "Values are: " << std::get<0>(value) << " " + << std::get<1>(value) << " ,are you now happy?" << std::endl; - //CSVInt testIt(HRCSV); + //-------------------------------------------------------------------- + //testing differnet parameters to the constructor - /* std::ifstream HRCSV(HRCSVPath); - C->registerSensorValues(HRSensor, CSVInt(HRCSV, 5, false, ';', '\n'), CSVInt()); */ + //uncomment to see that it is not possible to iterate over an vector in the tuple. + //rosa::csv::CSVIterator> it2(file, 1); - std::ifstream HRCSVOld(HRCSVPathOld); - C->registerSensorValues(HRSensor, CSVInt(HRCSVOld/*, 2*/), CSVInt()); + //try to skip a valid number of lines after the header + rosa::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 + //rosa::csv::CSVIterator it2_1(file_header_data_3, 0, rosa::csv::HeaderInformation::HasNoHeader); - /* - std::ifstream BRCSV(HRCSVPath); - C->registerSensorValues(BRSensor, CSVInt(BRCSV, 4, false, ';', '\n'), CSVInt()); */ + //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 + // rosa::csv::CSVIterator it2_2(file_header_data_4, 1, rosa::csv::HeaderInformation::HasNoHeader); - std::ifstream SpO2CSV(HRCSVPath); - C->registerSensorValues(SpO2Sensor, CSVInt(SpO2CSV/*, 2, false, ';', '\n'*/), CSVInt()); + //try to skip a valid number of lines of a file without header + rosa::csv::CSVIterator it2_3(file2, 1, rosa::csv::HeaderInformation::HasNoHeader); + //try to skip a valid number of lines after the header, but with different delimeter + rosa::csv::CSVIterator it2_4(file3, 2,rosa::csv::HeaderInformation::HasHeader, ';'); - /* - std::ifstream BPSysCSV(BPSysCSVPath); - C->registerSensorValues(BPSysSensor, CSVInt(BPSysCSV), CSVInt());*/ + // if you skip more lines than valid, you generate an infinte loop + //rosa::csv::CSVIterator it3(file_header_data_5, 500); - std::ifstream BodyTempCSV(HRCSVPath); - C->registerSensorValues(BodyTempSensor, CSVFloat(BodyTempCSV/*, 2, false, ';', '\n'*/), CSVFloat()); + //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. + rosa::csv::CSVIterator it4(file_header_data_6); +} - // - // Simulate. - // +int main(void) { + std::cout << "This is a short example to show you how you could use the CSVIterator class." << std::endl; + std::cout << "There are also some limitations explained in the source code of the example." << std::endl; - C->simulate(NumberOfSimulationCycles); + testtuple(); + std::cout << "All tests finished sucessfully" << std::endl; return 0; } + diff --git a/include/rosa/support/csv/CSVReader.hpp b/include/rosa/support/csv/CSVReader.hpp index 0df2355..5402018 100755 --- a/include/rosa/support/csv/CSVReader.hpp +++ b/include/rosa/support/csv/CSVReader.hpp @@ -1,492 +1,486 @@ //===-- rosa/support/csv/CSVReader.hpp --------------------------*- C++ -*-===// // // The RoSA Framework // //===----------------------------------------------------------------------===// /// /// \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 +#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 { -///\defgroup ValueParser Helper template struct to parse a value -/// -///@{ - /// 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 { - /// Parses one value from \p Cell + /// /// /// \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 specialization for signed integral types. 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 specialization for unsigned integral types. 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 specialization for floating-point types. 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 specialization for \c std::string. 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, Seq) { + void parseRow(std::stringstream &LineStream, char Delimeter, 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, ','), + ((std::getline(LineStream, Cell, Delimeter), 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. CSVRow(const size_t SkipRows = 0, const HeaderInformation HeaderInfo = HeaderInformation::HasHeader, const char Delimeter = ',') : SkipRows(SkipRows), HeaderInfo(HeaderInfo), Delimeter(Delimeter), 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, seq_t()); + parseRow(LineStream, Delimeter, 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) ){ 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 SkipRows the number of rows you want to skip at the beginning of the file. inline void SetSkipRows(const size_t SkipRows) noexcept { this->SkipRows = SkipRows; } /// 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 HeaderInfo if the first row is a header row or not. inline void SetHeaderInfo(const HeaderInformation HeaderInfo) noexcept { this->HeaderInfo = HeaderInfo; } /// Set the seperator between data entries. /// /// This funcction sets the separator between the data entries of the csv file. /// /// \param Delimeter the character that separates the data values. inline void SetDelimeter(char Delimeter) { this->Delimeter = Delimeter; } /// 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 RowNumber; ///< Current row number, counts all row numbers including the header row. - HeaderInformation HeaderInfo; ///< If the file contains a header row or not. 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. - std::vector Header; ///< The content of the header row. - bool IsHeaderRead; ///< Was the header read or not. + HeaderInformation HeaderInfo; ///< If the file contains a header row or not. char Delimeter; ///< 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. CSVIterator(std::istream &S, const size_t SkipRows = 0, const HeaderInformation HeaderInfo = HeaderInformation::HasHeader, - const char Delimeter = ',') : Str(S.good() ? &S : nullptr), Row(), - SkipRows(SkipRows), HeaderInfo(HeaderInfo), Delimeter(Delimeter){ + const char Delimeter = ',') : Str(S.good() ? &S : nullptr), + SkipRows(SkipRows), HeaderInfo(HeaderInfo), Delimeter(Delimeter), Row(){ Row.SetSkipRows(SkipRows); Row.SetHeaderInfo(HeaderInfo); Row.SetDelimeter(Delimeter); // \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) {} /// 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. /// \param Delimeter the character which separates the values in the csv file. inline void setDelimeter(char Delimeter) noexcept { this->Delimeter = Delimeter; } /// get the delimeter 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; } private: std::istream *Str; ///< Input stream of a CSV file to iterate over. - CSVRow Row; ///< Content of the current row - char Delimeter; ///< Delimeter between the entries in the csv file. + 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. - size_t SkipRows; ///< Number of Rows to skip only once at the beginning of the file. + ///< in an error. + char Delimeter; ///< Delimeter 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