Reading separated columns in a CSV file in C++ using getLine - c++

I have a program that can successfully read a CSV file. The said CSV file is a list separated by 3 columns. And each column has a comma in between each line. For instance
line 1 --- artist, genre, song
line 2 --- Michael jackson, Pop, thriller
and so on. What I want my program to do is read the first line and to take artist, genre, and song, print those out to the user as options. For instance
"Choose an Option"
1. Artist
2. Genre
3. Song
and then when they select an option it then shows them either all the artists, songs, or genres in the CSV file.
So far I have my program reading the CSV and putting each line in a vector. Here is my code so far ...
#include <iostream>
#include <sstream>
#include <string>
#include <fstream>
#include <vector>
using namespace std;
int main()
{
ifstream infile("music_list.csv");
string line = "";
vector<string> all_words;
cout << "Hello";
while (getline(infile, line))
{
stringstream strstr(line);
string word = "";
while (getline(strstr,word, ','))
{
all_words.push_back(word);
}
for (unsigned i = 0; i < all_words.size(); i++)
{
cout << all_words.at(i)<< "\n";
}
}
system("pause");
return 0;
}
I'm just having trouble figuring out how to have it read the first line, separate each string in the first line that is already separated by a comma and have that then outputted to the user as an option. So in essence I can change artist, genre, song to something like appetizers, dish, drinks in the CSV file.

First you have to read the csv file and store the lines in a vector, You can use this function to do that.
vector<string> readCsvFileContent(const string file)
{
vector<string> buffer;
ifstream configFile;
configFile.exceptions(ifstream::badbit);
try
{
configFile.open(file.c_str(),ifstream::in);
if(configFile.is_open())
{
string line;
while (getline(configFile,line))
{
buffer.push_back(line);
}
configFile.close();
}
}
catch (ifstream::failure e){
throw e;
}
return buffer;
}
Then split the each line entry to 2D vector. for that you can use this function
vector<vector<string>> processCsvList(vector<string> csvList)
{
#define SINGER_CONFIG_COUNT 3 //number of comma separated data suppose to be in one line.
#define DELIMITED_CHAR ","
#define EMPTY_STRING "";
vector<vector<string>> tempList;
string configCell ="";
for(vector<string>::iterator it = csvList.begin(); it != csvList.end(); ++it)
{
if(*it == EMPTY_STRING)
{
continue;
}
stringstream configLine(*it);
vector<string> tempDevice;
for(int i=0; i<SINGER_CONFIG_COUNT; i++)
{
if(getline(configLine,configCell,DELIMITED_CHAR))
{
tempDevice.push_back(configCell);
}else
{
tempDevice.push_back(EMPTY_STRING);
}
}
tempList.push_back(tempDevice);
}
return tempList;
}
I haven't try to compile any of these function, because I do not have environment to do so here. But I think this will help to to think about the direction.
now your data in a 2D vector like excell sheet. So you can access by the index of the inner vector.

You could create a std::map<string,vector<string>> so that you could then store each column of the CSV file from line 2 onwards in the map as a vector of strings, keyed by the text that appears in the column on line 1 of the CSV file.
Then you could present the names of the fields by iterating through the keys of the std::map, which are conveniently stored in alphabetical order.
The task of reading in the first line of the CSV file could be performed by the code you already have, or something more sophisticated that takes into account the quoted fields, etc. that are found in fully-featured CSV files.

Related

How to read only one column from a csv file in c++?

