PDA

View Full Version : C++, using ignore twice on file stream skips to EOF?




ArtOfWarfare
May 20, 2012, 10:29 PM
I'm trying to write a function that reads several numbers from a file (it interprets them as powers of X and coefficients and stores them in an array that represents a polynomial.)

Anyways, I want it to skip over lines that start with a '/' character, to provide a mechanism of including comments in the file. Here's the code:

Oh, and a quick note.

POWER and COEFFICIENT are enum as 0 and 1, respectively. MAX_SIZE is #define as 32767 (fairly arbitrary value.)

// Read the file.
int reading = POWER;
int Xpower = 0;
long double Xcoefficients[MAX_SIZE] = { 0.0 };
long double diffTerm = 0.0;
long double mostNegDiffTerm = 0.0, mostPosDiffTerm = 0.0;

for (int i = 0; i < (MAX_SIZE*2); i++)
{
#ifdef NDEBUG
cout << "Running..." << endl;
#endif
if (file.peek() == '/')
{
#ifdef NDEBUG
cout << "Ignoring..." << endl;
#endif
file.ignore(MAX_SIZE, '\n');
file.clear(); // I've tried both with and without this line - behavior does not change.
}

switch (reading)
{
case COEFFICIENT:
file >> Xcoefficients[Xpower];
diffTerm = Xcoefficients[Xpower]*Xpower;
mostNegDiffTerm = (diffTerm<mostNegDiffTerm)?diffTerm:mostNegDiffTerm;
mostPosDiffTerm = (diffTerm>mostPosDiffTerm)?diffTerm:mostPosDiffTerm;
reading = POWER;
break;
case POWER: default:
file >> Xpower;
polyDegree = (Xpower>polyDegree)?Xpower:polyDegree;
reading = COEFFICIENT;
break;
}

if (file.peek() == EOF)
{
#ifdef NDEBUG
cout << "Reached EOF" << endl;
#endif
break;
}
}

This works perfectly for files with only one comment line, for example, this file:

// polynomial f(x) = 2.5x^3 + 3x^2 - 2.13x + 1

1 -2.13
0 1
2 3.0
3 2.5

The output for the above file is this:
Running...
Ignoring...
Running...
Running...
Running...
Running...
Running...
Running...
Running...
Running...
Reached EOF

However, if a file includes multiple comments in it, like this one:
// polynomial f(x) = 2.5x^3 + 3x^2 - 2.13x + 1

1 -2.13
0 1
//4 26
2 3.0
3 2.5

My output should look exactly the same, but instead I get this:
Running...
Ignoring...
Running...
Running...
Running...
Running...
Reached EOF

It doesn't print out that it has ignored the second comment line... it doesn't even attempt to read the rest of the file, instead it just skips straight to the end of the file?

I've tried placing the second comment in multiple places in the file, and no matter where I put it, it always skips straight to the EOF when it reaches it, without even printing that it is ignoring a comment line.

Any suggestions on why it's doing this or how I can fix it to behave the way I want it to?



lloyddean
May 20, 2012, 10:56 PM
Would you mind posting the whole source? And a sample file that fails?

chown33
May 20, 2012, 11:43 PM
Would you mind posting the whole source? And a sample file that fails?

I agree with this. Please include the exact data file that causes it to fail.

When I see this:
file.ignore(MAX_SIZE, '\n');

my first thought is, "What if the file doesn't contain another \n?". Depending on where the file came from, or how it was last edited, it's possible there are only CRs (\r's) remaining in the file, even though it appears to have multiple remaining lines when edited. Many Mac text editors will accept single CRs, single LFs, or CRLF pairs as line-endings, without regard to what line-endings came before. Programming editors in particular will do this. Many of them have a way to normalize line-endings, or convert all lines to a specific form.

I suggest looking at the failing file in a hex editor. Something like Hex Fiend (google it), or this command-line:
hexdump -C failing-file.txt

