Trouble with Out of range in memory using stringsteam - c++

I just started using the stringstream for the first time and I love the concept, but I am having a hard time finding where exactly I am having an out of range in memory with my stringstream function.
What my function does is it takes in a string, for example, "N02550 G3 X16.7379 Y51.7040 R0.0115" This is machine code for a CNC machine at my job. I pass the string to a stringstream in order to find the strings that have a X, Z, Y next to them, these are coordinates. It then gets rid of the character at the beggining in order to save the float number to my struct "Coordinate"(there are 3 doubles, x, y, z).
When I run a text file that has this machine code with 33 lines, my program works. When I run it with machine code of 718 lines, it gets to 718, then crashes with out of range memory. Then another weird part is when I run machine code with 118,000 lines, it goes up to around 22,000 lines then crashes. So I'm having trouble figuring out why it is able to do that and whats causing the problem.
Here is the function:
void getC(string& line, Coordinates& c)//coordinates holds 3 doubles, x, y, z
{
//variables
string holder;
stringstream ss(line);
while(ss)
{
ss >> holder;
if(holder.at(0) == 'X')
{
holder.erase(0,1);//get rid the the character at the beggining
stringstream sss(holder);
sss >> c.x;
sss.clear();
}
if(holder.at(0) == 'Y')
{
holder.erase(0,1);
stringstream sss(holder);
sss >> c.y;
sss.clear();
}
if(holder.at(0) == 'Z')
{
holder.erase(0,1);
stringstream sss(holder);
sss >> c.z;
sss.clear();
}
if(ss.eof()) // to get out of the ss stream
break;
}
ss.clear();
}
If you want to see the whole application(the application is well documented) then ask or if you need the txt files containing the machine code. Thank you!

