diff --git a/include/cute.h b/include/cute.h index 1dd22106b8c21fbfff16b3e375e6cc5d953dbdd7..712cedefc3f332cdd58d570886bb05f79bbfe4e6 100644 --- a/include/cute.h +++ b/include/cute.h @@ -10,7 +10,7 @@ namespace cute { - struct Point ///< Representation of a Point in 2D-Space used by this library. + struct Point /// Representation of a Point in 2D-Space used by this library. { float x; float y; @@ -29,7 +29,7 @@ namespace cute all }; - enum Scale ///< Representing different axis scalings. + enum Scale /// Representing different axis scalings. { linear, logarithmic @@ -49,7 +49,7 @@ namespace cute uint8_t transparency; }; - enum LineStyle ///< Different possibilities to draw your lines. + enum LineStyle /// Different possibilities to draw your lines. { none, solid, @@ -59,43 +59,43 @@ namespace cute DashDotDot }; - enum Shape ///< Draw your Points as circles or rectangles. + enum Shape /// Draw your Points as circles or rectangles. { circle, rectangle }; - namespace Colors ///< A collection of Color presets. Not exhaustive, try them, change them, suggest other ones. + namespace Colors /// A collection of Color presets. Not exhaustive, try them, change them, suggest other ones. { - constexpr Color none = {0, 0, 0, 0xFF}; ///< {0, 0, 0, 0xFF} + constexpr Color none = {0, 0, 0, 0xFF}; ///< {0, 0, 0, 0xFF} constexpr Color white = {0xFF, 0xFF, 0xFF, 0}; ///< {0xFF, 0xFF, 0xFF, 0} - constexpr Color black = {0, 0, 0, 0}; ///< {0, 0, 0, 0} - constexpr Color red = {0xFF, 0, 0, 0}; ///< {0xFF, 0, 0, 0} - constexpr Color darkRed = {0x80, 0, 0, 0}; ///< {0x80, 0, 0, 0} - constexpr Color green = {0, 0xFF, 0, 0}; ///< {0, 0xFF, 0, 0} - constexpr Color darkGreen = {0, 0x80, 0, 0}; ///< {0, 0x80, 0, 0} - constexpr Color blue = {0, 0, 0xFF, 0}; ///< {0, 0, 0xFF, 0} - constexpr Color darkBlue = {0, 0, 0x80, 0}; ///< {0, 0, 0x80, 0} - constexpr Color cyan = {0, 0xFF, 0xFF, 0}; ///< {0, 0xFF, 0xFF, 0} - constexpr Color darkCyan = {0, 0x80, 0x80, 0}; ///< {0, 0x80, 0x80, 0} - constexpr Color magenta = {0xFF, 0, 0xFF, 0}; ///< {0xFF, 0, 0xFF, 0} - constexpr Color darkMagenta = {0x80, 0, 0x80, 0}; ///< {0x80, 0, 0x80, 0} - constexpr Color yellow = {0xFF, 0xFF, 0, 0}; ///< {0xFF, 0xFF, 0, 0} - constexpr Color darkYellow = {0x80, 0x80, 0, 0}; ///< {0x80, 0x80, 0, 0} + constexpr Color black = {0, 0, 0, 0}; ///< {0, 0, 0, 0} + constexpr Color red = {0xFF, 0, 0, 0}; ///< {0xFF, 0, 0, 0} + constexpr Color darkRed = {0x80, 0, 0, 0}; ///< {0x80, 0, 0, 0} + constexpr Color green = {0, 0xFF, 0, 0}; ///< {0, 0xFF, 0, 0} + constexpr Color darkGreen = {0, 0x80, 0, 0}; ///< {0, 0x80, 0, 0} + constexpr Color blue = {0, 0, 0xFF, 0}; ///< {0, 0, 0xFF, 0} + constexpr Color darkBlue = {0, 0, 0x80, 0}; ///< {0, 0, 0x80, 0} + constexpr Color cyan = {0, 0xFF, 0xFF, 0}; ///< {0, 0xFF, 0xFF, 0} + constexpr Color darkCyan = {0, 0x80, 0x80, 0}; ///< {0, 0x80, 0x80, 0} + constexpr Color magenta = {0xFF, 0, 0xFF, 0}; ///< {0xFF, 0, 0xFF, 0} + constexpr Color darkMagenta = {0x80, 0, 0x80, 0}; ///< {0x80, 0, 0x80, 0} + constexpr Color yellow = {0xFF, 0xFF, 0, 0}; ///< {0xFF, 0xFF, 0, 0} + constexpr Color darkYellow = {0x80, 0x80, 0, 0}; ///< {0x80, 0x80, 0, 0} constexpr Color gray = {0xA0, 0xA0, 0xA4, 0}; ///< {0xA0, 0xA0, 0xA4, 0} constexpr Color darkGray = {0x80, 0x80, 0x80, 0}; ///< {0x80, 0x80, 0x80, 0} constexpr Color lightGray = {0xC0, 0xC0, 0xC0, 0}; ///< {0xC0, 0xC0, 0xC0, 0} } // namespace Colors - struct PlotProperties ///< A structure to bundle all the possibilities to infuelce how your Data should be plotted. + struct PlotProperties /// A structure to bundle all the possibilities to infuelce how your Data should be plotted. { std::string name = ""; ///< The name displayed in the Legend. An empty name will cause it not to be displayed. int size = 10; ///< The diameter of points and the width of lines. No fix unit sadly, adjust yourself. - Color color = Colors::none; ///< The color of what you want plotted. The default value none will have a color chosen at random. - LineStyle line = solid; ///< The style, you want lines to be drawn in. - bool onTop = false; ///< Wether that Data should always be displayed above other data. So you dont have to redraw points manually all the time. - bool legend = true; ///< Whether you want the data to show up in the legend. - Shape shape = circle; ///< The shape you want points to have. + Color color = Colors::none; ///< The color of what you want plotted. Default value none -> random color. + LineStyle line = solid; ///< The style, you want lines to be drawn in. + bool onTop = false; ///< Whether the Data should always be drawn above other data. + bool legend = true; ///< Whether you want the data to show up in the legend. + Shape shape = circle; ///< The shape you want points to have. }; void DYNAMIC setPlotVisible(int plot, bool visible = true); void DYNAMIC setPlotTitle(int plot, const std::string& title); @@ -157,11 +157,11 @@ namespace cute { // These are for loading and unloading libcute.dll at runtime #if !defined(RUNTIME_LOAD) and !defined(EXPORT_CUTE) - inline int initialize() ///< In case of not loading at runtime and importing cute as the User, do nothing. + inline int initialize() ///< In case of not loading at runtime and importing cute as the User, do nothing. { return 0; } - inline void shutdown() {} ///< In case of not loading at runtime and importing cute as the User, do nothing. + inline void shutdown() {} ///< In case of not loading at runtime and importing cute as the User, do nothing. #else int initialize(); void shutdown(); diff --git a/src/configPanel.cpp b/src/configPanel.cpp index a78de029e004987c16d41c6199874ca612d8338c..98c1da57092cb1f6ea8a9ce8b6ba12e9d97c92d3 100644 --- a/src/configPanel.cpp +++ b/src/configPanel.cpp @@ -95,7 +95,7 @@ ConfigPanel::ConfigPanel(QWidget* parent) : QDockWidget{parent} stackedWidget->setCurrentWidget(pasteWidget); - for (auto &&button : lockButtons) + for (auto&& button : lockButtons) { connect(button, &QPushButton::clicked, this, lockButtonClicked); } @@ -117,7 +117,7 @@ void ConfigPanel::setRunning(bool running) void ConfigPanel::setGenLocked() { - for (auto &&button : lockButtons) + for (auto&& button : lockButtons) { button->setText("Unlock"); } @@ -129,7 +129,7 @@ void ConfigPanel::setGenLocked() void ConfigPanel::setGenUnlocked() { - for (auto &&button : lockButtons) + for (auto&& button : lockButtons) { button->setText("Lock"); } diff --git a/src/configPanel.h b/src/configPanel.h index b4049321a46d93cf07ea203f2770e708bd80ea03..2ce74a3226b54ddfc4e064e65e29159d9d382875 100644 --- a/src/configPanel.h +++ b/src/configPanel.h @@ -12,7 +12,6 @@ /** * @brief Responsible for controlling the users Algorithms and generating the Problem. * This is a Dockwidget, so it could be moved around or locked in place. - * */ class ConfigPanel : public QDockWidget { @@ -22,17 +21,17 @@ private: std::vector<QPushButton*> lockButtons; ///< Holds Buttons used to prohibit changing the generated Problem. public: - QPushButton* runButton; ///< Used to Start and Stop the Algorithm. - QPushButton* openButton; ///< Used to Select a File via QFileDialog. - QLineEdit* fileLine; ///< Shows path and filename and allows editing it. - QPlainTextEdit* pasteBox; ///< Used to enter csv data via drag n drop or copy and paste. - QComboBox* algoBox; ///< Used to select one of the Algorithms registered with cute::registerAlgorithm(). + QPushButton* runButton; ///< Used to Start and Stop the Algorithm. + QPushButton* openButton; ///< Used to Select a File via QFileDialog. + QLineEdit* fileLine; ///< Shows path and filename and allows editing it. + QPlainTextEdit* pasteBox; ///< Used to enter csv data via drag n drop or copy and paste. + QComboBox* algoBox; ///< Used to select one of the Algorithms registered with cute::registerAlgorithm(). QSpinBox* problemSizeBox; ///< Used to adjust the number of randomly generated Points. - QComboBox* verbosityBox; ///< Used to set the verbosity, the users code should exhibit. - QStackedWidget* stackedWidget; ///< Holds the different problem generation Widgets. - QWidget* fileWidget; ///< Holds Widgets to read a Problem from a File. - QWidget* randomWidget; ///< Holds Widgets to generate random Problems. - QWidget* pasteWidget; ///< Holds Widgets to read Problem from dragged or pasted Text. + QComboBox* verbosityBox; ///< Used to set the verbosity, the users code should exhibit. + QStackedWidget* stackedWidget; ///< Holds the different problem generation Widgets. + QWidget* fileWidget; ///< Holds Widgets to read a Problem from a File. + QWidget* randomWidget; ///< Holds Widgets to generate random Problems. + QWidget* pasteWidget; ///< Holds Widgets to read Problem from dragged or pasted Text. public: explicit ConfigPanel(QWidget* parent = nullptr); @@ -47,7 +46,7 @@ public slots: signals: void previewButtonClicked(); ///< Signal emitted, when one of the Preview Buttons was clicked. - void lockButtonClicked(); ///< Signal emitted, when one of the Lock/Unlock buttons was clicked. + void lockButtonClicked(); ///< Signal emitted, when one of the Lock/Unlock buttons was clicked. }; #endif // CONFIGPANEL_H diff --git a/src/control.cpp b/src/control.cpp index 2fabc210ce0b6e5b085c27882975869dc184f6d1..f410a89861890d5cc2a6110292a41cf89722eb10 100644 --- a/src/control.cpp +++ b/src/control.cpp @@ -69,7 +69,7 @@ void Control::makeConnections() * the appropriate thread is started. * This works fine, because QApplications event queue is already created on construction, * and will only processed on execution. - * + * * @param userSetup Pointer to the User implemented Setup function. * @return int return value of QApplication::exec() or -1 if called before exiting. */ @@ -117,7 +117,7 @@ Control::Control() /** * @brief Appends a QString to MainWindows logging Area / textEdit. - * + * * @param message The message to be appended. */ void Control::logMessage(const QString message) @@ -127,7 +127,7 @@ void Control::logMessage(const QString message) /** * @brief Sets the status bars progress bar to the given percentage. Only integer values are supported. - * + * * @param percentage The Percentage 0 - 100, the processbar should show. */ void Control::setProgress(int percentage) @@ -137,9 +137,9 @@ void Control::setProgress(int percentage) /** * @brief Displays the given message in MainWindows status bar for duration_s seconds. - * + * * @param message The QString to be displayed. - * @param duration_s Time in seconds, the message should stay. 0 will have the message only disappear when overwritten. + * @param duration_s Time in seconds, the message should stay. 0 will have the message only disappear when overwritten. */ void Control::setStatusMessage(const QString message, int duration_s) { @@ -148,7 +148,7 @@ void Control::setStatusMessage(const QString message, int duration_s) /** * @brief Show or hide the given plot - * + * * @param plot The number of the plot to be manipulated. Starting from 0 for the left mose plot. * @param visible Whether to show or hide. * @todo ensure every graphing widget can hide @@ -164,7 +164,7 @@ void Control::setPlotVisible(int plot, bool visible) * which would create complicated dependencies. * @todo This will break, if other plotting widgets than QChart are added. Most easy thing would likely be, * to offer setTile() as an interface in the new Widget and adjust the this->charts vector accordingly. - * + * * @param plot The number of the plot to be manipulated. Starting from 0 for the left mose plot. * @param title The new title. */ @@ -177,7 +177,7 @@ void Control::setPlotTitle(int plot, const QString title) /** * @brief Instruct the MainWindow to change the given plots theme to one of the themes built into QtCharts * This will affect all and only QCharts. - * + * * @param themeNumber 0-7 are valid. */ void Control::setPlotTheme(int themeNumber) @@ -187,7 +187,7 @@ void Control::setPlotTheme(int themeNumber) /** * @brief Whether the legend should be shown or hidden - * + * * @param plot The number of the plot to be manipulated. Starting from 0 for the left mose plot. * @param visible Whether to show or hide. * @todo dynamic graphs... @@ -202,7 +202,7 @@ void Control::setLegendVisible(int plot, bool visible) * @brief Applies the given Titles to the axes of that plot. Hides the Title if empty. * Manipulating the axes is a bit tedious, since we can only get a list of all axes from the chart. * We then have to check it's properties to find the orientation. - * + * * @param plot The plot to be manipulated. * @param x Title for the x-axis. * @param y Title for the y-axis. @@ -230,7 +230,7 @@ void Control::setAxisTitles(int plot, QString x, QString y) /** * @brief Show or hide the given plots axes. - * + * * @param plot The plot to be manipulated. * @param x Wether the x-axis should be visible. * @param y Wether the y-axis should be visible. @@ -299,9 +299,9 @@ void Control::threadEnded() * Most importantly CuteControl is then moved to the EventQueue of the newly created thread. * That has to be done, since CuteControl was created in the main thread and thus is hooked into the main EventLoop. * This can only be done, pushing from the objects current thread, so it has to be moved back by the AlgoThread. - * When the objects are in different threads, the signals are automatically stored and processed in the receivers EventQueue. - * This way, it is thread safe. - * + * When the objects are in different threads, the signals are automatically stored and processed in the receivers + * EventQueue. This way, it is thread safe. + * * @param func The function/algorithm to be executed by the AlgoThread. */ void Control::startThread(void (*func)()) @@ -367,7 +367,7 @@ void Control::setProblemFile(QString path) /** * @brief Depending of if the problem was locked/retained or not, a new problem is generated or the old problem is used. - * + * * @return std::vector<cute::Point> The given Problem. Newly generated if problemRetained was not set. */ std::vector<cute::Point> Control::getProblem() @@ -384,12 +384,12 @@ std::vector<cute::Point> Control::getProblem() * The method depends on the current Widget of the ConfigPanel. * For random generation, floats with 2 decimal places and to a maximum number of 100 are generated. * These values are currently hard coded. The amount of numbers is read directly from the GUI. - * + * * For reading a file, the CSVParser is used. * The path is read directly from the GUI. * Only lines with at least two fields and numbers in the first two fields are read.abort * Other lines as well as data beyond the second column are simply discarted. - * + * * For interpreting pasted or drag'n'dropped data, the CSVParser is used is the same way, * with the exception of the string being handed to the parser rather than a filepath. */ @@ -457,7 +457,8 @@ void Control::generateProblem() /** * @brief Resets the left most plot (plot0) and plots the problems Points to it. - * @todo getProblem is called here, but is that problem then used by the thread, if it is not locked? UX wise, that would make sense. + * @todo getProblem is called here, but is that problem then used by the thread, if it is not locked? UX wise, that + * would make sense. */ void Control::onPreviewButton() { @@ -754,8 +755,8 @@ void Control::redrawOnTopSeries(int plot) { for (auto s : onTopSeries[plot]) { - QtCharts::QLegendMarker* legendMarker = w->charts[plot]->legend()->markers(s)[0]; - bool legendVisible = legendMarker->isVisible(); + QtCharts::QLegendMarker* legendMarker = w->charts[plot]->legend()->markers(s)[0]; + bool legendVisible = legendMarker->isVisible(); w->charts[plot]->removeSeries(s); w->charts[plot]->addSeries(s); auto axes = w->charts[plot]->axes(); diff --git a/src/csvParser.h b/src/csvParser.h index bf991eb0f09452018ba1816f9968400bad748abd..fa658997eae6653b3c05135e6eedf756951a1a70 100644 --- a/src/csvParser.h +++ b/src/csvParser.h @@ -5,48 +5,54 @@ Add a function to turn each field into a float. Add an option to skip the first /* Below is an updated version of the CSV parser class. In this revision, we have: -1. **An option to skip the first (header) line.** - You can enable this behavior by passing a boolean flag (`skipHeader`) to the constructor. If enabled, the first line is read and discarded before parsing the data. +1. **An option to skip the first (header) line.** + You can enable this behavior by passing a boolean flag (`skipHeader`) to the constructor. If enabled, the first line +is read and discarded before parsing the data. -2. **A function to convert each field into a float.** - The new member function, `getFloatRows()`, converts every field from the parsed rows to a float value using `std::stof`. If a field cannot be parsed as a float—due to it being non-numeric or out-of-range—a `NaN` value (using `std::numeric_limits<float>::quiet_NaN()`) is inserted in its place. +2. **A function to convert each field into a float.** + The new member function, `getFloatRows()`, converts every field from the parsed rows to a float value using +`std::stof`. If a field cannot be parsed as a float—due to it being non-numeric or out-of-range—a `NaN` value (using +`std::numeric_limits<float>::quiet_NaN()`) is inserted in its place. Below is the complete, self-contained code: */ #ifndef CSV_PARSER -#define CSV_PARSER + #define CSV_PARSER -#include <iostream> -#include <fstream> -#include <sstream> -#include <vector> -#include <string> -#include <limits> -#include <cmath> // For std::isnan + #include <cmath> // For std::isnan + #include <fstream> + #include <iostream> + #include <limits> + #include <sstream> + #include <string> + #include <vector> /** * @brief Kindly provided by Copilot, this can parse a .csv File into std::strings and further into floats. - * + * */ -class CSVParser { +class CSVParser +{ public: -/** - * @brief Construct a new CSVParser object - * - * @param delimiter Char that seperates the values. Default: COMMA Seperated Values - */ + /** + * @brief Construct a new CSVParser object + * + * @param delimiter Char that seperates the values. Default: COMMA Seperated Values + */ CSVParser(char delimiter = ',') : delimiter(delimiter) {} /** * @brief Opens a filestream and passes it to parseStream() to do the actual parsing. - * + * * @param filename The /path/to/file.csv to parse. * @return false if the File could not be opened. * @return true otherwise. */ - bool parseFile(const std::string& filename) { + bool parseFile(const std::string& filename) + { std::ifstream file(filename); - if (!file.is_open()) { + if (!file.is_open()) + { std::cerr << "Error opening file: " << filename << std::endl; return false; } @@ -57,42 +63,49 @@ public: /** * @brief Creates a stringstream from the input string and passes it to parseStream() to do the actual parsing. - * + * * @param rawInput The string to be parsed. */ - void parseString(const std::string& rawInput) { + void parseString(const std::string& rawInput) + { std::istringstream input(rawInput); parseStream(input); } /** * @brief Get the whole parsed CSV data as strings. - * + * * @return The parsed CSV data as a vector of rows containing strings. */ - const std::vector<std::vector<std::string>>& getRows() const { - return rows; - } + const std::vector<std::vector<std::string>>& getRows() const { return rows; } /** * @brief Converts each field from the already parsed CSV to a float. * Fields that cannot be converted are represented by NaN (Not a Number). * parseString() or parseFile() have to be called before this function so that rows is populated. - * + * * @return The parsed CSV data as a vector of rows containing floats. NaN for invalid fields. */ - std::vector<std::vector<float>> getFloatRows() const { + std::vector<std::vector<float>> getFloatRows() const + { std::vector<std::vector<float>> floatRows; - for (const auto& row : rows) { + for (const auto& row : rows) + { std::vector<float> fRow; - for (const auto& field : row) { - try { + for (const auto& field : row) + { + try + { float value = std::stof(field); fRow.push_back(value); - } catch (const std::invalid_argument&) { + } + catch (const std::invalid_argument&) + { // The field is not a valid number. fRow.push_back(std::numeric_limits<float>::quiet_NaN()); - } catch (const std::out_of_range&) { + } + catch (const std::out_of_range&) + { // The number is out of the range of a float. fRow.push_back(std::numeric_limits<float>::quiet_NaN()); } @@ -104,19 +117,20 @@ public: private: char delimiter; ///< Char that separates the values. - std::vector<std::vector<std::string>> rows; ///< Holds the strings, parsed from the file. + std::vector<std::vector<std::string>> rows; ///< Holds the strings, parsed from the file. /** * @brief Splits the stream into lines, passes them to parseLine() and stores the result in rows. - * + * * @param input The stream to be parsed. */ void parseStream(std::istream& input) { - rows.clear(); // Ensure that rows is empty before starting. + rows.clear(); // Ensure that rows is empty before starting. std::string line; // Read each subsequent line and parse it. - while (std::getline(input, line)) { + while (std::getline(input, line)) + { rows.push_back(parseLine(line)); } } @@ -124,26 +138,32 @@ private: /** * @brief Helper function that parses a single line of CSV. * It supports quoted fields and the basic escaping of quotes by doubling (""). - * + * * @param line The line to be parsed. * @return A vector of the parsed values as strings. */ - std::vector<std::string> parseLine(const std::string& line) { + std::vector<std::string> parseLine(const std::string& line) + { std::vector<std::string> tokens; - std::string token; - bool inQuotes = false; - size_t i = 0; - - while (i < line.size()) { + std::string token; + bool inQuotes = false; + size_t i = 0; + + while (i < line.size()) + { char ch = line[i]; - if (ch == '"') { - if (inQuotes && i + 1 < line.size() && line[i + 1] == '"') { + if (ch == '"') + { + if (inQuotes && i + 1 < line.size() && line[i + 1] == '"') + { // Escaped quote: insert one quote and skip the next character. token.push_back('"'); i += 2; continue; - } else { + } + else + { // Toggle the inQuotes flag. inQuotes = !inQuotes; i++; @@ -153,10 +173,13 @@ private: // If we hit the delimiter and are not inside a quoted field, // finish the current token. - if (ch == delimiter && !inQuotes) { + if (ch == delimiter && !inQuotes) + { tokens.push_back(token); token.clear(); - } else { + } + else + { token.push_back(ch); } i++; @@ -166,25 +189,29 @@ private: return tokens; } }; -#ifdef TESTING_CSV + #ifdef TESTING_CSV /** * @brief Only used to test the CSVParser. - * + * * @return 0 */ -int main() { +int main() +{ // Create an instance of CSVParser with skipHeader enabled. // Replace "example.csv" with your CSV file. - CSVParser parser("example.csv", ',', true); + CSVParser parser(','); // Parse the CSV file. - if (parser.parse()) { + if (parser.parseFile("example.csv")) + { // Display the parsed data as strings. std::cout << "Parsed CSV (strings):" << std::endl; const auto& rows = parser.getRows(); - for (const auto& row : rows) { - for (const auto& field : row) { + for (const auto& row : rows) + { + for (const auto& field : row) + { std::cout << field << " | "; } std::cout << std::endl; @@ -193,8 +220,10 @@ int main() { // Convert to floats and display. std::cout << "\nParsed CSV (floats):" << std::endl; const auto floatRows = parser.getFloatRows(); - for (const auto& row : floatRows) { - for (const auto& num : row) { + for (const auto& row : floatRows) + { + for (const auto& num : row) + { if (std::isnan(num)) std::cout << "NaN" << " | "; else @@ -202,32 +231,43 @@ int main() { } std::cout << std::endl; } - } else { + } + else + { std::cerr << "Failed to parse CSV file." << std::endl; } return 0; } -#endif //TESTING_CSV -#endif //CSV_PARSER + #endif //TESTING_CSV +#endif //CSV_PARSER /* ### Explanation -1. **Constructor & Members** - The constructor initializes the parser with the file path and the delimiter. The internal data structure (`rows`) is a vector of vectors of strings that holds the CSV data. +1. **Constructor & Members** + The constructor initializes the parser with the file path and the delimiter. The internal data structure (`rows`) is +a vector of vectors of strings that holds the CSV data. -2. **`parse()` Method** - This method opens the file and reads it line by line. For each line, it calls the helper function `parseLine` and stores the resulting vector of tokens. If the file cannot be opened, it outputs an error message. +2. **`parse()` Method** + This method opens the file and reads it line by line. For each line, it calls the helper function `parseLine` and +stores the resulting vector of tokens. If the file cannot be opened, it outputs an error message. -3. **`parseLine()` Method** - This helper method goes through the string character by character. It toggles a flag when it encounters quotes so that delimiters inside quotes are treated as literal characters. It also supports escaping a double quote by using two consecutive double quotes. +3. **`parseLine()` Method** + This helper method goes through the string character by character. It toggles a flag when it encounters quotes so +that delimiters inside quotes are treated as literal characters. It also supports escaping a double quote by using two +consecutive double quotes. -4. **`main()` Function** - In the `main()` function, an instance of `CSVParser` is created. After parsing, it prints each field separated by a bar (`|`) for clarity. This demonstration shows how to use the class; you’d replace `"example.csv"` with your actual file path. +4. **`main()` Function** + In the `main()` function, an instance of `CSVParser` is created. After parsing, it prints each field separated by a +bar (`|`) for clarity. This demonstration shows how to use the class; you’d replace `"example.csv"` with your actual +file path. -This example should serve as a solid starting point. Depending on the CSV complexity, you might need to extend the functionality to handle things like newline characters inside quoted fields or different escape mechanisms. +This example should serve as a solid starting point. Depending on the CSV complexity, you might need to extend the +functionality to handle things like newline characters inside quoted fields or different escape mechanisms. -If you’re intrigued by how CSV parsing varies with different formats or want to explore more advanced parsing techniques—perhaps using regular expressions, state machines, or even third-party libraries like [CSV for C++](https://github.com/ben-strasser/fast-cpp-csv-parser)—I’d be happy to dive deeper into those topics. Enjoy coding! +If you’re intrigued by how CSV parsing varies with different formats or want to explore more advanced parsing +techniques—perhaps using regular expressions, state machines, or even third-party libraries like [CSV for +C++](https://github.com/ben-strasser/fast-cpp-csv-parser)—I’d be happy to dive deeper into those topics. Enjoy coding! */ /* @@ -235,27 +275,36 @@ If you’re intrigued by how CSV parsing varies with different formats or want t ### How It Works -1. **Skipping the Header:** - The constructor now accepts a third parameter, `bool skipHeader`. In the `parse()` function, if `skipHeader` is true, we first perform a `std::getline` to skip over the header before processing the rest of the file. +1. **Skipping the Header:** + The constructor now accepts a third parameter, `bool skipHeader`. In the `parse()` function, if `skipHeader` is +true, we first perform a `std::getline` to skip over the header before processing the rest of the file. -2. **Converting Fields to Floats:** - The new function `getFloatRows()` iterates over each row and each field (stored as a string). It attempts to convert each field using `std::stof`. If conversion throws an exception (either because the field is not a valid number or it is out of range), a NaN is inserted instead. This way, the function always returns a consistent vector of float values for further processing. +2. **Converting Fields to Floats:** + The new function `getFloatRows()` iterates over each row and each field (stored as a string). It attempts to convert +each field using `std::stof`. If conversion throws an exception (either because the field is not a valid number or it is +out of range), a NaN is inserted instead. This way, the function always returns a consistent vector of float values for +further processing. --- ### Further Ideas -- **Enhanced Error Handling:** +- **Enhanced Error Handling:** You might want to add logging or more sophisticated error handling to report which fields failed conversion. -- **Custom Conversion:** - Sometimes CSV files include cells that are empty strings or other special tokens. You could extend the conversion process to handle these cases gracefully. +- **Custom Conversion:** + Sometimes CSV files include cells that are empty strings or other special tokens. You could extend the conversion +process to handle these cases gracefully. -- **Supporting Different Data Types:** - Consider adding more conversion functions (e.g., to int, double, or even date/time types) that can process certain columns. +- **Supporting Different Data Types:** + Consider adding more conversion functions (e.g., to int, double, or even date/time types) that can process certain +columns. -- **Template-Based Parsing:** - For a more robust solution, especially with large CSV files or varying types, you might employ template-based functions or even leverage existing CSV libraries that offer advanced features like multi-threading or schema-based parsing. +- **Template-Based Parsing:** + For a more robust solution, especially with large CSV files or varying types, you might employ template-based +functions or even leverage existing CSV libraries that offer advanced features like multi-threading or schema-based +parsing. -This code provides a good foundation—and from here, you can fine-tune it to suit the specific needs of your projects. Enjoy exploring! +This code provides a good foundation—and from here, you can fine-tune it to suit the specific needs of your projects. +Enjoy exploring! */