Here I have an excel file whose first column has ID's i.e:
ID
12
32
45
12
..
There are other columns as well but I only want to read the data present in first column i.e. ID.
Here is my code which throws exception. I don't know why?
#include <iostream>
#include <string>
#include <fstream>
#include <sstream>
#include <vector>
#include <algorithm>
#include<string>
#include<cstdlib>
//std::find
#include<cstring>
using namespace std;
int main()
{
ifstream fin("1.csv");
string line;
int rowCount = 0;
int rowIdx = 0; //keep track of inserted rows
//count the total nb of lines in your file
while (getline(fin, line)) {
rowCount++;
}
//this will be your table. A row is represented by data[row_number].
//If you want to access the name of the column #47, you would
//cout << data[0][46]. 0 being the first row(assuming headers)
//and 46 is the 47 column.
//But first you have to input the data. See below.
std::vector<std::vector<std::string>> data;
fin.clear(); //remove failbit (ie: continue using fin.)
fin.seekg(fin.beg); //rewind stream to start
while (getline(fin, line)) //for every line in input file
{
stringstream ss(line); //copy line to stringstream
string value;
while (getline(ss, value, ',')) { //for every value in that stream (ie: every cell on that row)
data[rowIdx].push_back(value);//add that value at the end of the current row in our table
}
rowIdx++; //increment row number before reading in next line
}
fin.close();
//Now you can choose to access the data however you like.
//If you want to printout only column 47...
int colNum;
string colName = "ID";
//1.Find the index of column name "computer science" on the first row, using iterator
//note: if "it == data[0].end()", it means that that column name was not found
vector<string>::iterator it = find(data[0].begin(), data[0].end(), colName);
//calulate its index (ie: column number integer)
colNum = std::distance(data[0].begin(), it);
//2. Print the column with the header "computer science"
for (int row = 0; row < rowCount; row++)
{
cout << data[row][colNum] << "\t"; //print every value in column 47 only
}
cout << endl;
return 0;
}
Kindly help me to fix the issue. I want to display only first column which contain ID's.
Here's a simplified version of the above code. It gets rid of the 2D vector, and only reads the first column.
std::vector<std::string> data;
ifstream fin("1.csv");
string line;
while (getline(fin, line)) //for every line in input file
{
stringstream ss(line); //copy line to stringstream
string value;
if (getline(ss, value, ',')) {
data.push_back(value);
}
}
EDIT
How to display the data
for (size_t i = 0; i < data.size(); ++i)
cout << data[i] << '\n';
The problem:
std::vector<std::vector<std::string>> data;
Allocates a vector of vectors. Both dimensions are currently set to 0.
data[rowIdx].push_back(value);
pushes into and potentially resizes the inner vector, but the outer vector remains size 0. No value of rowIdx is valid. The naive solution is to use rowCount to size the outer vector, but it turns out that's a waste. You can assemble whole rows and then push_back the row into data, but even this is a waste since only one column is needed.
One of the beauties of vector is you don't have to know the number of rows. Your make a row vector, push the columns into it, then push the row into data. when you hit the end of he file, you're done reading. No need to read the file twice, but you probably trade off a bit of wasted storage on data's last self-resize.
#include <iostream>
#include <string>
#include <fstream>
#include <sstream>
#include <vector>
// reduced includes to minimum needed for this example
// removed using namespace std; to reduce odds of a naming collision
int main() {
std::ifstream fin("1.csv"); // Relative path, so watch out for problems
// with working directory
std::string line;
std::vector<std::string> data; // only need one column? Only need one
// dimension
if (fin.is_open())
{
while (std::getline(fin, line)) //for every line in input file
{
std::stringstream ss(line);
std::string value;
if (std::getline(ss, value, ','))
{
// only take the first column If you need, say, the third
// column read and discard the first two columns
// and store the third
data.push_back(value); // vector sizes itself with push_back,
// so there is no need to count the rows
}
}
}
else
{
std::cerr << "Cannot open file\n";
return -1;
}
// use the column of data here
}

Reading a text file and storing data into multiple arrays C++

