Nobody will be interested in the following, since this answer is given 5 days after the question has been asked.
Additionally, the OP is new to C++ wnd will not understand it. But since this questions is tagged with C++, and the other answers are very C-Style-ish, I want to show here a more-modern C++ solution with an object oriented approach.
And, I see everywhere the abuse of the std::getline
for splitting strings, which is unbelievable, since a dedicated functions exist for splitting strings. The intended use for std::getline
is to "get a line" from a stream. As it name suggests. People fiddle around with this function and searching for delimiters and so on, but in my very humble opinion, we should not do this.
Since roundabout 10 years we have a dedicated, special C++ functionality for splitting strings into tokens, explicitely designed for this purpose. The std::sregex_token_iterator
. And because we have such a dedicated function, we should simply use it.
The idea behin it is the iterator concept. In C++ we have many containers and always iterators, to iterate over the similar elements in these containers. And a string, with similar elements (tokens), separated by a delimiter, can also be seen as such a container. And with the std::sregex:token_iterator
, we can iterate over the elements/tokens/substrings of the string, splitting it up effectively.
This iterator is very powerfull and you can do really much much more fancy stuff with it. But that is too much for here. Important is that splitting up a string into tokens is a one line. For example a variable definition using a range constructor for iterating over the tokens. See for example:
// The delimiter
const std::regex delimiter{ "," };
// Test data
std::string csvData("d1,d2,d3,d4,d5");
// Split the string
std::vector<std::string> tokens(std::sregex_token_iterator(csvData.begin(), csvData.end(), delimiter, -1), {});
Ant since is an iterator, you can use it with many algorithms. You should really stduy and use it. And for all those people, with big concerns about efficiency, please note, that in 90% of the cases only a few lines will be parsed. No problem.
So, next. We have an object oriented language. So lets use it and define a class for your data, with the data mebers mentioned by you, and additionally functions or operators, to operate on this data members. Only the class should know about its internals. Outside, we want to use it, without knowing the implementation.
In the example below the extractor and iserter will be overwritten, to enable stream compliant IO operations. In the extractor we will use also a regex
feature and will check exactly, if the input string matches our expectations. For this we use a std::regex
which exactly defines the data pattern in the CSV string. Then, if we found a match, we can use the data. So, this is not only splitting up the string, but also input validation. The function for this is: std::regex_match
.
And, if you look in main
, reading and parsing all CSV data is a one-liner. Same for the data output.
Please see:
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <iterator>
#include <algorithm>
#include <regex>
const std::regex re{R"(^\s*?(\b\w+)\s*?,\s*?(\d+\.\d+)\s*?,\s*?(\d+)\s*?$)"};
// Proxy class for easy input and output
struct MyData {
std::string name{};
double price{};
long quantity{};
// Overwrite extractor operator for easier input
friend std::istream& operator >> (std::istream& is, MyData& md) {
// Read a complete line from your CSV file
if (std::string line{}; std::getline(is, line)) {
// Check, if the input string matches to pour expectation, so split and validate
if (std::smatch match{}; regex_match(line, match, re)) {
// Match is perfect. Assign the values to our internal data members
md.name = match[1]; md.price = std::stod(match[2]); md.quantity = std::stol(match[3]);
}
else // No match. Set everything to default
md = {};
}
return is;
}
// Simple output of our data members
friend std::ostream& operator << (std::ostream& os, const MyData& md) {
return os << "Name: " << md.name << "\t\tPrice: " << md.price << "\tQuantity: " << md.quantity;
}
};
int main() {
// Open the csv File and check, if it could be opened and everything is ok
if (std::ifstream csvStream("r:\\antiquelist.txt"); csvStream) {
// Read and parse the complete source file
std::vector myData(std::istream_iterator<MyData>(csvStream), {});
// Show complete result to user
std::copy(myData.begin(), myData.end(), std::ostream_iterator<MyData>(std::cout, "\n"));
}
else {
std::cerr << "\n*** Error: Could not open input file\n";
}
return 0;
}
Please look here, to get a better understanding about regex
What a pity that nobody will read this . . .