How can I read lines from file and cut it pieces? - c++

[EDIT]
I specified my question, maybe it has more information:
I have a file with lots of lines like "string1 string2 int1 int2", so a line contains two strings and two integers. I would like to read the file line by line and push those four datas into a vector of my struct( which has the same variable types and num ). My question is, how can I do this, because the >> operator won't work anyway. Nor the getline().
and the code:
void FromFile(string filename)
{
ifstream stream;
stream.open(filename);
adatok adattemp;
while(stream.good())
{
stream >> adattemp.agresszor >> adattemp.vedo >> adattemp.haborukezdete >> adattemp.haboruvege >> ws;
cout << adattemp.agresszor;
vektor.push_back(adattemp);
}
stream.close();
}

Assuming each string is just a single word, this should work:
#include <vector>
#include <string>
#include <fstream>
struct Entry {
std::string s1;
std::string s2;
int i1;
int i2;
};
std::vector<Entry> entries;
int main()
{
std::ifstream file("yourfile");
while (file.good()) {
Entry entry;
file >> entry.s1 >> entry.s2 >> entry.i1 >> entry.i2 >> std::ws;
entries.push_back(entry);
}
return 0;
}
Note: it's important to include >> std::ws at the end of reading each line. It eats up extra whitespace. Otherwise you'll end up with an extra junk entry at the end of the file.
EDIT: As Simple pointed out in the comments, if any errors occur while reading the file, the above code will store a junk entry at the end of the vector. This code will fix that by making sure there are no errors before storing the entry:
Entry entry;
while (file >> entry.s1 >> entry.s2 >> entry.i1 >> entry.i2 >> std::ws)
{
entries.push_back(entry);
}

I'd overload >> and use std::copy
#include<vector>
#include<algorithm>
//...
struct Reader {
std::string str1;
std::string str2;
int int1;
int int2;
friend std::istream& operator << (std::istream& is, Reader &r)
{
return is >> r.str1 >> r.st2 >> r.int1 >> r.int2 ;
}
};
std::vector<Reader> vec;
std::ifstream fin("file_name");
std::copy(std::istream_iterator<Reader>(fin),
std::istream_iterator<Reader>(),
std::back_inserter(vec)
) ;
This assumes all string and int are seperated by white space
You can also overload >> too to display content in similar fashion

Since you do not know the file size, a vector would be more suited IMO.
while ( myFile >> string1 >> string2 >> myInt1 >> myInt2 ) {
stringvec1.push_back( string1 );
stringvec2.push_back( string2 );
intvec1.push_back( myInt1 );
intvec2.push_back( myInt2 );
}
EDIT:
If the 4 variables that you are reading correspond to a particular logically meaningful class, then you can have one vector of class/struct with all these 4 as members.
Something like:
struct myFileVariables {
std:string m_string1;
std:string m_string2;
int m_myInt1;
int m_myInt2;
myFileVariables ( string string1, string string2, int myInt1, int myInt2 ) :
m_string1( string1 ), m_string2( string2 ), m_myInt1( myInt1 ),
m_myInt2( myInt2 ) {}
};
And in your main function:
while ( myFile >> string1 >> string2 >> myInt1 >> myInt2 ) {
myFileVariables newEntry( string1, string2, myInt1, myInt2 );
myVec.push_back( newEntry );
}

Related

string tokeniser, delimiter and making objects with file input