lloyddean
May 21, 2012, 12:08 AM
Silly questions -

Is this your own file format?
If so why the power first and then the coefficient?
Why not put everything on a single line instead of spreading it out over four lines?

Others to follow at a later time!

ArtOfWarfare
May 21, 2012, 05:55 AM
Silly questions -

Is this your own file format?
If so why the power first and then the coefficient?
Why not put everything on a single line instead of spreading it out over four lines?

Others to follow at a later time!

It's a class assignment. We were told the files would be formatted as having a pair of numbers on each line, the power first and then the coefficient. Some lines would have comments on them, starting with "//", which should be ignored.

I'm on my iPhone right now, I'll post the files in a few hours when I have my laptop in a wifi hotspot.

Quick question... Is it possible to have multiple delaminators for the ignore command? So I could say stop ignoring at the next '/n' or next '/r', for example? I don't have control over the files that my code will be tested with, so I don't know what the line endings the files will actually have.

Edit:

I had a couple seconds between classes to try doing the hex dump, here's the hex dump for a sample file that fails (this is for poly.txt, which is attached to the post.)
00000000 2f 2f 70 6f 6c 79 20 66 28 78 29 20 3d 20 31 39 |//poly f(x) = 19|
00000010 78 5e 34 2d 33 34 78 5e 33 2b 37 36 78 5e 32 2d |x^4-34x^3+76x^2-|
00000020 31 30 30 38 0a 2f 2f 33 0a 34 09 31 39 0a 33 09 |1008.//3.4.19.3.|
00000030 2d 33 34 0a 32 09 37 36 0a 30 09 2d 31 30 30 38 |-34.2.76.0.-1008|
00000040 0a |.|
00000041

0a is a '\n', correct?

Here's enough of the source code that it compiles on its own:

