0

I'm currently trying to write a bit of code to read a file and extract bits of it and save them as variables.

Here's the relevant code:

char address[10];

ifstream tracefile;
tracefile.open ("trace.txt");
tracefile.seekg(2, ios::beg);
tracefile.get(address, 10, ' ');
cout << address;

The contents of the file: (just the first line)

R 0x00000000

The issue I'm having is that address misses the final '0' because it puts a /0 character there, and I'm not sure how to get around that? So it outputs:

0x0000000

I'm also having issues with

tracefile.seekg(2, ios::cur);

It doesn't seem to work, hence why I've changed it to ios::beg just to try and get something work, although obviously that won't be useable once I try to read multiple lines after one another.

Any help would be appreciated.

GeorgeStorm
  • 15
  • 1
  • 8

3 Answers3

3

ifstream::get() will attempt to produce a null-terminated C string, which you haven't provided enough space for.

You can either:

  1. Allocate char address[11]; (or bigger) to hold a null-terminated string longer than 9 characters.
  2. Use ifstream::read() instead to read the 10 bytes without a null-terminator.

Edit:

If you want a buffer that can dynamically account for the length of the line, use std::getline with a std::string.

std::string buffer;
tracefile.seekg(2, ios::beg);
std::getline( tracefile, buffer );

Edit 2

If you only want to read to the next whitespace, use:

std::string buffer;
tracefile.seekg(2, ios::beg);
tracefile >> buffer;
Drew Dormann
  • 59,987
  • 13
  • 123
  • 180
  • As I said to Pete, making it bigger solves this issue but I think would introduce others :( I'll have a look at ifstream::read() thanks :) – GeorgeStorm Apr 28 '13 at 16:16
  • 1
    @user2329442 _Not_ providing a trailing `'\0'` will create a lot more problems downstream, if you're treating it as a string. If you want to treat the input as a string (which apparently you do, since that is what `char[]` generally means), then you _must_ either ensure that there is an additional '`'\0'` at the end, _or_ use `std::string` (generally the preferred solution). – James Kanze Apr 28 '13 at 16:25
  • The aim is for the program to read in the address, check to see if the cache contains the data at that address, if so read it if not then go to main memory and copy it into the cache (both the cache and main memory are arrays). So 'address' will be compared to the address variable of each of the entries in the 'cache' array. I quickly tried making address a string instead of char[] but then read/get didn't like it? – GeorgeStorm Apr 28 '13 at 16:33
  • I guess I could take the entire line, then split it if needed, since whilst some lines will be as per my OP, others will be like: W 0x00000800 0x00000000 And so obviously I would want to seperate the address and the data. – GeorgeStorm Apr 28 '13 at 16:37
  • Currently using your 2nd edit, and it's working perfectly :) (as far as I can tell so far) Thanks :) – GeorgeStorm Apr 28 '13 at 20:22
0

Make the buffer bigger, so that you can read the entire input text into it, including the terminating '\0'. Or use std::string, which doesn't have a pre-determined size.

Pete Becker
  • 74,985
  • 8
  • 76
  • 165
  • Thanks, I did do that, but then it introduces a problem with the next bit of code I'll need to write, which will be comparing address to an entry in an array (I'm coding a cache simulator) since there will be an extra character that I don't want, I should have mentioned that in the OP. – GeorgeStorm Apr 28 '13 at 16:16
  • Reading into a C-style text buffer puts a `'\0'` at the end of the input. That's how C strings work. If you're not interested in the `'\0'` afterwords, ignore it. – Pete Becker Apr 28 '13 at 16:19
  • @user2329442 Why will it cause a problem with comparing the address. You have a string. By definition, a C style string is `'\0'` terminated; _without_ the `'\0'`, you'll have very serious problems doing anything with your variable (i.e. functions like `strcmp` will not work). – James Kanze Apr 28 '13 at 16:22
0

There are several issues with your code. The first is that seekg( 2, ios::beg ) is undefined behavior unless the stream is opened in binary mode (which yours isn't). It will work under Unix, and depending on the contents of the file, it might work under Windows (but it could also send you to the wrong place). On some other systems, it might systematically fail, or do just about anything else. You cannot reliably seek to arbitrary positions in a text stream.

The second is that if you want to read exactly 10 characters, the function you need is istream::read, and not istream::get. On the other hand, if you want to read up to the next white space, using >> into a string will work best. If you want to limit the number of characters extracted to a maximum, set the width before calling >>:

std::string address;
//  ...
tracefile >> std::setw( 10 ) >> address;

This avoids all issues of '\0', etc.

Finally, of course, you need error checking. You should probably check whether the open succeeded before doing anything else, and you should definitely check whether the read succeeded before using the results. (As you've written the code, if the open fails for any reason, you have undefined behavior.)

If you're reading multiple lines, of course, the best solution is usually to use std::getline to read each line into a string, and then parse that string (possibly using std::istringstream). This prevents the main stream from entering error state if there is a format error in the line, and it provides automatic resynchronization in such cases.

James Kanze
  • 150,581
  • 18
  • 184
  • 329