I have a file that have multiple lines of the sample data given in the code. Each line is an object. I've been trying to work with string tokenizer but I keep getting errors.
#include <stdio.h>
#include <iostream>
#include <sstream>
#include <string>
int main()
{
std::string input = "kevin hill;8;8;jacky knight;5;6;alejandro wilson;jordan walls;6;layla penn;7;mindy kaling;9;jon adams;8;";
std::istringstream ss(input);
std::string token;
std::string mN, fN, mgr, psy, physio, tCo, fCo;
int mSta, mStr, fSta, fStr, psyS, physioS, tCoS, fCoS;
struct points
{
std::string athName, sportName;
int totPoints, sc;
points(int totPoints, int sc, std::string athName, std::string sportName)
{
this->totPoints = totPoints;
this->sc = sc;
this->athName = athName;
this->sportName = sportName;
}
};
while (getline(ss, token, ';'))
{
mN >> mSta >> mStr >> fN >> fSta >> fStr >> mgr >> psy >> psyS >> physio >> physioS >> tCo >> tCoS >> fCo >> fCoS;
}
points *one = new points(mSta, mStr, mN, fN);
std::cout << one->athName << std::endl;
}
I'm getting error in the beginning of the while loop as the >> after mN is giving "no operator matches these operands" error. When I start the while loop with the istringstream ss as:
ss >> mN >> mSta >> .....
It executes, but skips the first name and reads 8;8;jacky as one string and I guess that is because it is trying to complete a string but even then it is skipping the delimiter as it stops reading at a whitespace.
I'm clueless what is happening here. How do I read different data types using delimiters and make objects with them? Any suggestions?

How do i scan line and extract integers and strings containing whitespaces