Try changing:
while(ss)
{
ss >> holder;
...
if(ss.eof()) // to get out of the ss stream
break;
}
To simply this:
while(ss >> holder)
{
...
}
And you can get rid of those calls to clear in each branch (X/Y/Z) as it doesn't really do anything given that sss is a temporary and you're not doing anything more with it (no point setting flags on something you're going to discard right after). I suspect your out of range issue is coming from trying to access holder.at(0) after ss >> holder fails.
You generally want to check for input failure right after reading a token, and a convenient way to both attempt to input and check for failure at once is to simply check if ss >> token evaluates to true. So we can write code like:
if (ss >> token)
{
...
}
else
{
// handle failure if necessary
}
I generally find it's a lot easier to avoid getting in trouble writing code that way than manually checking error flags.
As a simplified version:
void getC(string& line, Coordinates& c)
{
stringstream ss(line);
for (string holder; ss >> holder; )
{
const char ch = holder.at(0);
stringstream sss(holder.substr(1));
if (ch == 'X')
sss >> c.x;
else if (ch == 'Y')
sss >> c.y;
else if (ch == 'Z')
sss >> c.z;
}
}

Related

A real solution to the 'cin' and 'getline' issue

How do I get rid of the leading ' ' and '\n' symbols when I'm not sure I'll get a cin, before the getline?
Example:
int a;
char s[1001];
if(rand() == 1){
cin >> a;
}
cin.getline(s);
If I put a cin.ignore() before the getline, I may lose the first symbol of the string, so is my only option to put it after every use of 'cin >>' ? Because that's not very efficient way to do it when you are working on a big project.
Is there a better way than this:
int a;
string s;
if(rand() == 1){
cin >> a;
}
do getline(cin, s); while(s == "");
Like this:
std::string line, maybe_an_int;
if (rand() == 1)
{
if (!(std::getline(std::cin, maybe_an_int))
{
std::exit(EXIT_FAILURE);
}
}
if (!(std::getline(std::cin, line))
{
std::exit(EXIT_FAILURE);
}
int a = std::stoi(maybe_an_int); // this may throw an exception
You can parse the string maybe_an_int in several different ways. You could also use std::strtol, or a string stream (under the same condition as the first if block):
std::istringstream iss(maybe_an_int);
int a;
if (!(iss >> a >> std::ws) || iss.get() != EOF)
{
std::exit(EXIT_FAILURE);
}
You could of course handle parsing errors more gracefully, e.g. by running the entire thing in a loop until the user inputs valid data.
Both the space character and the newline character are classified as whitespace by standard IOStreams. If you are mixing formatted I/O with unformatted I/O and you need to clear the stream of residual whitespace, use the std::ws manipulator:
if (std::getline(std::cin >> std::ws, s) {
}

Reading in from a txt file. Trouble parsing info

I want to read in scores from a txt file. The scores are going into a struct.
struct playerScore
{
char name[32];
int score, difficulty;
float time;
};
the text file looks like this
Seth 26.255 40 7
as one line, where each item is followed by a tab. (Name\t time\t score\t difficulty\n)
When I begin to read in the text, I don't know how to tell the program when to stop. The scores file could be any number of lines or score entries. This is what I have attempted.
hs.open("scores.txt", ios_base::in);
hs.seekg(0, hs.beg);
if (hs.is_open())
{
int currpos = 0;
while (int(hs.tellg()) != int(hs.end));
{
hs>> inScore.name;
hs >> inScore.time;
hs >> inScore.score;
hs >> inScore.difficulty;
hs.ignore(INT_MAX, '\n');
AllScores.push_back(inScore);
currpos = (int)hs.tellg();
}
}
I'm trying to make a loop that will read in a line of code into a temp struct for the data, then push that struct into a vector of structs. Then update the currpos variable with the current location of the input pointer. However, the loop just gets stuck on the condition and freezes.
There are a multitude of ways to do this, but the following is likely what you're looking for. Declare a free-operator for extracting a single-line definition of a player-score:
std::istream& operator >>(std::istream& inf, playerScore& ps)
{
// read a single line.
std::string line;
if (std::getline(inf, line))
{
// use a string stream to parse line by line.
std::istringstream iss(line);
if (!(iss.getline(ps.name, sizeof(ps.name)/sizeof(*ps.name), '\t') &&
(iss >> ps.time >> ps.score >> ps.difficulty)))
{
// fails to parse a full record. set the top-stream fail-bit.
inf.setstate(std::ios::failbit);
}
}
return inf;
}
With that, your read code can now do this:
std::istream_iterator<playerScore> hs_it(hs), hs_eof;
std::vector<playerScore> scores(hs_it, hs_eof);
I dont think that you can just >> from your file. Do you think it will take everything till \t? :)
You can try to take for example token with strtok()
I guess it can use '\t' to split string and take for each variable via this function needed part of string
In case if it strtok() doesnt work that way i guess you can just copy till '\t' in sub-loop
You can do like this
playerScore s1;
fstream file;
file.open("scores.txt", ios::in | ios::out);
while(!file.eof()) //For end of while loop
{
file.read(s1, sizeof(playerScore));//read data in one structure.
AllScores.push_back(s1);
}

C++: Resetting an istream after reaching an 'end of file' or error

I want to be able to continue using inputs after I have made an error while inputting a value using 'std::cin >>'.
After putting a character into an integer variable, for instance, all other statements in the source that use the cin function stop working.
Is it possible to continue using cin after creating an error?
#include <iostream>
using namespace std;
int addition(){
int sum = 0, val = 0;
while(cin >> val){
sum += val;
}
return sum;
}
int multiplication(){
int x = 0, y = 0;
cin >> y;
x = y;
while(cin >> y){
x = x * y;
}
return x;
}
int main()
{
int x = addition();
int y = multiplication();
return 0;
}
If it's an error because you tried to read an integer and got some "non-number" character, you need to use cin.clear() to clear the error, then cin.get() or similar to remove the offending character(s). If you have received an end-of-file, it's much harder, since that tends to "stick" in some implementations - so, basically, if you hit an end of file, it's game over unless you jump through some really nasty hoops (writing your own streambuf seems to be one solution, but but that's about as pleasant as sticking red hot needles in your eyes, I've been told - never really tried either...).
There are many alternative solutions, such as reading a string, and then trying to translate that to numbers, or reading a character at a time, and doing your own translation, or using some ready-made library that translates for you. None of these are guaranteed to allow you to continue if the user hits the "end-of-file" key (CTRL-Z in Windows and some others, CTRL-D in Linux/Unix/MacOS X and related - not sure about MacOS before X, but does anyone still care?)
You just need to read the character out and try again.
while( !(cin >> y) && !cin.eof() )
{
cin.get(); // Skip invalid character
}
This is a little unusual. What we would normally expect is to output an error. If values are being entered at the prompt, we tend to use getline instead and then parse the result:
while(1)
{
string line;
if( getline(cin, line) )
{
istringstream iss(line);
if( iss >> y ) {
break;
} else {
cerr << "Invalid input. Try again.\n";
}
} else {
cerr << "End of stream\n";
break;
}
}

Extract data from file into vectors but avoid duplicates

I want to read data in files that are formatted like:
Point1, [3, 4]
I'm using delimiters '[' ']' and ',' and replace them with ' ' (empty space). My code now is fine and working. But the problem is, if Point1, [3, 4] appeared once, I want it to be unique and not to appear again if the same data in the text file exist.
Here is what I have:
string line, name;
char filename[50];
int x,y;
cout << "Please enter filename : ";
cin >> filename;
ifstream myfile(filename);
if (myfile.is_open()) {
while ( myfile.good() ) {
getline(myfile, line);
for (unsigned int i = 0; i < line.size(); ++i) {
if (line[i] == '[' || line[i] == ']' || line[i] == ',') {
line[i] = ' ';
}
istringstream in(line);
in >> name >> x >> y;
}
cout <<name <<endl;
if (name=="Point") {
p.push_back(Point(x,y));
}
count++;
}
myfile.close();
cout << count;
}
else cout<< "Unable to open file";
How do i do this? I tried adding this after if(name=="Point")
for (int j=0; j<p.size(); j++) {
if(p.getX != x && p.getY) != y) {
p.push_back(Point(x,y))
}
}
...but this is not working properly as data was not stored into the vector.
Anyone can help?
Instead of storing your data into vectors you can store it into sets. A set contains only unique values, so you don't have to check the uniqueness of your Points, the set will manage that.
Instead of defining vector<Point>, you have to define set<Point>, and instead of using p.push_back to add points into your vector, you have to use p.insert if it is a set.
You can check the set documentation here.
Assuming you want to keep you data store in a std::vector<Point> you could just check that no corresponding point already exists. Assuming there is an equality operator defined, it is as easy as this:
if (p.end() == std::find(p.begin(), p.end(), Point(x, y))) {
p.push_back(Point(x, y));
}
If you Point type doesn't have an equality operator and shouldn't get one, you can use a function object together with find_if() instead, e.g.:
if (p.end() == std::find_if(p.begin(), p.end(),
[=](Point const& v) { return x == v.x && y == v.y; })) {
...
You should separate your loops from other operations: The loop you propose to check if the point already exists is basically what std::find() does except that you insert a new point in each iteration! You first want to go through all existing points and see if it exists anywhere. Only if it does not, you'd insert a new point.
Note, that you did a similar mistake when replacing characters in your string: you try to decode the string after checking each character. This isn't so much a semantic problem but it is a major performance problem. You should replace the characters in a loop and after that loop you should decode the string just once. Of course, when decoding the string you need to check if the format was OK as it is always necessary to determine if an input operation was successful. That is, after your loop replacing the characters you want something like:
std::istringstream in(line);
if (in >> name >> x >> y) {
// now process the data
}
... or, if you are like me and don't like naming things which are only around temporarily:
if (std::istringstream(line) >> std::ws >> name >> x >> y) {
As another note: checking a stream for good() is generally the wrong thing to do because a stream can be in perfectly good conditions except that it had seen EOF at some point and, thus, as set the std::ios_base::eofbit. More importantly, you need to check after the input operation, not before! That is, your first loop should start with something like this:
while (std::getline(myFile, line)) {
...

boost lexical_cast throws exception

I'm using boost libs for c++ and the function lexical_cast behaves really weird. If I do lexical_cast("0.07513994") it works fine, but if I use my variable which I need to convert, it throws the bad_lexical_cast exception. Here is the code:
string word;
istringstream iss(line);
do
{
string word;
iss >> word;
double x;
x = lexical_cast<double>(word);
cout << x << endl;
} while (iss);
What am I doing wrong here? I appreciate any help, thanks
Your problem is probably that the loop is processed one more time than you expect.
The last time through the loop, the read to word fails, setting the fail bit in iss, which is what while(iss) is checking. To fix it you need to do something like this.
string word;
istringstream iss(line);
do
{
string word;
iss >> word;
if(iss)
{
double x;
x = lexical_cast<double>(word);
cout << x << endl;
}
} while (iss);
Unlike functions such as atof() which stop parsing as soon as they see an invalid character, lexical_cast requires that every character in the input string be valid. i.e. any leading or trailing spaces will cause it to throw an exception.
You want to see what kind of input it is getting and trim it accordingly. You should also catch bad_lexical_cast just in case it does get input which is completely garbage.
One possible solution is to use boos.regex or boost.xpressive to extract a valid sub-string and pass the result to lexical_cast.
The problem is probably that you are sending an empty string when there is no data left.
You should change the loop you are using.
Use the while {} loop (not the 'do while' loop). This allows you to read from the stream and test it in a single easy to read statement. Note the result of iss >> word is the stream. When used in this boolean context it is tested to see if the state is good and its value converted into something that can be used by the while condition. Thus if the operator >> filed to work correctly then the loop is never entered.
istringstream iss(line);
string word;
while(iss >> word)
{
double x = lexical_cast<double>(word);
cout << x << endl;
}
But really you don't even need the lexical cast in this situation (unless you want to test for non numbers with an exception). The standard stream operator will convert the input into a double.
istringstream iss(line);
double word;
while(iss >> word)
{
cout << word << endl;
}
if (iss.fail())
{ /* Failure to convert input to a double */
}