(the args are optional. stepSize/argv[2] aren't actually used in this function... they get passed to another function unrelated to reading the file. If argv[1] isn't provided, it'll ask for it. It behaves the same way whether it asks or not.)

#include <iostream>
#include <fstream>
#include <cmath>

#define MAX_SIZE 32767

using namespace std;

static int polyDegree = 0;

typedef enum {
POWER = 0,
COEFFICIENT = 1
} readingTypes;

int main(int argc, char* argv[])
{
fstream file;
char fileName[MAX_SIZE];
double stepSize = 0.0005;

if (argc > 1)
{
for (int i = 0; i < MAX_SIZE; i++)
{
if (i < (MAX_SIZE-1))
{
fileName[i] = argv[1][i];
if (fileName[i] == '\0') break;
}

else
{
fileName[MAX_SIZE-1] = '\0';
cout << "File name is too long! Truncated to: " << fileName << endl;
}
}

if (argc == 3)
{
stepSize = atof(argv[2]);
}
}

else
{
cout << "Enter the name of the file." << endl << "> " << endl;
cin.getline (fileName, MAX_SIZE, '\n');
}

file.open(fileName);

if (file.is_open())
{
// Read the file.
int reading = POWER;
int Xpower = 0;
long double Xcoefficients[MAX_SIZE] = { 0.0 };
long double diffTerm = 0.0;
long double mostNegDiffTerm = 0.0, mostPosDiffTerm = 0.0;

for (int i = 0; i < (MAX_SIZE*2); i++)
{
#ifdef NDEBUG
cout << "Running..." << endl;
#endif
if (file.peek() == '/')
{
#ifdef NDEBUG
cout << "Ignoring..." << endl;
#endif
file.ignore(MAX_SIZE, '\n');
file.clear();
}

switch (reading)
{
case COEFFICIENT:
file >> Xcoefficients[Xpower];
diffTerm = Xcoefficients[Xpower]*Xpower;
mostNegDiffTerm = (diffTerm<mostNegDiffTerm)?diffTerm:mostNegDiffTerm;
mostPosDiffTerm = (diffTerm>mostPosDiffTerm)?diffTerm:mostPosDiffTerm;
reading = POWER;
break;
case POWER: default:
file >> Xpower;
polyDegree = (Xpower>polyDegree)?Xpower:polyDegree;
reading = COEFFICIENT;
break;
}

if (file.peek() == EOF)
{
#ifdef NDEBUG
cout << "Reached EOF" << endl;
#endif
break;
}
}

// Print out f(x)
cout << endl << "f(x)=";

for (int i = polyDegree; i >= 0; i--)
{
if (!Xcoefficients[i]) continue;
cout << showpos << Xcoefficients[i];
switch(i)
{
case 1:
cout << "x";
case 0:
break;
default:
cout << "x^" << noshowpos << i;
}
}

cout << endl << endl;
cout << noshowpos;
file.close();
}

else
{
cout << "A file named \"" << fileName << "\" could not be opened." << endl;
}

return 0;
}

I've also attached three sample files.

When I test poly.txt, it doesn't read anything.

When I test polynomial.txt, it reads the first two pairs, then skips everything else.

When I test test.txt, it reads everything properly.

In polynomial.txt, if I remove the second comment, it reads everything properly.

2X Edit: I wonder if this could be remedied by moving a read pointer around or something? (I seem to recall hearing there's something called a read pointer or something like that.)

lloyddean
May 21, 2012, 12:00 PM
0a is a '\n', correct?

No, 0x0A is "line feed", 0x0D "carriage return".

'\n' is platform dependent and may be either one or a combination of the two.

Won't have time till later this afternoon to look the code over well enough to determine what's going on.

vandersmissenc
May 21, 2012, 12:35 PM
Upon looking at the code it seems like when you try to ignore it was only ignoring the first '/' in the row and so then when it began trying to read in it read 47 , the ascii representation of '/' and then just started reading from the wrong point. I went ahead and moved it to first check if file.peek() == EOF and if not then it checks if the character is a '/' or '\n' finally if none of those cases are met it reads in the character, the way you had it before it read a character every time instead of only in certain cases when other tests pass. Anyways, here is the working code.

#include <iostream>
#include <fstream>
#include <cmath>

#define MAX_SIZE 32767

using namespace std;

static int polyDegree = 0;

typedef enum {
POWER = 0,
COEFFICIENT = 1
} readingTypes;

int main(int argc, char* argv[])
{
fstream file;
char fileName[MAX_SIZE];
double stepSize = 0.0005;

if (argc > 1)
{
for (int i = 0; i < MAX_SIZE; i++)
{
if (i < (MAX_SIZE-1))
{
fileName[i] = argv[1][i];
if (fileName[i] == '\0') break;
}

else
{
fileName[MAX_SIZE-1] = '\0';
cout << "File name is too long! Truncated to: " << fileName << endl;
}
}

if (argc == 3)
{
stepSize = atof(argv[2]);
}
}

else
{
cout << "Enter the name of the file." << endl << "> " << endl;
cin.getline (fileName, MAX_SIZE, '\n');
}

file.open(fileName);

if (file.is_open())
{
// Read the file.
int reading = POWER;
int Xpower = 0;
long double Xcoefficients[MAX_SIZE] = { 0.0 };
long double diffTerm = 0.0;
long double mostNegDiffTerm = 0.0, mostPosDiffTerm = 0.0;

for (int i = 0; i < (MAX_SIZE*2); i++)
{
#ifdef NDEBUG
cout << "Running..." << endl;
#endif
if (file.peek() == EOF)
{
#ifdef NDEBUG
cout << "Reached EOF" << endl;
#endif
break;
}

else if (file.peek() == '/' || file.peek() == '\n')
{
#ifdef NDEBUG
cout << "Ignoring..." << endl;
#endif
file.ignore(MAX_SIZE, '\n');
file.clear();
}

else { switch (reading)
{
case COEFFICIENT:
file >> Xcoefficients[Xpower];
diffTerm = Xcoefficients[Xpower]*Xpower;
mostNegDiffTerm = (diffTerm<mostNegDiffTerm)?diffTerm:mostNegDiffTerm;
mostPosDiffTerm = (diffTerm>mostPosDiffTerm)?diffTerm:mostPosDiffTerm;
reading = POWER;
break;
case POWER: default:
file >> Xpower;
polyDegree = (Xpower>polyDegree)?Xpower:polyDegree;
reading = COEFFICIENT;
break;
}
}

}

// Print out f(x)
cout << endl << "f(x)=";

for (int i = polyDegree; i >= 0; i--)
{
if (!Xcoefficients[i]) continue;
cout << showpos << Xcoefficients[i];
switch(i)
{
case 1:
cout << "x";
case 0:
break;
default:
cout << "x^" << noshowpos << i;
}
}

cout << endl << endl;
cout << noshowpos;
file.close();
}

else
{
cout << "A file named \"" << fileName << "\" could not be opened." << endl;
}

return 0;
}

lloyddean
May 21, 2012, 02:52 PM
Although I haven't verified at this point I'm assuming vandersmissenc got you up and running.

If you don't mind - what class is this for, what text are you using and how far into the text are you?

ArtOfWarfare
May 21, 2012, 03:43 PM
Upon looking at the code it seems like when you try to ignore it was only ignoring the first '/' in the row and so then when it began trying to read in it read 47 , the ascii representation of '/' and then just started reading from the wrong point. I went ahead and moved it to first check if file.peek() == EOF and if not then it checks if the character is a '/' or '\n' finally if none of those cases are met it reads in the character, the way you had it before it read a character every time instead of only in certain cases when other tests pass. Anyways, here is the working code.

I tried your code but it didn't work? Except I noticed from your code that I was able to use peek() to learn that the next character was a '\n'.

After looking at the hex dump and keeping track of where tellg() - the position of the get cursor was - all the time, I found that after it passes over a '\n', it seems to jump to -1 and report itself as being at the EOF.

Thus, I gave this a shot: seekg(int, pos) moves the cursor an int distance from pos, so I just moved forward 1 character without actually reading the line break. Somehow, that caused it to avoid reading it and mistaking a peek of the next character as being an EOF?
for (int i = 0; i < (MAX_SIZE*2); i++)
{
#ifdef NDEBUG
cout << "Running... at " << file.tellg() << endl;
#endif

if (file.peek() == EOF)
{
#ifdef NDEBUG
cout << "Reached EOF at " << file.tellg() << endl;
#endif
break;
}

// Without this bit, it'll report the get cursor as being at -1 and
// cause it to declare itself as at EOF. I couldn't find out why,
// but just moving the get cursor one character forward avoids it.
else if (file.peek() == '\n')
{
file.seekg(1, ios::cur);
}

if (file.peek() == '/')
{
#ifdef NDEBUG
cout << "Ignoring... at " << file.tellg() << endl;
#endif
file.ignore(MAX_SIZE, '\n');
}

switch (reading)
{
case COEFFICIENT:
file >> Xcoefficients[Xpower];
diffTerm = Xcoefficients[Xpower]*Xpower;
mostNegDiffTerm = (diffTerm<mostNegDiffTerm)?diffTerm:mostNegDiffTerm;
mostPosDiffTerm = (diffTerm>mostPosDiffTerm)?diffTerm:mostPosDiffTerm;
reading = POWER;
break;
case POWER: default:
file >> Xpower;
polyDegree = (Xpower>polyDegree)?Xpower:polyDegree;
reading = COEFFICIENT;
break;
}
}

And it works!

vandersmissenc
May 21, 2012, 03:56 PM
I'm glad you got it working. What IDE are you using ? Perhaps thats why it did not work for you. I tried on Xcode and VS2010 and it was able to output everything properly in both using my code.

Exactly as you stated however, when I was debugging everything was set to -1 and that was what alerted me to an issue with the reading of the file.

ArtOfWarfare
May 21, 2012, 05:10 PM
I'm glad you got it working. What IDE are you using ? Perhaps thats why it did not work for you. I tried on Xcode and VS2010 and it was able to output everything properly in both using my code.

Exactly as you stated however, when I was debugging everything was set to -1 and that was what alerted me to an issue with the reading of the file.

I wrote the code in gedit and compiled it in terminal with

g++ -Wall -g -DNDEBUG HW#2.cpp -o HW#2exe

In the interest of becoming more comfortable with terminal, I use it instead of Xcode when working on school/"personal learning" assignments. (When I'm working on bigger apps that I intend to submit to the iOS or Mac OS app store, I use Xcode.)

I'm using llvm-g++ 4.2, on a computer running Mac OS X 10.7.4. I use Xcode 4.3.2 and the command line tools are installed. 64 bit intel. I can't think of any other relevant data.

lloyddean
May 21, 2012, 06:07 PM
'NDEBUG' means NO DEBUG. To me it looks like you've gotten it's usage reversed.

Once you get more familiar with 'iostream''s in their many forms reading the parameters can be much simplified.

An example follows:


/* ==================================================================================================== =================
* File - main.cpp
* ---------------------------------------------------------------------------------------------------------------------
* <<http://forums.macrumors.com/showthread.php?t=1373702>
*
* Set working directory
* cd ~/Desktop/MacRumors\ Forums/SimplerFileIO
*
* Debug build
* clang++ -stdlib=libc++ -std=c++11 main.cpp
*
* Release build
* clang++ -stdlib=libc++ -std=c++11 -DNDEBUG main.cpp
*
* Run with
* ./a.out
*/

/* ==================================================================================================== =================
* ---------------------------------------------------------------------------------------------------------------------
*/

//#define NDEBUG

#include <algorithm>
#include <iostream>
#include <fstream>
#include <string>
#include <sstream>
#include <vector>

#include <cmath>


/* ==================================================================================================== =================
* ---------------------------------------------------------------------------------------------------------------------
*/

#ifdef NDEBUG
#define DebugString(OS, PSTR)
#else
inline void DebugString(std::ostream& os, const char* const psz) {
using std::endl;

os << psz << endl;
}
#endif


/* ==================================================================================================== =================
* ---------------------------------------------------------------------------------------------------------------------
*/

struct param_pair_t
{
long double _power;
long double _coefficient;
};

typedef std::vector<param_pair_t> parameters_t;
typedef std::vector<param_pair_t>::iterator parameters_itr;


/* ==================================================================================================== =================
* ---------------------------------------------------------------------------------------------------------------------
*/

const std::string strFilename("poly.txt");
const std::string strComment("//");


/* ==================================================================================================== =================
* ---------------------------------------------------------------------------------------------------------------------
* Support Routines
*/

// --- string 'trimming'

inline std::string& ltrim(std::string& str) {
str.erase(str.begin(), find_if(str.begin(), str.end(), std::not1(std::ptr_fun<int, int>(std::isspace))));
return str;
}

inline std::string& rtrim(std::string& str) {
str.erase(std::find_if(str.rbegin(), str.rend(), std::not1(std::ptr_fun<int, int>(std::isspace))).base(), str.end());
return str;
}

inline std::string& trim(std::string& str) {
return ltrim(rtrim(str));
}


// --- 'sort' comparator routines

inline bool sort_ascending(const param_pair_t& lfs, const param_pair_t& rhs) {
return (lfs._power < rhs._power);
}

inline bool sort_descending(const param_pair_t& lfs, const param_pair_t& rhs) {
return (lfs._power > rhs._power);
}


/* ==================================================================================================== =================
* ---------------------------------------------------------------------------------------------------------------------
*/

std::ostream& parameters_dump(std::ostream& os, parameters_t& parameters)
{
#if 1
// dump contents of read 'parameters' using 'iterator'
for ( parameters_itr itr = parameters.begin(); itr != parameters.end(); itr++ )
{
os << "power: " << (*itr)._power << " _coefficient: " << (*itr)._coefficient << std::endl;
}
#else
// dump contents of read 'parameters' using array syntax
for ( size_t i = 0; i < parameters.size(); i++ )
{
os << "power: " << parameters[i]._power << " _coefficient: " << parameters[i]._coefficient << std::endl;
}
#endif

return os;
}


/* ==================================================================================================== =================
* ---------------------------------------------------------------------------------------------------------------------
*/

int main(int argc, const char* const argv[])
{
DebugString(std::cout , "\n\n!!!Warning debug build!!!\n");

parameters_t paramsDB;

std::fstream ifs(strFilename.c_str());
if ( ifs.is_open() )
{
std::istringstream iss;
std::string str;
while ( getline(ifs, str) )
{
trim(str);

if ( strComment != str.substr(0, strComment.size()) )
{
iss.clear();
iss.str(str);

param_pair_t params;
iss >> params._power;
iss >> params._coefficient;
if ( iss )
{
paramsDB.push_back(params);
}
}
}

sort(paramsDB.begin(), paramsDB.end(), sort_descending);

parameters_dump(std::cout, paramsDB);

return EXIT_SUCCESS;
}

return EXIT_FAILURE;
}

ArtOfWarfare
May 21, 2012, 09:21 PM
'NDEBUG' means NO DEBUG. To me it looks like you've gotten it's usage reversed.

Why do you comment out the preprocessor lines? I thought the whole point of including #ifdef NDEBUG was so that you could easily change whether those lines were considered when you make.

So if I'd like to make with the lines, I can include -DNDEBUG, and if I don't want them, I just leave off that flag.

Regarding whether I have it backwards... I really don't know. No one else has commented on it to me yet. I copied the convention a lot of Apple's sample code seems to follow.

lloyddean
May 21, 2012, 09:37 PM
NDEBUG is used by the C/C++ assert library routines to enable or disable verbose assertion output.

<http://www.cplusplus.com/reference/clibrary/cassert/assert/>

lloyddean
May 22, 2012, 01:30 PM
So if I'd like to make with the lines, I can include -DNDEBUG, and if I don't want them, I just leave off that flag.

Your wording still makes it unclear if your saying defining NDEBUG enables extra debugging or disables it. Would you mind making your understanding more explicit?

During development the normal state is extra code not in the release build. So in building the release version one defines NDEBUG which removes debugging aides.

ArtOfWarfare
May 22, 2012, 01:46 PM
Your wording still makes it unclear if your saying defining NDEBUG enables extra debugging or disables it. Would you mind making your understanding more explicit?

During development the normal state is extra code not in the release build. So in building the release version one defines NDEBUG which removes debugging aides.

Interesting. I just reviewed your code and it makes some sense to me. I'll have to double check some Apple code to see if they do the same thing and I've just been misunderstanding their code (possibly overlooking "//#define NDEBUG" hidden somewhere in the code?)

Anyways, here's what I've done in the past:
- Lines that should only appear in the debug build of my app are surrounded by #ifdef NDEBUG and #endif.
- If I'd like to have a debug build of my app, I include the flag -DNDEBUG when building with g++.

- Lines that should appear in all builds of my app are not surrounded by any special preprocessor directives.
- I don't include any special flags to do the release build of my app.

lloyddean
May 22, 2012, 02:24 PM
It's normally done as '-DNDEBUG' option to the compiler but can be useful in debugging individual modules by placing in a source file here and there.

The ONLY reason I placed it into the example code was to make others aware of it for the conditionally compiled 'DebugString'. After all it's just a bunch of code with nothing to indicate compiler options required for debug vs release.