I have a file with following lines:
51:HD L80 Phone:78
22:Nokia Phone:91
I need to split these into 3 separate variables
(int, string, int)
int id = line[0]
string phoneName = line[1]
int price = line [2]
I have tried many solutions for example:
std::ifstream filein("records");
for (std::string line; std::getline(filein, line); )
{
// std::cout << line << std::endl;
std::istringstream iss (line);
std::string word;
std::vector<string> tempString;
while(std::getline(iss,word,',')){
tempString.push_back(word);
// std::cout << word << "\n";
}
However in this example I do get the values but they are coming in a stream and not in one go. I do not want to save them into vector (no other way to store the incoming values) but call a function immediately after getting all the 3 values.
SOLUTION
This is a modification of the accepted answer:
`for (std::string line; std::getline(filein, line); )
{
// std::cout << line << std::endl;
std::istringstream iss (line);
for (int stockID; iss >> stockID; )
{
char eater;
iss >> eater; // this gets rid of the : after reading the first int
std::string stockName;
std::getline(iss, stockName, ':'); // reads to the next :, tosses it out and stores the rest in word
std::string catagory;
std::getline(iss, catagory, ':'); // reads to the next :, tosses it out and stores the rest in word
std::string subCatagory;
std::getline(iss, subCatagory, ':');
int stockPrice;
iss >> stockPrice;
iss >> eater; // this gets rid of the : after reading the first int
int stockQTY;
iss >> stockQTY; // get the last int
// iss >> eater;
// std::cout << stockName << "\n";
Record recordd = Record(stockID,stockName,catagory,subCatagory,stockPrice,stockQTY);
record.push_back(recordd);
}
}`
for when text file contains:
51:HD L80 Phone:Mobile:Samsung:480:40
22:Nokia Phone:Mobile:Nokia:380:200
There is no reason to use a std::stringstream here if you know you are going to have exactly 3 columns in every row. Instead you can read those values directly from the file, store them in temporaries, and then call the function with those temporary variables.
for (int a; filein >> a; )
{
char eater;
filein >> eater; // this gets rid of the : after reading the first int
std::string word;
std::getline(filein, word, ':'); // reads to the next :, tosses it out and stores the rest in word
int b;
filein >> b; // get the last int
function_to_call(a, word, b);
}
You can find different ways to split a string here:
https://www.fluentcpp.com/2017/04/21/how-to-split-a-string-in-c/
Example:
std::vector<std::string> split(const std::string& s, char delimiter)
{
std::vector<std::string> tokens;
std::string token;
std::istringstream tokenStream(s);
while (std::getline(tokenStream, token, delimiter))
{
tokens.push_back(token);
}
return tokens;
}

C++ Reading text file into struct data members

I'm currently trying to load a text file into struct data members. Each number is separated by a comma.
#include<string>
#include<sstream>
using namespace std;
struct server{
bool isBusy;
};
struct pass{
double arrivalTime = 0;
double serviceTime = 0;
int classType = 0;
};
int main(){
string fileName;
string line;
pass Pass;
cout << "Enter file name: ";
cin >> fileName;
ifstream fin(fileName);
while (getline(fin, line, ','))
{
/*NEED HELP HERE*/
fin >> Pass[0].arrivalTime;
fin >> Pass[0].serviceTime;
fin >> Pass[0].classType;
}
}
Here is an example of the text file.
0.951412936,2.131445423,0
1.902743503,2.010703852,0
2.537819984,2.326199911,0
3.425838997,1.603712153,0
3.502553324,0.998192867,0
3.917348666,1.49223429,0
4.391605986,0.831661367,0
4.947059678,0.8557003,0
5.429305232,2.42029408,0
The data in the text file follows this format:
arrivalTime,serviceTime,classType
As you can see i have split the line up and stored it in "line" using the comma delimiter, but i am unsure how to load each number into the struct in the while loop.
Any help would be appreciated.
Define an istream operator >> for your struct. Something like
struct pass {
double arrivalTime = 0;
double serviceTime = 0;
int classType = 0;
friend std::istream & operator >>(std::istream & in, pass & p) {
char c;
in >> p.arrivalTime >> c >> p.serviceTime >> c >> p.classType;
return in;
}
};
Then, simply
pass Pass;
fin >> Pass;
while (getline(fin, line))
{
sscanf(line.c_str(), "%lf,%lf,%d", &arrivalTime, &serviceTime, &classType);
}
This loop is wrong:
while (getline(fin, line, ','))
{
/*NEED HELP HERE*/
fin >> Pass[0].arrivalTime;
fin >> Pass[0].serviceTime;
fin >> Pass[0].classType;
}
You are reading everything from the stream up to the next ',' character, then trying to read more from the stream.
Given the input file:
0.951412936,2.131445423,0
1.902743503,2.010703852,0
2.537819984,2.326199911,0
Your program reads "0.951412936" into line (and discards the ',') then tries to read the next input into Pass[0].arrivalTime but the next input is 2.131445423, which was meant to be the serviceTime (which you already read into line).
As Shreevardhan suggests you can define an operator for reading your struct from a stream. I would make it more reliable like so:
struct ExpectedChar { char expected; };
// read a character from a stream and check it has the expected value
std::istream& operator>>(std::istream& in, const ExpectedChar& e)
{
char c;
if (in >> c)
if (c != e.expected) // failed to read expected character
in.setstate(std::ios::failbit);
return in;
}
// read a struct pass from a stream
std::istream& operator>>(std::istream& in, pass& p)
{
ExpectedChar comma{ ',' };
in >> p.arrivalTime >> comma >> p.serviceTime >> comma >> p.classType;
return in;
}
This will stop reading if the input file does not meet the expected format. Now you can do:
while (fin >> Pass)
{
// do something with each pass
}
if (!fin.eof()) // stopped reading before end-of-file
throw std::runtime_error("Invalid data in input file");
This will keep reading a pass from the file until reading fails, either because it reached the end of the file, or because there was some bad data in the file. If there is bad data it throws an exception.
#include<string>
#include<iostream>
#include<fstream>
#include<vector>
using namespace std;
struct server{
bool isBusy;
};
struct pass{
double arrivalTime;
double serviceTime;
int classType;
friend std::istream & operator >>(std::istream &in, pass &p) {
char c;
in >> p.arrivalTime >> c >> p.serviceTime >> c >> p.classType;
return in;
}
};
int main(){
string fileName;
string line;
cout << "Enter file name: ";
cin >> fileName;
ifstream fin(fileName.c_str(), ifstream::in);
vector<pass> passes;
pass Pass;
while (fin>>Pass)
passes.push_back(Pass);
for(vector<pass>::const_iterator iter = passes.begin();
iter != passes.end();
++iter)
std::cout<<iter->arrivalTime<<" "<<iter->serviceTime<<" "
<<iter->classType<<std::endl;
}
Here is hint;
string line;
string temp;
string::size_type sz;
while (getline(cin, line))
{
istringstream ss( line );
getline( ss, temp, ',' );
double arrivalTime = stod(temp, &sz);
getline( ss, temp, ',' );
double serviceTime = stod(temp, &sz);
getline( ss, temp, ',' );
double classType = stod(temp, &sz);
cout << arrivalTime << ' '
<< serviceTime << ' '
<< classType << endl;
}

istringsteam with line breaks

Okay I read that if we have a string s =" 1 2 3"
we can do :
istringstream iss(s);
int a;
int b;
int c;
iss >> a >> b >> c;
Lets say we have a text file with the following :
test1
100 ms
test2
200 ms
test3
300 ms
ifstream in ("test.txt")
string s;
while (getline(in, s))
{
// I want to store the integers only to a b and c, How ?
}
1) You can rely on succesful convertions to int:
int value;
std::string buffer;
while(std::getline(iss, buffer,' '))
{
if(std::istringstream(buffer) >> value)
{
std::cout << value << std::endl;
}
}
2) or just skip over unnecessary data:
int value;
std::string buffer;
while(iss >> buffer)
{
iss >> value >> buffer;
std::cout << value << std::endl;
}
If you know the pattern of the details in the text file, you could parse through all the details, but only store the int values. For example:
ifstream in ("test.txt")
string s;
while (getline(in, s))
{
getline(in,s); //read the line after 'test'.
string temp;
istringstream strm(s);
s >> temp;
int a = stoi(temp) // assuming you are using C++11. Else, atoi(temp.c_str())
s >> temp;
getline(in,s); // for the line with blank space
}
This above code is still somewhat of a inelegant hack. What you could do besides this is use random file operations in C++. They allow you to move your pointer for reading data from a file. Refer to this link for more information: http://www.learncpp.com/cpp-tutorial/137-random-file-io/
PS: I haven't run this code on my system, but I guess it should work. The second method works for sure as I have used it before.

