item,price,qty,ordno,trdno
abc,54,2,123,32
xyz,34,2,345,21
item: string (char[])
price,qty (int)
ordno (long long)
trdno (int)
Make a structure for above mentioned fields
Make a vector (array, or any other container type) to hold multiple instances of this structure
1: Read file
2: read line, split values
3: initialize above mentioned structure object
4: add this object of structure to container
5: when whole file is read.. iterate over the container and print each elements values (serialno, orderno, tradeno, price, qty, item)
I tried this and it is not working-
#include<bits/stdc++.h>
using namespace std;
struct item {
string name;
double price;
int quantity;
int order_no;
int trd_no;
};
int main()
{
int n;cin>>n;
string str, T;
ifstream read("input.txt");
while(getline(read,str))
{
cout<<str<<endl;
}
stringstream X(str); // X is an object of stringstream that references the S string
cout<<endl;
while (getline(X, T, ','))
{
cout << T << endl; // print split string
}
read.close();
return 0;
}
For the code that you are showing, you misplaced just one '}' after the first while. So the code will read in the first while-loop all lines of the file and is then empty. And, then you try to split the lines. This can of course not work.
If you move the closing bracket to in front of read.close(); then your code will work. Like this:
#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
using namespace std;
struct item {
string name;
double price;
int quantity;
int order_no;
int trd_no;
};
int main()
{
int n; cin >> n;
string str, T;
ifstream read("input.txt");
while (getline(read, str))
{
cout << str << endl;
stringstream X(str); // X is an object of stringstream that references the S string
cout << endl;
while (getline(X, T, ','))
{
cout << T << endl; // print split string
}
}
read.close();
return 0;
}
Caveat: this will not work, if the source file contains the header row! You can read this and throw it away, if needed.
If we follow the instruction of your homework, line by line, and assume that the first line contains header rows, then we would do like the following.
#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
#include <vector>
struct Item {
std::string name;
double price;
int quantity;
long long order_no;
int trd_no;
};
int main()
{
std::vector<Item> items;
std::string str, T;
std::ifstream read("input.txt");
// Read first line and ignore it
std::getline(read, str);
while (std::getline(read, str))
{
std::istringstream X(str);
Item tempItem;
getline(X, T, ',');
tempItem.name = T;
getline(X, T, ',');
tempItem.price = std::stod(T);
getline(X, T, ',');
tempItem.quantity = std::stoi(T);
getline(X, T, ',');
tempItem.order_no = std::stoll(T);
getline(X, T, ',');
tempItem.trd_no = std::stoi(T);
items.push_back(tempItem);
}
read.close();
for (const Item& item : items)
std::cout << item.name << ' ' << item.price << ' ' << item.quantity << ' '
<< item.order_no << ' ' << item.trd_no << '\n';
}
And, with a little bit more advanced C++, where we especially keep data and methods in a class, we could do the following:
#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
#include <vector>
#include <iomanip>
#include <algorithm>
#include <iterator>
// The item
struct Item {
std::string name{};
double price{};
int quantity{};
long long order_no{};
int trd_no{};
// Overwrite extraction operator for easier stream IO
friend std::istream& operator >> (std::istream& is, Item& i) {
char c;
return std::getline(is >> std::ws, i.name, ',') >> i.price >> c >> i.quantity >> c >> i.order_no >> c >> i.trd_no;
}
// Overwrite inserter for easier output
friend std::ostream& operator << (std::ostream& os, const Item& i) {
return os << "Name: " << i.name << "\tPrice: " << i.price << "\tQuantity: " << i.quantity << "\tOrder no: " << i.order_no << "\tTRD no: " << i.trd_no;
}
};
// The Container
struct Data {
std::vector<Item> items{};
// Overwrite extraction operator for easier stream IO
friend std::istream& operator >> (std::istream& is, Data& d) {
// Read header line and ignore it
std::string dummy; std::getline(is, dummy);
// Delete potential old data
d.items.clear();
// Read all new data from file
std::copy(std::istream_iterator<Item>(is), {}, std::back_inserter(d.items));
return is;
}
// Overwrite inserter for easier output
friend std::ostream& operator << (std::ostream& os, const Data& d) {
std::copy(d.items.begin(), d.items.end(), std::ostream_iterator<Item>(os, "\n"));
return os;
}
};
int main()
{
// Open file and check, if it could be opened
if (std::ifstream sourceFileStream("input.txt"); sourceFileStream) {
// Define container
Data data{};
// Read and parse complete source file and assign to data
sourceFileStream >> data;
// Show result
std::cout << data;
}
else std::cerr << "\n\nError: Could not open source file:\n\n";
}
Related
Input file: In each row, there's an entry that is a pair of ID - name - GPA. Values are tab-delimited.
20210001 Bill 3.61
20210002 Joe 3.21
20210003 Royce 4.32
20210004 Lucy 2.21
I have to rearrange this file sorted by the GPA (in decreasing order).
#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
#include <vector>
using namespace std;
int main() {
ifstream inputfile("input.txt");
ofstream outputfile("output.txt");
if (inputfile.fail()) {
cout << "Cannot open inputfile" << endl;
}
if (outputfile.fail()) {
cout << "Cannot open outputfile" << endl;
}
if (inputfile.is_open()) {
string line;
while (getline(inputfile, line)) {
string token;
stringstream ss(line);
while (getline(ss, token, '\t')) {
}
}
}
inputfile.close();
outputfile.close();
return 0;
}
I'm not sure what to do next.
When doing I/O from/to streams (like file streams) it usually makes it easier to create a class to keep the data for each record in the file and to create overloads for operator>> (in) and operator<< (out).
Example:
#include <algorithm>
#include <fstream>
#include <iostream>
#include <sstream>
#include <string>
#include <vector>
// one line in the file could possibly have this representation in your program:
struct record {
std::uint32_t ID;
std::string name;
double GPA;
};
// an overload to read one line of data from an istream (like an ifstream) using getline
std::istream& operator>>(std::istream& is, record& r) {
if(std::string line; std::getline(is, line)) {
std::istringstream iss(line);
if(not (iss >> r.ID >> r.name>> r.GPA)) {
is.setstate(std::ios::failbit);
}
}
return is;
}
// an overload to write one line to an ostream (like an ofstream)
std::ostream& operator<<(std::ostream& os, const record& r) {
return os << r.ID<< '\t' << r.name << '\t' << r.GPA << '\n';
}
With that boilerplate in place, making the actual program becomes easy.
int main() {
std::ifstream inputfile("input.txt");
// read all records from the file into a vector
std::vector<record> records(
std::istream_iterator<record>(inputfile),
std::istream_iterator<record>{}
);
// sort the records according to GPA
// if you want a decending order, just make it return rhs.GPA < lhs.GPA;
std::sort(records.begin(), records.end(),
[](const record& lhs, const record& rhs) {
return lhs.GPA < rhs.GPA;
}
);
std::ofstream outputfile("output.txt");
// put the result in the output file
std::copy(records.begin(),
records.end(),
std::ostream_iterator<record>(outputfile));
}
There may be a few things in this answer that you haven't seen before. I'll list resources for those I anticipate may require some reading:
operator<< / operator>> overloading. See Stream extraction and insertion
std::istringstream - You put a std::string in it - and then it behaves like any other std::istream (like a std::ifstream).
std::istream_iterator/std::ostream_iterator
std::sort / std::copy are two of the many algorithms in the standard library.
std::vector - A class template acting as a dynamic array where you can add and remove data, like record in this answer.
Lambda expressions - In this answer it's the functor [](const record& lhs, const record& rhs) { ... } used to sort the records.
If you know exactly how many tokens are in a line:
You could simply getline() 3 times with the tab delimiter, and store the values separately.
string id;
string name;
string gpa;
getline(ss, id, '\t');
getline(ss, name, '\t');
getline(ss, gpa, '\t');
This logic would live within your loop that iterates over the lines in the file.
You could use a struct to contain all your fields:
struct user
{
int id; string name; double point;
};
Then insert all of them into a std::vector and finally use sort() with the comp parameter to sort by points.
Code:
#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
#include <vector>
#include <algorithm>
#include <iomanip>
using namespace std;
struct user
{
int id; string name; double point;
};
vector<user> users;
void stripInfoFromString(string inp)
{
stringstream cur(inp);
user curUser;
cur >> curUser.id >> curUser.name >> curUser.point;
users.push_back(curUser);
}
bool compareUser(user x, user y)
{
return x.point < y.point;
}
int main()
{
string a1 = "20210001 Bill 3.61";
string a2 = "20210002 Joe 3.21";
string a3 = "20210003 Royce 4.32";
string a4 = "20210004 Lucy 2.21";
stripInfoFromString(a1);
stripInfoFromString(a2);
stripInfoFromString(a3);
stripInfoFromString(a4);
sort(users.begin(), users.end(), compareUser);
cout << fixed << setprecision(2);
for (user cur : users)
{
cout << cur.id << " " << cur.name << " " << cur.point << "\n";
}
}
Output:
20210004 Lucy 2.21
20210002 Joe 3.21
20210001 Bill 3.61
20210003 Royce 4.32
I used standard input/output to minimize the code, you can simply switch for file inputs easily.
More info:
struct : https://en.cppreference.com/w/c/language/struct
sort() : https://en.cppreference.com/w/cpp/algorithm/sort
Also, see here why is using namespace std; considered bad practice.
I would suggest defining a struct to hold the 3 tokens, and then create a std::vector holding instances of that struct for each line. You can then sort that vector on the 3rd token. You already have <vector> in your header includes.
#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
#include <vector>
#include <algorithm>
using namespace std;
struct entry
{
int id;
string name;
double gpa;
};
int main() {
ifstream inputfile("input.txt");
ofstream outputfile("output.txt");
vector<entry> entries;
if (inputfile.fail()) {
cout << "Cannot open inputfile" << endl;
}
if (outputfile.fail()) {
cout << "Cannot open outputfile" << endl;
}
string line;
while (getline(inputfile, line)) {
istringstream iss(line);
entry e;
string token;
getline(iss, token, '\t');
e.id = stoi(token);
getline(iss, e.name, '\t');
getline(iss, token, '\t');
e.gpa = stod(token);
/* alternatively:
iss >> e.id >> e.name >> e.gpa;
*/
entries.push_back(e);
}
inputfile.close();
outputfile.close();
sort(entries.begin(), entries.end(),
[](const entry &e1, const entry &e2){
return e1.gpa > e2.gpa;
}
);
for (const entry &e : entries) {
outputfile << e.id << '\t' << e.name << '\t' << e.gpa << '\n';
}
return 0;
}
Demo
Just a simple question, how do I include int values inside a getline()? I have searched online but couldn't find any that helps me. I am reading off a txt file. It is a row of numbers. For eg: 1,2,3,4,5. I am hoping that I can apply these int values anywhere so that the only way I can change the values is through the txt file.
I decided to use getline() but realise that I cannot use an integer. I am sorry, I am new to this C++. I hope that you can tell me where I went wrong.
Thanks!
This is my struct:
struct vacancyData {
int CCSpot;
int SNSpot;
int TPSpot;
int SCSpot;
int DRSpot;
};
This is my code:
ifstream File2;
File2.open("Vacancy.txt");
vector<vacancyData> v1;
vacancyData f;
while (getline(File2, f.CCSpot, ','))
{
getline(File2, f.SNSpot, ',');
getline(File2, f.TPSpot, ',');
getline(File2, f.SCSpot, ',');
getline(File2, f.DRSpot, '\n');
v1.push_back(f);
}
The direct answer to the question
How do I include an int inside a getline()
is: This is not possible at all.
std::getline is basically used to read a std::string from a stream, until a delimiter is found. In most cases, and that is also a default argument, the delimiter is '\n'. And with that, a complete line is read into a std::string. Please read here for a description.
If your input data is OK in most cases, then no std::getline is needed. Basic input validation can also be done directly. Please see the below example code:
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
struct VacancyData {
int CCSpot;
int SNSpot;
int TPSpot;
int SCSpot;
int DRSpot;
};
int main() {
// Filename
const std::string fileName{ "r:\\vacancy.txt" };
// Open file and check, if it could be opened. Use C++17 if-statement with initializer
if (std::ifstream vacancyFileStream{ fileName }; vacancyFileStream) {
// Here we will stor our data
std::vector<VacancyData> all{};
// Temps, to check, if delimiter is comma
char c1{}, c2{}, c3{}, c4{};
// This is one for loop and will read the complet file and do basic input validation
for (VacancyData vc{};
(vacancyFileStream >> vc.CCSpot >> c1 >> vc.SNSpot >> c2 >> vc.TPSpot >> c3 >> vc.SCSpot >> c4 >> vc.DRSpot) &&
c1 == ',' && c2 == ',' && c3 == ',' && c4 == ',';
all.push_back(vc))
; // Empt loop body. No statement within for loop body
// Ws there an error and could all data be read?
if (vacancyFileStream.fail() || not vacancyFileStream.eof())
std::cerr << "\n\nErorw hile reading input data\n\n";
for (const VacancyData& vc : all)
std::cout << vc.CCSpot << '\t' << vc.SNSpot << '\t' << vc.TPSpot << '\t' << vc.SCSpot << '\t' << vc.DRSpot << '\n';
}
else std::cerr << "\n\nEror: cannot open source file '" << fileName << "'\n\n";
return 0;
}
But, it is considere good practice to first read a complete line, then split it up in parts and then convert it to your structure elements.
Additionally: In C++ we often use an object oriented approach. Meaning, and objects methods, operating with the data are encapsulated in a class/struct.
In your case, we would overwrite the extractor >> and inserter operator <<.
This would then look like that (I am using C++17):
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <iterator>
#include <sstream>
struct VacancyData {
int CCSpot;
int SNSpot;
int TPSpot;
int SCSpot;
int DRSpot;
friend std::istream& operator >> (std::istream& is, VacancyData& vc) {
char c{};
return is >> vc.CCSpot >> c >> vc.SNSpot >> c >> vc.TPSpot >> c >> vc.SCSpot >> c >> vc.DRSpot;
}
friend std::ostream& operator << (std::ostream& os, const VacancyData& vc) {
return os << vc.CCSpot << '\t' << vc.SNSpot << '\t' << vc.TPSpot << '\t' << vc.SCSpot << '\t' << vc.DRSpot;
}
};
int main() {
// Filename
const std::string fileName{ "r:\\vacancy.txt" };
// Open file and check, if it could be opened. Use C++17 if-statement with initializer
if (std::ifstream vacancyFileStream{ fileName }; vacancyFileStream) {
// Here we will store our data. Read complete file and parse it
std::vector all(std::istream_iterator< VacancyData>(vacancyFileStream), {});
// Ws there an error and could all data be read?
if (vacancyFileStream.fail() || not vacancyFileStream.eof())
std::cerr << "\n\nErorw hile reading input data\n\n";
// Show output
for (const VacancyData& vc : all)
std::cout << vc << '\n';
}
else std::cerr << "\n\nEror: cannot open source file '" << fileName << "'\n\n";
return 0;
}
Now, if you want to change the reading of your data, you can use any method that you want. For example, you can read a complete line, using std::getline use any method shown below to split the input string into parts. Then you can use any method to convert that string parts into an integer for your struct. You will just change the body of your extractor operator. Nothing else will be affected. That sis the beauty of encapsulation.
Regarding: Splitting a string into tokens is a very old task. There are many many solutions available. All have different properties. Some are difficult to understand, some are hard to develop, some are more complex, slower or faster or more flexible or not.
Alternatives
Handcrafted, many variants, using pointers or iterators, maybe hard to develop and error prone.
Using old style std::strtok function. Maybe unsafe. Maybe should not be used any longer
std::getline. Most used implementation. But actually a "misuse" and not so flexible
Using dedicated modern function, specifically developed for this purpose, most flexible and good fitting into the STL environment and algortithm landscape. But slower.
Please see 4 examples in one piece of code.
#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
#include <regex>
#include <algorithm>
#include <iterator>
#include <cstring>
#include <forward_list>
#include <deque>
using Container = std::vector<std::string>;
std::regex delimiter{ "," };
int main() {
// Some function to print the contents of an STL container
auto print = [](const auto& container) -> void { std::copy(container.begin(), container.end(),
std::ostream_iterator<std::decay<decltype(*container.begin())>::type>(std::cout, " ")); std::cout << '\n'; };
// Example 1: Handcrafted -------------------------------------------------------------------------
{
// Our string that we want to split
std::string stringToSplit{ "aaa,bbb,ccc,ddd" };
Container c{};
// Search for comma, then take the part and add to the result
for (size_t i{ 0U }, startpos{ 0U }; i <= stringToSplit.size(); ++i) {
// So, if there is a comma or the end of the string
if ((stringToSplit[i] == ',') || (i == (stringToSplit.size()))) {
// Copy substring
c.push_back(stringToSplit.substr(startpos, i - startpos));
startpos = i + 1;
}
}
print(c);
}
// Example 2: Using very old strtok function ----------------------------------------------------------
{
// Our string that we want to split
std::string stringToSplit{ "aaa,bbb,ccc,ddd" };
Container c{};
// Split string into parts in a simple for loop
#pragma warning(suppress : 4996)
for (char* token = std::strtok(const_cast<char*>(stringToSplit.data()), ","); token != nullptr; token = std::strtok(nullptr, ",")) {
c.push_back(token);
}
print(c);
}
// Example 3: Very often used std::getline with additional istringstream ------------------------------------------------
{
// Our string that we want to split
std::string stringToSplit{ "aaa,bbb,ccc,ddd" };
Container c{};
// Put string in an std::istringstream
std::istringstream iss{ stringToSplit };
// Extract string parts in simple for loop
for (std::string part{}; std::getline(iss, part, ','); c.push_back(part))
;
print(c);
}
// Example 4: Most flexible iterator solution ------------------------------------------------
{
// Our string that we want to split
std::string stringToSplit{ "aaa,bbb,ccc,ddd" };
Container c(std::sregex_token_iterator(stringToSplit.begin(), stringToSplit.end(), delimiter, -1), {});
//
// Everything done already with range constructor. No additional code needed.
//
print(c);
// Works also with other containers in the same way
std::forward_list<std::string> c2(std::sregex_token_iterator(stringToSplit.begin(), stringToSplit.end(), delimiter, -1), {});
print(c2);
// And works with algorithms
std::deque<std::string> c3{};
std::copy(std::sregex_token_iterator(stringToSplit.begin(), stringToSplit.end(), delimiter, -1), {}, std::back_inserter(c3));
print(c3);
}
return 0;
}
In modern C++ you would probably do:
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <iterator>
#include <sstream>
#include <regex>
// Regex for integer
const std::regex re{R"(([-+]?\d+))"};
struct VacancyData {
int CCSpot;
int SNSpot;
int TPSpot;
int SCSpot;
int DRSpot;
friend std::istream& operator >> (std::istream& is, VacancyData& vc) {
// Read a complete line and check, if that worked
if (std::string line{}; std::getline(is, line)) {
// Split the string into parts. The parts will definitely contain a integer
std::vector part(std::sregex_token_iterator(line.begin(), line.end(), re), {});
// Sanity check. Could we read 5 values?
if (not (part.size() == 5u)) {
std::cerr << "\n\nError while parsing line '" << line << '\n';
is.setstate(std::ios::failbit);
}
else {
// Splitting the string worked. We will have intergers in the parts. stoi will not fail
vc.CCSpot = std::stoi(part[0]);
vc.SNSpot = std::stoi(part[1]);
vc.TPSpot = std::stoi(part[2]);
vc.SCSpot = std::stoi(part[3]);
vc.DRSpot = std::stoi(part[4]);
}
}
return is;
}
friend std::ostream& operator << (std::ostream& os, const VacancyData& vc) {
return os << vc.CCSpot << '\t' << vc.SNSpot << '\t' << vc.TPSpot << '\t' << vc.SCSpot << '\t' << vc.DRSpot;
}
};
int main() {
// Filename
const std::string fileName{ "r:\\vacancy.txt" };
// Open file and check, if it could be opened. Use C++17 if-statement with initializer
if (std::ifstream vacancyFileStream{ fileName }; vacancyFileStream) {
// Here we will store our data. Read complete file and parse it
std::vector all(std::istream_iterator< VacancyData>(vacancyFileStream), {});
// Show output
for (const VacancyData& vc : all)
std::cout << vc << '\n';
}
else std::cerr << "\n\nEror: cannot open source file '" << fileName << "'\n\n";
return 0;
}
But, there are tons of different possible solutions. And everybody can select whatever.
All above needs to be compiles with C++17.
You can use std::getline to do this, but you need use a std::string and then to convert it to an integer. One way is to use the std::stoi function.
Example:
#include <algorithm>
#include <fstream>
#include <iostream>
#include <iterator>
#include <string>
#include <vector>
struct vacancyData {
int CCSpot;
int SNSpot;
int TPSpot;
int SCSpot;
int DRSpot;
};
// Overload operator>> for reading a "vacancyData" struct from an istream (like a file)
std::istream& operator>>(std::istream& is, vacancyData& vd) {
std::string tmp;
try {
if(std::getline(is, tmp, ',')) {
vd.CCSpot = std::stoi(tmp);
if(std::getline(is, tmp, ',')) {
vd.SNSpot = std::stoi(tmp);
if(std::getline(is, tmp, ',')) {
vd.TPSpot = std::stoi(tmp);
if(std::getline(is, tmp, ',')) {
vd.SNSpot = std::stoi(tmp);
if(std::getline(is, tmp, ',')) {
vd.SCSpot = std::stoi(tmp);
if(std::getline(is, tmp)) {
vd.DRSpot = std::stoi(tmp);
}
}
}
}
}
}
}
catch(...) { // one of the stoi calls failed
is.setstate(std::ios::failbit);
}
return is;
}
int main() {
std::ifstream File2("Vacancy.txt");
if(File2) {
// construct the vector using iterators:
std::vector<vacancyData> v1(std::istream_iterator<vacancyData>(File2),
std::istream_iterator<vacancyData>{});
// use the filled vector "v1" ...
}
}
But since there is built-in support for extracting ints directly from istreams, I suggest using that instead.
Example:
// A small support class to "eat" separators, like comma and newline
struct eater { char ch; };
std::istream& operator>>(std::istream& is, eater& e) {
if(is.peek() == e.ch) is.ignore(); // if the next char is the expected, skip it
else is.setstate(std::ios::failbit); // else set the failbit
return is;
}
std::istream& operator>>(std::istream& is, vacancyData& vd) {
eater comma{','};
eater newline{'\n'};
return is >>
vd.CCSpot >> comma >>
vd.SNSpot >> comma >>
vd.TPSpot >> comma >>
vd.SCSpot >> comma >>
vd.DRSpot >> newline;
}
I want to read a csv data to vector of struct in cpp, This is what I wrote, I want to store the iris dataset in pointer of struct vector csv std::vector<Csv> *csv = new std::vector<Csv>;
#include <vector>
#include <iostream>
#include <fstream>
#include <string>
#include <sstream>
struct Csv{
float a;
float b;
float c;
float d;
std::string e;
};
int main(){
std::string colname;
// Iris csv dataset downloaded from
// https://gist.github.com/curran/a08a1080b88344b0c8a7
std::ifstream *myFile = new std::ifstream("iris.csv");
std::vector<Csv> *csv = new std::vector<Csv>;
std::string line;
// Read the column names
if(myFile->good())
{
// Extract the first line in the file
std::getline(*myFile, line);
// Create a stringstream from line
std::stringstream ss(line);
// Extract each column name
while(std::getline(ss, colname, ',')){
std::cout<<colname<<std::endl;
}
}
// Read data, line by line
while(std::getline(*myFile, line))
{
// Create a stringstream of the current line
std::stringstream ss(line);
}
return 0;
}
I dont know how to implement this part of the code which outputs line with both float and string.
// Read data, line by line
while(std::getline(*myFile, line))
{
// Create a stringstream of the current line
std::stringstream ss(line);
}
Evolution
We start with you program and complete it with your current programm style. Then we analyze your code and refactor it to a more C++ style solution. In the end we show a modern C++ solution using more OO methods.
First your completed code:
#include <vector>
#include <iostream>
#include <fstream>
#include <string>
#include <sstream>
struct Csv {
float a;
float b;
float c;
float d;
std::string e;
};
int main() {
std::string colname;
// Iris csv dataset downloaded from
// https://gist.github.com/curran/a08a1080b88344b0c8a7
std::ifstream* myFile = new std::ifstream("r:\\iris.csv");
std::vector<Csv>* csv = new std::vector<Csv>;
std::string line;
// Read the column names
if (myFile->good())
{
// Extract the first line in the file
std::getline(*myFile, line);
// Create a stringstream from line
std::stringstream ss(line);
// Extract each column name
while (std::getline(ss, colname, ',')) {
std::cout << colname << std::endl;
}
}
// Read data, line by line
while (std::getline(*myFile, line))
{
// Create a stringstream of the current line
std::stringstream ss(line);
// Extract each column
std::string column;
std::vector<std::string> columns{};
while (std::getline(ss, column, ',')) {
columns.push_back(column);
}
// Convert
Csv csvTemp{};
csvTemp.a = std::stod(columns[0]);
csvTemp.b = std::stod(columns[1]);
csvTemp.c = std::stod(columns[2]);
csvTemp.d = std::stod(columns[3]);
csvTemp.e = columns[4];
// STore new row data
csv->push_back(csvTemp);
}
// Show everything
for (const Csv& row : *csv)
std::cout << row.a << '\t' << row.b << '\t' << row.c << '\t' << row.d << '\t' << row.e << '\n';
return 0;
}
The question that you have regarding the reading of the columns from your Csv file, can be answered like that:
You need a temporary vector. Then you use the std::getline function, to split the data in the std::istringstream and to copy the resulting substrings into the vector. After that, we use string conversion functions and assign the rsults in a temporary Csv struct variable. After all conversions have been done, we move the temporary into the resulting csv vector that holds all row data.
Analysis of the program.
First, and most important, in C++ we do not use raw pointers for owned memory. We should ven not use new in most case. If at all, std::unique_ptrand std::make_unique should be used.
But we do not need dynamic memory allocation on the heap at all. You can simply define the std::vector on the functions stack. Same like in your line std::string colname; you can also define the std::vector and the std::ifstream as a normal local variable. Like for example std::vector<Csv> csv{};. Only, if you pass this variable to another function, then use pointers, but smart pointers.
Next, if you open a file, like in std::ifstream myFile("r:\\iris.csv"); you do not need to test the file streams condition with if (myFile->good()). The std::fstreams bool operator is overwritten, to give you exactly this information. Please see here.
Now, next and most important.
The structure of your source file is well known. There is a header with 5 elements and then 4 doubles and at then end a string without spaces. This makes life very easy.
If we would need to validate the input or if there would be spaces within an string, then we would need to implement other methods. But with this structure, we can use the build in iostream facilities. The snippet
// Read all data
Csv tmp{};
char comma;
while (myFile >> tmp.a >> comma >> tmp.b >> comma >> tmp.c >> comma >> tmp.d >> comma >> tmp.e)
csv.push_back(std::move(tmp));
will do the trick. Very simple.
So, the refactored solution could look like this:
#include <vector>
#include <iostream>
#include <fstream>
#include <string>
#include <sstream>
struct Csv {
float a;
float b;
float c;
float d;
std::string e;
};
int main() {
std::vector<Csv> csv{};
std::ifstream myFile("r:\\iris.csv");
if (myFile) {
if (std::string header{}; std::getline(myFile, header)) std::cout << header << '\n';
// Read all data
Csv tmp{};
char comma;
while (myFile >> tmp.a >> comma >> tmp.b >> comma >> tmp.c >> comma >> tmp.d >> comma >> tmp.e)
csv.push_back(std::move(tmp));
// Show everything
for (const Csv& row : csv)
std::cout << row.a << '\t' << row.b << '\t' << row.c << '\t' << row.d << '\t' << row.e << '\n';
}
return 0;
}
This is already much more compact. But there is more . . .
In the next step, we want to add a more Object Oriented approch.
The key is that data and methods, operating on this data, should be encapsulated in an Object / class / struct. Only the Csv struct should know, how to read and write its data.
Hence, we overwrite the extractor and inserter operator for the Csv struct. We use the same approach than before. We just encapsulate the reading and writing in the struct Csv.
After that, the main function will be even more compact and the usage is more logical.
Now we have:
#include <vector>
#include <iostream>
#include <fstream>
#include <string>
struct Csv {
float a;
float b;
float c;
float d;
std::string e;
friend std::istream& operator >> (std::istream& is, Csv& c) {
char comma;
return is >> c.a >> comma >> c.b >> comma >> c.c >> comma >> c.d >> comma >> c.e;
}
friend std::ostream& operator << (std::ostream& os, const Csv& c) {
return os << c.a << '\t' << c.b << '\t' << c.c << '\t' << c.d << '\t' << c.e << '\n';
}
};
int main() {
std::vector<Csv> csv{};
if (std::ifstream myFileStream("r:\\iris.csv"); myFileStream) {
if (std::string header{}; std::getline(myFileStream, header)) std::cout << header << '\n';
// Read all data
Csv tmp{};
while (myFileStream >> tmp)
csv.push_back(std::move(tmp));
// Show everything
for (const Csv& row : csv)
std::cout << row;
}
return 0;
}
OK. Alread rather good. Bit there is even more possible.
We can see that the source data has a header and then Csv data.
Also this can be modelled into a struct. We call it Iris. And we also add an extractor and inserter overwrite to encapsulate all IO-operations.
Additionally we use now modern algorithms, regex, and IO-iterators. I am not sure, if this is too complex now. If you are interested, then I can give you further information. But for now, I will just show you the code.
#include <vector>
#include <iostream>
#include <fstream>
#include <string>
#include <algorithm>
#include <regex>
#include <iterator>
const std::regex re{ "," };
struct Csv {
float a;
float b;
float c;
float d;
std::string e;
// Overwrite extratcor for simple reading of data
friend std::istream& operator >> (std::istream& is, Csv& c) {
char comma;
return is >> c.a >> comma >> c.b >> comma >> c.c >> comma >> c.d >> comma >> c.e;
}
// Ultra simple inserter
friend std::ostream& operator << (std::ostream& os, const Csv& c) {
return os << c.a << "\t\t" << c.b << "\t\t" << c.c << "\t\t" << c.d << "\t\t" << c.e << '\n';
}
};
struct Iris {
// Iris data consits of header and then Csv Data
std::vector<std::string> header{};
std::vector<Csv> csv{};
// Overwrite extractor for generic reading from streams
friend std::istream& operator >> (std::istream& is, Iris& i) {
// First read header values;
if (std::string line{}; std::getline(is, line))
std::copy(std::sregex_token_iterator(line.begin(), line.end(), re, -1), {}, std::back_inserter(i.header));
// Read all csv data
std::copy(std::istream_iterator<Csv>(is), {}, std::back_inserter(i.csv));
return is;
}
// Simple output. Copy data to stream os
friend std::ostream& operator << (std::ostream& os, const Iris& i) {
std::copy(i.header.begin(), i.header.end(), std::ostream_iterator<std::string>(os, "\t")); std::cout << '\n';
std::copy(i.csv.begin(), i.csv.end(), std::ostream_iterator<Csv>(os));
return os;
}
};
// Driver Code
int main() {
if (std::ifstream myFileStream("r:\\iris.csv"); myFileStream) {
Iris iris{};
// Read all data
myFileStream >> iris;
// SHow result
std::cout << iris;
}
return 0;
}
Look at the main function and how easy it is.
If you have questions, then please ask.
Language: C++17
Compiled and tested with MS Visual Studio 2019, community edition
I am new to c++ and while i was working with streams i saw the following code:
#include <iostream>
#include <fstream>
#include <sstream>
#include <vector>
#include <string>
using namespace std;
class Results
{
public:
string const& operator[](int index) const
{
return m_data[index];
}
int size() const
{
return m_data.size();
}
void readnext(istream& str)
{
string line;
getline(str, line);
cout << "line :" << line <<endl;
stringstream lineStream(line);
string cell;
m_data.clear();
while(getline(lineStream, cell, ','))
{
m_data.push_back(cell);
cout << cell<<endl;
}
}
private:
vector<string> m_data;
};
istream& operator>>(istream& str, Results & data)
{
data.readnext(str);
return str;
}
int main()
{
ifstream file("filename.txt");
Results r1;
while(file >> r1)
{
cout << "1st element: " << r1[3] << "\n";
}
}
When the call data.readnext(str) is made:
1)what is the value of str which is passed as an argument? When I print it out and i get 0x7ffd30f01b10 which is an address.
2)in the function getline(str, line); gives line the value of the first row of the file. I don't get why. Shouldn't that be getline(file, line);
I generally don't understand how this works so any help would be highly appreciated
The value is a reference to the std::istream superclass of the instantiated std::ifstream file object.
No. There is no file object visible in the scope of the readnext() function. The code is correct. str is the std::istream & parameter to readnext(), and matches the type of the first parameter to std::getline().
I have the following data:
$GPVTG,,T,,M,0.00,N,0.0,K,A*13
I need to read the data, however there are blanks in between the commas, therefore I am not sure how I should read the data.
Also, how do I select GPVTG only for a group of data? For example:
GPVTG,,T,,M
GPGGA,184945.00
GPRMC,18494
GPVTG,,T,,M,0
GPGGA,184946.000,3409
I have tried using:
/* read data line */
fgets(gpsString,100,gpsHandle);
char type[10] = "GPVTG";
sscanf(gpsString," %GPVTG", &type);
if (strcmp(gpsString, "GPTVG") == 0){
printf("%s\n",gpsString);
}
Thats what i'd do
#include <iostream>
#include <vector>
#include <sstream>
#include <fstream>
#include <string>
using namespace std;
vector<string> &split(const string &s, char delim, vector<string> &elems) {
stringstream ss(s);
string item;
while (getline(ss, item, delim)) {
elems.push_back(item);
}
return elems;
}
vector<string> split(const string &s, char delim) {
vector<string> elems;
split(s, delim, elems);
return elems;
}
int main()
{
ifstream ifs("file.txt");
string data_string;
while ( getline( ifs, data_string ) )
{
//i think you'd want to erase first $ charachter
if ( !data_string.empty() ) data_string.erase( data_string.begin() );
//now all data put into array:
vector<string> data_array = split ( data_string, ',' );
if ( data_array[0] == "GPVTG" )
{
//do whatever you want with that data entry
cout << data_string;
}
}
return 0;
}
Should handle your task. All empty elements will be empty "" strings in array. Ask if you need anything else.
Credits for split functions belong to Split a string in C++? answer.
How about this
#include <istream>
#include <sstream>
class CSVInputStream {
public:
CSVInputStream(std::istream& ist) : input(ist) {}
std::istream& input;
};
CSVInputStream& operator>>(CSVInputStream& in, std::string& target) {
if (!in.input) return in;
std::getline(in.input, target , ',');
return in;
}
template <typename T>
CSVInputStream& operator>>(CSVInputStream& in, T& target) {
if (!in.input) return in;
std::string line;
std::getline(in.input, line , ',');
std::stringstream translator;
translator << line;
translator >> target;
return in;
}
//--------------------------------------------------------------------
// Usage follow, perhaps in another file
//--------------------------------------------------------------------
#include <fstream>
#include <iostream>
int main() {
std::ifstream file;
file.open("testcsv.csv");
CSVInputStream input(file);
std::string sentence_type;
double track_made_good;
char code;
double unused;
double speed_kph;
char speed_unit_kph;
double speed_kmh;
char speed_unit_kmh;
input >> sentence_type >> track_made_good >> code;
input >> unused >> unused;
input >> speed_kph >> speed_unit_kph;
input >> speed_kmh >> speed_unit_kmh;
std::cout << sentence_type << " - " << track_made_good << " - ";
std::cout << speed_kmh << " " << speed_unit_kmh << " - ";
std::cout << speed_kph << " " << speed_unit_kph << std::endl;;
}
This separates the comma separation from the reading of the values, and can be reused on
most other comma separated stuff.
If you want use C++ style code based on fstream:
fin.open(input);
cout << "readed";
string str;
getline(fin, str); // read one line per time