I am trying to read a database file (as txt) where I want to skip empty lines and skip the column header line within the file and store each record as an array. I would like to take stop_id and find the stop_name appropriately. i.e.
If i say give me stop 17, the program will get "Jackson & Kolmar".
The file format is as follows:
17,17,"Jackson & Kolmar","Jackson & Kolmar, Eastbound, Southeast Corner",41.87685748,-87.73934698,0,,1
18,18,"Jackson & Kilbourn","Jackson & Kilbourn, Eastbound, Southeast Corner",41.87688572,-87.73761421,0,,1
19,19,"Jackson & Kostner","Jackson & Kostner, Eastbound, Southeast Corner",41.87691497,-87.73515882,0,,1
So far I am able to get the stop_id values but now I want to get the stop name values and am fairly new to c++ string manipulation
mycode.cpp
#include <iostream>
#include <fstream>
#include <string>
#include <sstream>
using namespace std;
int main()
{
string filename;
filename = "test.txt";
string data;
ifstream infile(filename.c_str());
while(!infile.eof())
{
getline(infile,line);
int comma = line.find(",");
data = line.substr(0,comma);
cout << "Line " << count << " "<< "is "<< data << endl;
count++;
}
infile.close();
string sent = "i,am,the,champion";
return 0;
}
You can use string::find 3 times to search for the third occurrence of the comma, and you must store the positions of the last 2 occurrences found in line, then use them as input data with string::substr and get the searched text:
std::string line ("17,17,\"Jackson & Kolmar\",\"Jackson & Kolmar, Eastbound, Southeast Corner\",41.87685748,-87.73934698,0,,1");
std::size_t found=0, foundBack;
int i;
for(i=0;i<3 && found!=std::string::npos;i++){
foundBack = found;
found=line.find(",",found+1);
}
std::cout << line.substr(foundBack+1,found-foundBack-1) << std::endl;
You can read the whole line of the file intoa string and then use stringstream to give you each piece one at a time up until and exluding the commas. Then you can fill up your arrays. I am assuming that you wanted each line in it's own array and that you wanted unlimited arrays. The best way to do that is to have an array of arrays.
std::string Line;
std::array<std::array<string>> Data;
while (std::getline(infile, Line))
{
std::stringstream ss;
ss << Line;
Data.push_back(std::vector<std::string>);
std::string Temp;
while (std::getline(ss, Temp, ','))
{
Data[Data.size() - 1].push_back(Temp);
}
}
This way you will have a vector, full of vectors, each of which conatining strings of all your data in that line. To access the strings as numbers, you can use std::stoi(std::string) which converts a string to an integer.

C++ 2D Vector from a csv file

I have this .csv file. It has 6 columns (separated by comma) and many rows. I want to make a 3D vector such that each row is in a vector and all rows are inside the 3D vector.
My code so far:
// open a file in read mode.
ifstream csv("test.csv");
string line;
vector <string> lineV;
if (csv.is_open()) {
for (int i = 0; csv.good(); i++)
{
getline(csv, line, ',');
lineV.push_back(line);
}
}
else {
cout << "Unable to open file";
}
// close the opened file.
csv.close();
Now, I have this vector lineV which has all the values in the .csv file separated by commas, like this:
row1column1
row1column2
row1column3
row1column4
row1column5
row1column6
row2column1
...and so on
I thought of trying to iterate through the vector and passing the values to a new vector after every 6th value, but I don't know how to start.
Alternatively, if I change getline(csv, line, ','); to getline(csv, line);, I can get it row by row:
row1column1,row1column2,row1column3,row1column4,row1column5,row1column6
row2column1,row2column2,row2column3,row2column4,row2column5,row2column6
...and so on
But I will still have to split each row to its own vector...
It may be easier for you to read the csv file by lines first (rows), then use a std::istringstream to extract comma separated entities from each line.
// open a file in read mode.
std::ifstream csv("test.csv");
std::string line;
std::vector <std::vector<std::string>> items;
if (csv.is_open()) {
for (std::string row_line; std::getline(csv, row_line);)
{
items.emplace_back();
std::istringstream row_stream(row_line);
for(std::string column; std::getline(row_stream, column, ',');)
items.back().push_back(column);
}
}
else {
cout << "Unable to open file";
}

Reading data in from a .csv into usable format using C++

