I am reading line by line from a text file whose contents are separated by commas and parsed by extracting with getline() into my stringColor, stringName, stringReward variables, passed into my stringstream ss, and then passed to my tileArray pointer array into respective int, string, and int variables.
My program compiles, however when I run it, it generates a Segmentation Fault 11 to what appears to be where I pass the line contents into stringstream. I cannot find where the problem is however...
Perhaps if someone could point out where the error is, I would be greatly appreciative.
This is the format of each line I am trying to read in from the text file.
It should be able to read in any number of lines.
0,tile 1,5
4,tile 2,0
2,tile 4,1
#include <stdio.h>
#include <string>
#include <fstream>
#include <sstream>
#include <iostream>
#include <stdlib.h>
using namespace std;
typedef struct
{
int color;
string name;
int reward;
}tile;
int main()
{
string line;
int numberOfLines = 0;
ifstream inputFile("inputFile.txt");
if (inputFile.is_open())
{
while(getline(inputFile, line))
{
++numberOfLines; //value to set tile amount
cout << numberOfLines <<endl;
}
tile *tileArray = new tile[numberOfLines];
string stringColor, stringName, stringReward; //declare these values as strings and later convert
stringstream ss; //stringstream variable to convert string variable
for(int n = 0; n<(numberOfLines-1); n++)
{
getline(inputFile, stringColor, ','); //delimiter at first comma
cout << stringColor << endl;
getline(inputFile, stringName, ','); // delimiter at second
cout << stringName << endl;
getline(inputFile, stringReward); // stop at the end of the line
cout << stringReward << endl;
ss<<stringColor;
ss>>tileArray[n]->color;
ss.str("");
ss.clear();
cout << tileArray[n]->color;
ss<<stringName;
ss>>tileArray[n]->name;
ss.str("");
ss.clear();
cout << tileArray[n]->name;
ss<<stringReward;
ss>>tileArray[n]->reward;
ss.str("");
ss.clear();
cout << tileArray[n]->reward;
}
}
return 0;
}
I would simplify the use of the stringstream object. Using the same stringstream object as an input stream and output stream requires a deep understanding of how the internal position is manipulated.
{
// Create a nested block and a local istringstream in the nested scope
istringstream ss(stringColor);
ss >> tileArray[n]->color;
}
cout << tileArray[n]->color;
Similarly,
{
istringstream ss(stringName);
ss >> tileArray[n]->name;
}
cout << tileArray[n]->name;
and
{
istringstream ss(stringReward);
ss >> tileArray[n]->reward;
}
cout << tileArray[n]->reward;
Related
I am making a bowling program for school that stored scores in a text file with the format:
paul 10 9 1 8 1, ...etc
jerry 8 1 8 1 10 ...etc
...etc
I want to read the file into a stringstream using getline() so I can use each endl as a marker for a new player's score (because the amount of numbers on a line can be variable, if you get a spare or strike on round 10). I can then read the stringstream using >> to get each score and push it into a vector individually.
However, when trying to use getline(fstream, stringstream), I get an error
no instance of overloaded function "getline" matches the argument list -- argument types are: (std::fstream, std::stringstream)
How can I make this work?
My code looks like this:
#include <iostream>
#include <vector>
#include <fstream>
#include <exception>
#include <string>
#include <sstream>
using namespace std;
//other parts of the program which probably don't matter for this error
vector <int> gameScore;
vector<string> playerName;
int i = 0;
int j = 0;
string name;
int score;
stringstream line;
while (in.good()){ //in is my fstream
playerName.push_back(name);
cout << playerName[i] << " ";
i++;
getline(in, line);
while (line >> score){
gameScore.push_back(score);
cout << gameScore[j] << " ";
j++;
}
}
You can't use std::getline() to read from a std::ifstream directly into a std::stringstream. You can only read into a std::string, which you can then assign to the std::stringstream, eg:
vector<int> gameScore;
vector<string> playerName;
string name, line;
int score;
while (getline(in, line)){
istringstream iss(line);
iss >> name;
playerName.push_back(name);
cout << name << " ";
while (iss >> score){
gameScore.push_back(score);
cout << score << " ";
}
}
I need to load in the vertices of a .obj file in c++. I just copied over the verts from the object I downloaded so I didn't have to worry about texture mapping or anything. I was able to separate it into a single line and get rid of the v but am not able to isolate the x,y,z coordinates so I can assign it to three separate variables.
main.cpp
// ConsoleApplication3.cpp : This file contains the 'main' function. Program execution begins and ends there.
//
#include <iostream>
#include <memory>
#include <fstream>
#include <string>
using namespace std;
string pos1, pos2, pos3;
int main()
{
string line;
ifstream myfile("fox.txt");
if (myfile.is_open())
{
while (getline(myfile, line))
{
line.erase(std::remove(line.begin(), line.end(), 'v'), line.end());
cout << line << endl;
}
myfile.close();
}
else cout << "Unable to open file";
return 0;
}
fox.txt
v 10.693913 60.403057 33.765018
v -7.016389 46.160694 36.028797
v 9.998714 51.307644 35.496368
v -8.642366 49.095310 35.725204
A simple way to read in the line
v 10.693913 60.403057 33.765018
and separate it into 3 different variables is to first read in a char, then read in three doubles:
ifstream fin("fox.txt");
vector <vector<double>> data; // holds sets of coordinates
double a, b, c;
char v;
while(fin >> v >> a >> b >> c){
data.push_back({a, b, c});
}
If you wanted, you could also use std::stringstream to parse the input into doubles.
An easy way is to simply use std::stringstream and treat it like you would any other stream.
#include <sstream>
...
std::string pos1, pos2, pos3;
std::stringstream lineStream;
...
while (getline(myfile, line))
{
/* Make a string stream out of the line we read */
lineStream.str(line);
char skip; // Temp var to skip the first 'v'
lineStream >> skip >> pos1 >> pos2 >> pos3;
/* Reset error state flags for next iteration */
lineStream.clear();
}
Or you could avoid all that by using the >> operator on myfile directly.
std::string temp, pos1, pos2, pos3;
while (myfile >> temp >> pos1 >> pos2 >> pos3)
{
...
}
I'm going to figure you want to store this data in the likes of a std::vector. This is one way of doing it.
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <sstream>
const char* test_str = R"(
v 10.693913 60.403057 33.765018
v -7.016389 46.160694 36.028797
v 9.998714 51.307644 35.496368
v -8.642366 49.095310 35.725204
)";
struct data_item {
double x;
double y;
double z;
};
using data_set = std::vector<data_item>;
int main()
{
//std::ifstream myfile("fox.txt");
//if (!myfile.is_open()) {
// std::cout << "Unable to open file\n";
// return -1;
//}
std::stringstream as_file;
as_file << test_str;
data_set set;
for (; ;) {
std::string dummy;
data_item item;
as_file >> dummy >> item.x >> item.y >> item.z;
if (!dummy.size())
break;
set.push_back(item);
}
for (auto& item : set)
std::cout << item.x << " " << item.y << " " << item.z << std::endl;
return 0;
}
Don't do: using namespace std; It will save you a lot of headaches down the road. It also makes your code more readable to know stuff is out of the standard library.
When testing, it is sometimes more simple to use local data as I have with test_str. As pointed out in the comments, you could just let the stream do the conversion from text to doubles.
Note I've taken care of a failed file error in one place, the commented file stuff. Putting an else way down from the failure is not so clear and creates a large unneeded scope.
I have a code like this, concerning stringstream. I found a strange behavior:
#include <iostream>
#include <fstream>
#include <sstream>
using namespace std;
int main()
{
int p, q;
fstream file;
string str;
stringstream sstr;
file.open("file.txt", ios::in);
if(file.is_open()) {
while(getline(file, str)) {
sstr << str;
sstr >> p >> q;
cout << p << ' ' << q << endl;
sstr.str("");
}
}
file.close();
return 0;
}
Suppose I have file.txt as
4 5
0 2
with return after 5 in the first line and 2 in the second line. The program gives me:
4 5
4 5
which means p and q are not correctly assigned. But I checked that each time sstr.str() with get the correct string of the line.
Why stringstream has a behaviour like this?
The stream is in a non-good state after reading the second integer, so you have to reset its error state before resuming.
Your real mistake was to not check the return value of the input operations, or you would have caught this immediately!
The simpler solution may be to not try to reuse the same stream, but instead make it anew each round:
for (std::string line; std::getline(file, line); )
{
std::istringstream iss(line);
if (!(iss >> p >> q >> std::ws) || !iss.eof())
{
// parse error!
continue;
}
std::cout << "Input: [" << p << ", " << q << "]\n";
}
When you read p, then q, you reach the end of your stream and the flag eofbit is set and you can't do anything anymore.
Just clear() it and your code will work as you expect.
But you may want to use directly file instead, and file.close(); will have a better place within your if:
fstream file;
file.open("file.txt", ios::in);
if(file.is_open()) {
int p, q;
while(file >> p >> q) {
cout << p << ' ' << q << endl;
}
file.close();
}
Your code has some redundant lines: fstream could be opened during the definition and no explicit file close() is needed, as it is automatically destroyed at the end of main().
Additionally, in your file reading loop, the line: sstr << str should be replaced with stringstream sstr(line); if you want to initialize a new stringstream for each line, which will make the line: sstr.str(""); redundant as well.
Applying the above corrections, here is your code:
int main() {
int p, q;
fstream file("file.txt", ios::in);
// check status
if (!file) cerr << "Can't open input file!\n";
string line;
// read all the lines in the file
while(getline(file, line)) {
// initialize the stringstream with line
stringstream sstr(line);
// extract line contents (see Note)
while (sstr >> p >> q) {
// print extracted integers to standard output
cout <<"p: " << p <<" q: "<< q << endl;
}
}
return 0;
}
Note: The line while (sstr >> p >> q) assumes that a line contains only integers, separated by white space.
Okay, so I have an input file input.txtthat contains a CSV sequence: 1,1,1,2,2,3,3,4,4
and I am trying to separate it at the commas using a stringstream; however I'm getting a little problem here. For some reason the first number from the sequence is not even getting read by the stream. To show this, I created a some debugging code to see what is happening and I found out that the first number is being stored inside csvLine and every other number is being read and coverted just fine. I don't understand why just the first number is being omitted. Below is an example pic showing exactly what I mean. num should have the same exact values and Line, but it's not. It has all the values except the first one, which is being stored inside csvLine. Why is this happening?!
#include <iostream>
#include <fstream>
#include <sstream>
using namespace std;
int main(int argc, const char * argv[]) {
ifstream file;
string line;
string csvLine; //comma seperated value line
int num = 0;
file.open(argv[1]);
if(file.is_open()) {
while(getline(file, line)) { //get the whole line and use string stream to break at commas
cout << "\nLine: " << line << endl;
//using stringstream to seperate at commas
stringstream ss(line);
while(getline(ss, csvLine, ',')) {
cout << "csvLine: " << csvLine << " " << endl;
//using stringstream to convert to int
ss >> num;
cout << "num: " << num << " " << endl;
}
}
}
return 0;
}
The problem arises since you're using getline and then extracting integer from your stringstream
You should only use getline
while(getline(ss, csvLine, ','))
{
cout << "csvLine: " << csvLine << " " << endl;
num = std::stoi( csvLine ) ;
}
When you read getline(ss, csvLine, ',') the first time it reads the number followed by the the ','. For the next numbers it just reads the comma as the number was already extracted using ss >> num. That is, the simplest fix is to only read everything up to and including the comma after the loop was executed. Since the value of string extracting the comma isn't used it makes sense to use ignore() instead of std::getline():
for (; ss >> num; ss.ignore(std::numeric_limits<std::streamsize>::max(), ',')) {
// do something with the number
}
Restructuring the loop this way has the added benefit that it is checked whether reading the number was successful.
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.)