My string gets from the ifstream also "\n"

This is my problem: I read some lines from a txt. This txt is like this:
Ciao: 2000
Kulo: 5000
Aereo: 7000
ecc. I have to assign every word before(':') to a string and then to a map; and the numbers to a int and then to a map. The problem is that beginning from the second line, my string become ("\nKulo") ecc! I don't want this! What can I do?
This is the code:
#include <iostream>
#include <fstream>
#include <string>
#include <map>
using namespace std;
int main()
{
map <string, int> record;
string nome, input;
int valore;
ifstream file("punteggi.txt");
while (file.good()) {
getline(file, nome, ':');
// nome.erase(0,2); //Elimina lo spazio iniziale
file >> valore;
record[nome] = valore;
cout << nome;
}
file.close();
cout << "\nNome: ";
cin >> input;
cout << input << ": " << record[input] << "\n";
cout << "\n\n";
return 0;
}
The issue you have is that std::getline() is an unformatted input function and as such doesn't skip leading whitespace. From the looks of it, you want to skip leading whitespace:
while (std::getline(in >> std::ws, nome, ':') >> valore) {
...
}
Alternatively, if there are leading spaces, you can ignore() all characters up to the end of line after reading a value.
BTW, since I saw someone over here recommending the use of std::endl: do not use std::endl unless you really intend to flush the buffer. It is a frequent major performance problem when writing files.
Use the standard line reading idiom:
for (std::string line; std::getline(file, line); )
{
std::string key;
int n;
std::istringstream iss(line);
if (!(iss >> key >> n) || key.back() != ':') { /* format error */ }
m.insert(std::make_pair(std::string(key.cbegin(), std::prev(key.cend()),
n));
}
(Instead of the temporary string-from-iterators, you can also use key.substr(0, key.length() - 1), although I imagine that my version may be a bit more efficient. Or add a key.pop_back(); before inserting the data into the map.)