I would like to be able to read the data that I have into C++ and then start to do things to manipulate it. I am quite new but have a tiny bit of basic knowledge. The most obvious way of doing this that strikes me (and maybe this comes from using excel previously) would be to read the data into a 2d array. This is the code that I have so far.
#include <iostream>
#include <fstream>
#include <algorithm>
#include <string>
#include <sstream>
using namespace std;
string C_J;
int main()
{
float data[1000000][10];
ifstream C_J_input;
C_J_input.open("/Users/RT/B/CJ.csv");
if (!C_J_input) return -1;
for(int row = 0; row <1000000; row++)
{
string line;
getline(C_J_input, C_J, '?');
if ( !C_J_input.good() )
break;
stringstream iss(line);
for(int col = 0; col < 10; col++)
{
string val;
getline(iss, val, ',');
if (!iss.good() )
break;
stringstream converter(val);
converter >> data[row][col];
}
}
cout << data;
return 0;
}
Once I have the data read in I would like to be able to read through it line by line and then pull analyse it, looking for certain things however I think that could probably be the topic of another thread, once I have the data read in.
Just let me know if this is a bad question in any way and I will try to add anything more that might make it better.
Thanks!
as request of the asker, this is how you would load it into a string, then split into lines, and then further split into elements:
#include <iostream>
#include <string>
#include <fstream>
#include <vector>
#include <sstream>
//This takes a string and splits it with a delimiter and returns a vector of strings
std::vector<std::string> &SplitString(const std::string &s, char delim, std::vector<std::string> &elems)
{
std::stringstream ss(s);
std::string item;
while (std::getline(ss, item, delim))
{
elems.push_back(item);
}
return elems;
}
int main(int argc, char* argv[])
{
//load the file with ifstream
std::ifstream t("test.csv");
if (!t)
{
std::cout << "Unknown File" << std::endl;
return 1;
}
//this is just a block of code designed to load the whole file into one string
std::string str;
//this sets the read position to the end
t.seekg(0, std::ios::end);
str.reserve(t.tellg());//this gives the string enough memory to allocate up the the read position of the file (which is the end)
t.seekg(0, std::ios::beg);//this sets the read position back to the beginning to start reading it
//this takes the everything in the stream (the file data) and loads it into the string.
//istreambuf_iterator is used to loop through the contents of the stream (t), and in this case go up to the end.
str.assign((std::istreambuf_iterator<char>(t)),
std::istreambuf_iterator<char>());
//if (sizeof(rawData) != *rawSize)
// return false;
//if the file has size (is not empty) then analyze
if (str.length() > 0)
{
//the file is loaded
//split by delimeter(which is the newline character)
std::vector<std::string> lines;//this holds a string for each line in the file
SplitString(str, '\n', lines);
//each element in the vector holds a vector of of elements(strings between commas)
std::vector<std::vector<std::string> > LineElements;
//for each line
for (auto it : lines)
{
//this is a vector of elements in this line
std::vector<std::string> elementsInLine;
//split with the comma, this would seperate "one,two,three" into {"one","two","three"}
SplitString(it, ',', elementsInLine);
//take the elements in this line, and add it to the line-element vector
LineElements.push_back(elementsInLine);
}
//this displays each element in an organized fashion
//for each line
for (auto it : LineElements)
{
//for each element IN that line
for (auto i : it)
{
//if it is not the last element in the line, then insert comma
if (i != it.back())
std::cout << i << ',';
else
std::cout << i;//last element does not get a trailing comma
}
//the end of the line
std::cout << '\n';
}
}
else
{
std::cout << "File Is empty" << std::endl;
return 1;
}
system("PAUSE");
return 0;
}
On second glance, I've noticed few obvious issues which will slow your progress greatly, so I'll drop them here:
1) you are using two disconnected variables for reading the lines:
C_J - which receives data from getline function
line - which is used as the source of stringstream
I'm pretty sure that the C_J is completely unnecessary. I think you wanted to simply do
getline(C_J_input, line, ...) // so that the textline read will fly to the LINE var
// ...and later
stringstream iss(line); // no change
or, alternatively:
getline(C_J_input, C_J, ...) // no change
// ...and later
stringstream iss(C_J); // so that ISS will read the textline we've just read
elsewise, the stringstream will never see what getline has read form the file - getline writes the data to different place (C_J) than the stringstream looks at (line).
2) another tiny bit is that you are feeding a '?' into getline() as the line separator. CSVs usually use a 'newline' character to separate the data lines. Of course, your input file may use '?' - I dont know. But if you wanted to use a newline instead then omit the parameter at all, getline will use default newline character matching your OS, and this will probably be just OK.
3) your array of float is, um huge. Consider using list instead. It will nicely grow as you read rows. You can even nest them, so list<list<float>> is also very usable. I'd actually probably use list<vector<float>> as the number of columns is constant though. Using a preallocated huge array is not a good idea, as there always be a file with one-line-too-much you know and ka-boom.
4) your code contains a just-as-huge loop that iterates a constant number of times. A loop itself is ok, but the linecount will vary. You actually don't need to count the lines. Especially if you use list<> to store the values. Just like you;ve checked if the file is properly open if(!C_J_input), you may also check if you have reached End-Of-File:
if(C_J_input.eof())
; // will fire ONLY if you are at the end of the file.
see here for an example
uh.. well, that's for start. Goodluck!

