Reputation: 887
So I have a CSV with 2 columns - One for A song and one for the artist of that song. For example
The Pretender,Foo Fighters
In Bloom,Nivana
Champagne Supernova,Oasis
Starlight,Muse
Will We Talk?,Sam Fender
I am converting a python program that I wrote a while back into c++. In that program it uses the csv library to read the csv, and choose a random row from that. It then splits it into two columns and then stores them in variables, answer being the song name and title being the song title:
#select a random row and store it in a list
randomrow = random.choice(list(csvreader))
#print(randomrow)
answer = randomrow[0]
answer = answer.strip()
#print(answer)
track = randomrow[0]
#print(track)
#split track into two columns - title and artist
title = track.split()
I have tried techniques from this thread: here
such as:
#include <sstream>
#include <string>
#include <fstream>
#include <iostream>
std::ifstream songsCsv("Songs.csv");
std::string line;
while (std::getline(songsCsv line))
{
std::istringstream iss(line);
std::string song, artist;
if (!(iss >> song >> artist)) { break; }
cout << song;
cout << artist;
}
however, nothing was outputted here. If anyone had any ideas on how to do this tey would be much appreciated.
Upvotes: 1
Views: 699
Reputation: 1123
According to your CSV file, you could have a solution like this :
#include <sstream>
#include <string>
#include <fstream>
#include <iostream>
#include <vector>
int main()
{
std::vector<std::pair<std::string, std::string>> result;
std::string line;
std::string first, second;
std::ifstream ifs("Songs.csv");
while (std::getline(ifs, line))
{
std::istringstream iss(line);
std::getline(iss, first, ',');
std::getline(iss, second, ',');
result.push_back(std::make_pair(first, second));
}
for (std::vector<std::pair<std::string, std::string>>::const_iterator it = result.cbegin(); it != result.cend(); ++it)
{
std::cout << it->first << " : " << it->second << '\n';
}
return 0;
}
A safer solution, if you have more than 2 columns (more than one song), would be to iterate over the line retrieved from your CSV file and append it to a std::map<std::string, std::vector<std::string>>
where the key is the name of the band.
Here is another solution which uses an std::map
where the first member is the name of the band. This one is preferred if you have more than two columns (more than one song) and if the last column of your CSV file is the name of the band.
For instance, you could have the following CSV file :
The Pretender,AAAA,Foo Fighters In Bloom,BBBB,Nivana Champagne Supernova,CCCC,Oasis Starlight,DDDD,Muse Will We Talk?,EEEE,FFFF,Sam Fender
#include <sstream>
#include <string>
#include <fstream>
#include <iostream>
#include <map>
#include <vector>
int main()
{
std::map<std::string, std::vector<std::string>> result;
std::string line;
std::ifstream ifs("Songs.csv");
int i = 0;
while (std::getline(ifs, line))
{
std::istringstream iss(line);
std::vector<std::string> temp;
for (std::string token; std::getline(iss, token, ','); )
{
temp.push_back(token);
}
result[temp[temp.size() - 1]].insert(result[temp[temp.size() - 1]].end(), std::make_move_iterator(temp.begin()), std::make_move_iterator(temp.end() - 1));
}
for (std::map<std::string, std::vector<std::string>>::const_iterator it = result.cbegin(); it != result.cend(); ++it)
{
std::cout << it->first << " : ";
for (std::vector<std::string>::const_iterator vit = it->second.cbegin(); vit != it->second.cend(); ++vit)
{
std::cout << *vit << ' ';
}
std::cout << '\n';
}
}
Upvotes: 3
Reputation: 1856
The problem is that >>
only splits words around whitespace (newline, tabs, spaces).
Inspired by this question you can change the delimiter to a comma like this:
#include <locale>
#include <iostream>
struct comma_is_space : std::ctype<char> {
comma_is_space() : std::ctype<char>(get_table()) {}
static mask const* get_table()
{
static mask rc[table_size];
rc[','] = std::ctype_base::space; // split on commas
return &rc[0];
}
};
{
...
std::istringstream iss(line);
iss.imbue(std::locale(iss.getloc(), new comma_is_space()));
...
}
Upvotes: 0