I\'m trying to create a code that will parse through a csv database with stock information. Currently, I have the code generated so that it will search with a keyword and pr
Here it is your solution (the most close to what you requested):
#include <iostream>
#include <fstream>
#include <string>
#include <sstream>
#include <vector>
using namespace std;
typedef std::vector<std::string> record;
std::istream&
operator>>(
std::istream& is,
record& r)
{
r.clear();
string line;
getline(is, line);
istringstream iss(line);
string field;
while (getline(iss, field, ',' ))
r.push_back(field);
return is;
}
int
main()
{
ifstream file("Stock Database.csv");
record headers, r;
if (file.good())
file >> headers;
while (file >> r)
{
for (int i = 0; i < r.size(); i++)
cout << headers[i] << ":\t" << r[i] << endl;
cout << "------------------------------" << endl;
}
return 0;
}
// EOF
The content of the "Stock Database.csv" file is:
Symbol,Name,Price,High Today,Low Today,52 Week Low
GOOG,Google Inc.,$568.77 ,$570.25 ,$560.35
AAPL,Apple Inc.,$93.28 ,$63.89 ,$99.44
Just do with the record whatever you want. The first read from the file suppose to bring you headers. Every next read fill each record with csv values.
First a comma should be parsed as whitespace. You can do this by changing the internal std::ctype<charT>
facet in the stream's locale:
struct csv_classification : std::ctype<char> {
csv_classification() : ctype(make_table()) { }
private:
static mask* make_table() {
const mask* classic = classic_table();
static std::vector<mask> v(classic, classic + table_size);
v[','] |= space;
v[' '] &= ~space;
return &v[0];
}
};
Then set the locale using:
ifs.imbue(std::locale(ifs.getloc(), new csv_classification));
Next make a manipulator that checks to see if you're at the end of the line. If you are it sets the std::ios_base::failbit
flag in the stream state. Also use internal storage to tell if the record belongs as a key or value in the map. Borrowing a bit from Dietmar...
static int row_end = std::ios_base::xalloc();
std::istream& record(std::istream& is) {
while (std::isspace(is.peek())) {
int c(is.peek());
is.ignore();
if (c == '\n') {
is.iword(row_end) = !is.iword(row_end);
is.setstate(std::ios_base::failbit);
}
}
return is;
}
Then you can do:
std::vector<std::string> keys, values;
for (std::string item;;) {
if (ifs >> record >> item)
keys.push_back(item);
else if (ifs.eof())
break;
else if (ifs.iword(row_end)) {
ifs.clear();
while (ifs >> record >> item)
values.push_back(item);
}
else
break;
}
Now we need to apply both the keys and values and print them out. We can create a new algorithm for that:
template<class Iter1, class Iter2, class Function>
void for_each_binary_range(Iter1 first1, Iter1 last1,
Iter2 first2, Iter2 last2, Function f)
{
assert(std::distance(first1, last1) <= std::distance(first2, last2));
while (first1 != last1) {
f(*first1++, *first2++);
}
}
Finally we do:
for_each_binary_range(std::begin(keys), std::end(keys),
std::begin(values), std::end(values),
[&] (std::string const& key, std::string const& value)
{
std::cout << key << ": " << value << std::endl;
}
Live Demo