Best method for reading row and col of txt file

Sorry, im a C++ noob! I have looked around for a solution but cant seem to find one that best suits my need. I am tring to read the width(max amount of char in line) and height (max amount of lines per file) of a txt file. Planning on using varaibles to help make dynamic sized txt files/levels.
I have had fixed width and height working previous to this!
TXT FILE: Simple output of chars for room layout (space=floor, #=wall, X=door):
########
# #
# X
# #
########
PROBLEM: Thought this to be a simple problem, but it only reads 2 of each variable(hNum,wNum)before breaking loop and program cant continue.
-What am i doing wrong?
-Should i be using seekg or different loop somehow?
-Do i need to alter my vector to 2D vector?
-Which is the best method for achieving this?
Room.cpp
//LOAD CURRENT ROOM FROM FILE
ss << RoomNo;
string str = ss.str();
string fname = ("Room");
fname.append(str);
fname.append(".txt");
infile.open(fname);
infile.clear();
infile.seekg(0);
if(infile.is_open())
{
// Sets width and height depndant on txt file size
string line;
//NOT WORKING!
while( !infile.eof())
{
hNum++;
getline ( infile, line);
wNum += line.length();
break;
}
height=hNum;
width=wNum;
//END
// Loop to end of file- to get you all the lines from txt file.
while(!infile.eof())
{
int i;
for(int row = 0; row < width; row++)
{
infile.getline(RoomFile, 256);
i = 0;
for(int col = 0; col < height; col++)
{
data.push_back(RoomFile[i]);
i++;
}
}
}
}
else
{
cout << "ERROR: infile not open" << endl;
}
infile.close();
UPDATE
This is what i got, tryin to do what Sky suggested...but cudnt work it all out. Then steped thro and thought the loop wasnt active so altered the argument. Now getting runtime error!
PROBLEM: Expression: vector subscript out of range!
Suggestions anyone?
string line;
while(getline(infile,line))
{
getline(infile, line);
tempVector.push_back(line);
}
width=line.length();
height=tempVector.size();
A possible way that can work would be to create a vector of strings and read the entire file, with each line a string in the vector.
Rough example:
#include <vector>
#include <string>
#include <fstream>
/* ... */
vector<string> content;
string buffer;
while(!infile.eof())
{
getline(infile, &string, '/n');
content.push_back(string);
}
width = findMaxLengthOfStrings(content);
height = content.size();
You are reading each line of the file as a separate string. The strings are pushed onto the vector. You can then easily find which string in the vector is longest, by iterating through the vector, using size(). The length in lines of the file is obtained with size() on the vector itself.
Also, posting just the relevant parts of the code, the I/O function, would have helped. Just saying ;) A small screen size and such.