Reading CSV files into a 2D vector - c++

The contents of my file look like:
Barr,3145,7
Rab,12,5513,1412,221,232,179,7121231
Bean,1,1231,219,21,337,9239,312,764,640391,4,7966346,22278,5,116364,56350
Earl,132,230,121,32,425,67
Donut,112,5525,23121,2123,65432,8790,3,4567,444
I want each line to be stored in a 2D vector (vector<vector<string>>) without the comma.
I have tried doing:
while(getline(filestream, line)){
stringstream linestream(line);
while(getline(linestream, anotherLine, ','){
oneDvector.push_back(anotherLine);
}
twoDvector.push_back(oneDvector);
oneDvector.clear();
}
But this does not seem to work. What can I do?

IMHO, you don't want to use a 2d vector or array. You want a std::vector of a class:
struct Record
{
std::string text;
std::vector<int> data;
friend std::istream& operator>>(std::istream& input, Record& r);
};
std::istream& operator>>(std::istream& input, Record& r)
{
std::string text_line;
std::getline(input, text_line);
std::istringstream text_stream(text_line);
std::getline(text_stream, r.text, ',');
int value;
char comma;
while (text_stream >> value)
{
r.data.push_back(value);
text_stream >> comma;
}
return input;
}
Note: In the above input function, a record is read by line into a string. This will make the reading of the numbers easier.
You input loop could look like:
Record r;
std::vector<Record> database;
while (file_stream >> r)
{
database.push_back(r);
}
Usually modeling a structure from an input record makes a better program. Easier to develop and debug.
The input may be simplified by replacing the 2nd occurrence and remaining commas with spaces. The first comma is used to end the text field.

Related

Reading from a .txt file into a structure in C++ language

Is there a way to import the content of my text file into a structure?
My code looks like this:
struct customerstruct {
int Customer_ID;
string name;
string adress;
};
And this is the content of my text file:
101
James
221B Baker St.
I would like to mirror the value of my text file into the structure.
Here's a suggestion:
struct customerstruct
{
int Customer_ID;
string name;
string adress;
friend std::istream& operator>>(std::istream& input, customerstruct & c);
};
std::istream& operator>>(std::istream& input, customerstruct & c)
{
input >> c.Customer_ID;
input.ignore(1000, '\n');
std::getline(input, c.name);
std::getline(input, c.adress);
return input;
}
Your input loop could look like this:
customerstruct c;
std::vector<customerstruct> database;
while (my_file >> c)
{
database.push_back(c);
}
The structure overloads operator>> to reading a single instance.
A std::vector is used to contain more than one instance read from a file.

Counting the no. of words in a csv file in c++

I am trying to count the no. of dates stores in the first line of a CSV file (Separated by commas):-
State,Region,Lat,Long,1/22/20,1/23/20,1/24/20,1/25/20,1/26/20
I have to count the no. of dates after Long(i.e. output = 5).
I have written the code to read the CSV file, which I have shown below, but, how shall I count the no. of dates after long. Would highly appreciate your help. Please feel free to ask for any other piece of information. Thanks.
char** readCSV(const char* csvFileName, int& csvLineCount)
{
ifstream fin(csvFileName);
if (!fin)
{
return nullptr;
}
csvLineCount = 0;
char line[1024];
while(fin.getline(line, 1024))
{
csvLineCount++;
};
char **lines = new char*[csvLineCount];
fin.clear();
fin.seekg(0, ios::beg);
for (int i=0; i<csvLineCount; i++)
{
fin.getline(line, 1024);
lines[i] = new char[strlen(line)+1];
strcpy(lines[i], line);
};
fin.close();
return lines;
}
This looks like you are reading a record that contains one or more subrecords.
I recommend using a Date class as well as a Record class.
class Date
{
public:
unsigned int month_num;
unsigned int day_num;
unsigned int year_num;
friend std::istream& operator>>(std::istream& input, Date& d);
};
std::istream& operator>>(std::istream& input, Date& d)
{
char forward_slash;
input >> d.month_num; input >> forward_slash;
input >> d.day_num; input >> forward_slash;
input >> d.year_num;
return input;
}
Overloading operator>> for the Date class will come in handy later.
Now the record class:
class Record
{
public:
std::string state;
std::string region;
std::string latitude;
std::string longitude;
// A container for the date subrecords
std::vector<Date> dates;
friend std::istream& operator>>(std::istream& input, Record& r);
};
std::istream& operator>>(std::istream& input, Record& r)
{
std::string text_line;
std::getline(input, text_line);
std::istringstream record_stream(text_line);
std::getline(record_stream, r.state, ',');
std::getline(record_stream, r.region, ',');
std::getline(record_stream, r.latitude, ',');
std::getline(record_stream, r.longitude, ',');
Date d;
while (record_stream >> d)
{
r.dates.push_back(d);
char comma;
record_stream >> comma;
}
return input;
}
The above function reads a text line of input, since the records are terminated by a newline. A stream is created from the text line. The stream helps in reading a variable quantity of dates.
Edit 1: Reading in the file
Your input code would look something like this:
std::vector<Record> database;
Record r;
while (fin >> r)
{
database.push_back(r);
}
Yes, you can use C-Strings and arrays, but C++ streams and std::vector simplify the code and simple code has less defects than complicated code.
Also, the std::vector and std::string classes manage dynamic memory for you. They expand as necessary. No need for checking for array overflows. They are also easier to pass. Passing arrays requires passing the capacity also (and maybe the quantity of elements in the array).

Unable to read .csv file into a class that represents a struct for a vector

I am unable to read the contents of a 30 row .csv file into the following struct:
class Entry {
public:
std::string GID;
std::string DName;
std::string Phone;
std::string POC;
std::string Item;
std::string Category;
double amount;
};
I want to be able to create a function that can read the csv file dubbed "Donations.csv" and remove the commas because I would like to create a vector possibly like this: std::vector<Entry> entries; so that I can sort the multiple items based on whatever element in the vector. So for example say I would want to sort by DName, the code would look something like this:
// sort by DName
std::sort(entries.begin(), entries.end(), [](const Entry& a, const Entry& b) {
return a.DName.compare(b.DName) < 0;
});
I was told not to use multiple vectors as this not safe and efficient however if it may help someone, this is how to read a csv file into multiple vectors:
FAIR WARNING, USING THE EOF CONTROLLER IS WRONG, REFER HERE
Albeit, this code will work just ok, I have personally tested it - numerous times on .csv files.
int main()
{
ifstream inFile; // Input file handler
ofstream outFile; // Output file handler
vector<string> GID; // Vector holding Gift ID
vector<string> DName; // Vector holding Donor Name
vector<string> Phone; // Vector holding Phone Number
vector<string> POC; // Vector holding point of contact
vector<string> Item; // Vector holding item donated
vector<string> Category; // Vector holding type of item donated
vector<double> amount; // Vector holding amount of donated items
int Input; // User selection choice from menu
// opening data file
inFile.open("Donations.csv");
if (!inFile) // If statement to check for file erroe
{
cout << "Input file did not open. Program will exit." << endl; // Does not compute!
exit(0);
}
// reading contents
string str; // temp string to hold parse comma
double temp; // temp double to hold null spaces for atoi function/strtod
getline(inFile, str, ','); // read first GID string outside of loop so that we
// won't accidentally reach the end of the file in the MIDDLE of the loop
while (!inFile.eof()) // end of file not reached
{
GID.push_back(str);
getline(inFile, str, ','); // get the contents of the line until a comma is encountered and push it into the necessary vector
DName.push_back(str);
getline(inFile, str, ',');
Phone.push_back(str);
getline(inFile, str, ',');
POC.push_back(str);
getline(inFile, str, ',');
Item.push_back(str);
getline(inFile, str, ',');
Category.push_back(str);
getline(inFile, str, '\n'); // get until the end of the line but split for parsing
// convert string to double using strtod and c_str
temp = strtod(str.c_str(), NULL);
// now push double onto vector
amount.push_back(temp);
getline(inFile, str, ','); // read in GID here instead of the top. If we are at the end
// of the file, it won't work and end-of-file flag will be set which is what we want.
}
inFile.close();
}
Add this method:
friend std::istream& operator>>(std::istream& input, Entry& e);
to your Entry structure.
Add this to your source code:
std::istream&
operator>>(std::istream& input, Entry& e)
{
input >> e.GID;
input >> e.Dname;
//.. input other members
return input;
}
Change your containers to:
std::vector<Entry> database;
Your input loop will look something like this:
Entry e;
while (my_data_file >> e)
{
database.push_back(e);
}
You may also want to search the internet for "stackoverflow c++ read file struct" for more examples.
In summary, create a struct or class to model the record.
Overload operator >> to read in a record.
Use std::vector of your struct, not a vector of each data member.
If you are serious about a database, then use one; don't rewrite your own.
Edit 1: Sorting
To make sorting easier, overload the > operator:
struct Entry
{
//...
bool operator<(const Entry& other) const
{
// add comparison functionality, example:
return GID < other.GID;
}
}
Now, you can sort the database by using:
std::sort(database.begin(), database.end());
Edit 2: Other sorting
To order the container using different criteria, you can create ordering functions and pass them to the std::sort function:
bool Order_By_Dname(const Entry& e1, const Entry& e2)
{
return e1.Dname < e2.Dname;
}
Your sort becomes:
std::sort(database.begin(), database.end(), Order_By_Dname);

Implementing an AVL tree using values from a text file

I am attempting to implement an AVL Tree by using key/value pairs from a text file. For example, (0003,09390,Vegas) would be one such key value pair, with 0003 being the key, and 09390, Vegas being the value. I am confused on how to extract each individual item. I would want 0003, 09390, and Vegas stored as each individual variables in a struct.
I recommend using a structure to hold the values:
struct Values
{
std::string value; // such as 09390
std::string city; // such as "Vegas"
};
Next, overload the extraction operator:
struct Values
{
std::string value; // such as 09390
std::string city; // such as "Vegas"
friend std::istream& operator>>(std::istream& inp, Values& v);
};
std::istream& operator>>(std::istream& inp, Values& v)
{
inp >> v.value;
// insert code to skip over delimiter.
inp >> v.city;
return inp;
}
Your input loop could look like this:
std::string key;
std::string delimiter;
Values v;
AVL tree;
//...
while (input_file >> key >> delimiter >> v)
{
tree[key] = v;
}
Note: the above input expression only works when values are not space or tab separated.
For more information about reading from a file, search StackOverflow for:
"c++ read file struct"
"c++ read file csv"
The easiest approach is probably to use ignore() in combination with std::getline():
std::string key, code, city;
if (in.ignore(std::numeric_limits<std::streamsize>::max(), '(')
&& std::getline(in, key, ',')
&& std::getline(in, code, ',')
&& std::getline(in, city, ')')) {
// ...
}

Reading in file with delimiter

How do I read in lines from a file and assign specific segments of that line to the information in structs? And how can I stop at a blank line, then continue again until end of file is reached?
Background: I am building a program that will take an input file, read in information, and use double hashing for that information to be put in the correct index of the hashtable.
Suppose I have the struct:
struct Data
{
string city;
string state;
string zipCode;
};
But the lines in the file are in the following format:
20
85086,Phoenix,Arizona
56065,Minneapolis,Minnesota
85281
56065
Sorry but I still cannot seem to figure this out. I am having a really hard time reading in the file. The first line is basically the size of the hash table to be constructed. The next blank line should be ignored. Then the next two lines are information that should go into the struct and be hashed into the hash table. Then another blank line should be ignored. And finally, the last two lines are input that need to be matched to see if they exist in the hash table or not. So in this case, 85281 is not found. While 56065 is found.
As the other two answers point out you have to use std::getline, but this is how I would do it:
if (std::getline(is, zipcode, ',') &&
std::getline(is, city, ',') &&
std::getline(is, state))
{
d.zipCode = std::stoi(zipcode);
}
The only real change I made is that I encased the extractions within an if statement so you can check if these reads succeeded. Moreover, in order for this to be done easily (you wouldn't want to type the above out for every Data object), you can put this inside a function.
You can overload the >> operator for the Data class like so:
std::istream& operator>>(std::istream& is, Data& d)
{
std::string zipcode;
if (std::getline(is, zipcode, ',') &&
std::getline(is, d.city, ',') &&
std::getline(is, d.state))
{
d.zipCode = std::stoi(zipcode);
}
return is;
}
Now it becomes as simple as doing:
Data d;
if (std::cin >> d)
{
std::cout << "Yes! It worked!";
}
You can use a getline function from <string> like this:
string str; // This will store your tokens
ifstream file("data.txt");
while (getline(file, str, ',') // You can have a different delimiter
{
// Process your data
}
You can also use stringstream:
stringstream ss(line); // Line is from your input data file
while (ss >> str) // str is to store your token
{
// Process your data here
}
It's just a hint. Hope it helps you.
All you need is function std::getline
For example
std::string s;
std::getline( YourFileStream, s, ',' );
To convert a string to int you can use function std::stoi
Or you can read a whole line and then use std::istringstream to extract each data with the same function std::getline. For example
Data d = {};
std::string line;
std::getline( YourFileStream, line );
std::istringstream is( line );
std::string zipCode;
std::getline( is, zipCode, ',' );
d.zipCode = std::stoi( zipCode );
std::getline( is, d.city, ',' );
std::getline( is, d.state, ',' );