tags:

views:

500

answers:

3

I've tried the following code with both normal ifstreams and the current boost:iostream I'm using, both have the same result.

It is intended to load a file from physfs into memory then pass it to a handler to process (eg Image, audio or data). Currently when c_str is called it only returns a small part of the file.

     PhysFS::FileStream file("Resources/test.png" , PhysFS::OM_READ);

 if(file.is_open()) {

  String* theFile;

  theFile = new String((std::istreambuf_iterator<char>(file)), 
  std::istreambuf_iterator<char>());

  String::iterator it;
  for ( it=theFile->begin() ; it < theFile->end(); it++ ) {
   std::cout << *it; 
  } // Outputs the entire file

  std::cout << theFile->c_str(); // Outputs only the first few lines

 }

The iterator loop outputs the entire png file as expected, but the c_str call only returns the first few characters (\211PNG).

I've been trying variations of this code for quite some time with no success. Any ideas?

+14  A: 

I imagine that the next character is a null (ASCII 0) byte. c_str() simply gives you a *char, therefore your write to stdout is interpreted as a class C string which ends at the first null byte.

If you really need a C-like interface to this string, the main thing is that theFile->c_str() points to your data and theFile.length gives you the number of characters in the string. So you might want to do something like this:

char *c_value = theFile->c_str()
for (int i = 0; i < theFile.length; i++)
{
   cout << c_value[i];
}

The real solution depends on why you are converting to a char * in the first place. If you are calling a legacy function that only accepts char *, there is likely also a length argument to that legacy function.

catfood
How would I go about fixing that? I don't have much experience with C style strings
Simie
Don't use c_str() - manipulate using std::string
Mark
I'm using a library which needs a c string input.
Simie
I updated my answer. Simie, you want to check that library function to see if it accepts a string-length argument. If it doesn't, that library function might simply be unable to process a string with an embedded null character.
catfood
Thank you very much! :)
Simie
You made it to my blog today too. :-) http://blog.criticalresults.com/2009/10/12/strings-and-pointers/
catfood
If you're using a `(const char*, length)` pair, don't use `.c_str()` but `.data()`. `.c_str()` adds an extra \0, possibly copying the string in the process.
MSalters
Excellent point, MSalters. Thanks for adding that.
catfood
+2  A: 

One of the bytes is probably 0. That means end of string to cout when passing a char* (which c_str is)

Lou Franco
+2  A: 

I would consider using std::vector<unsigned char> instead of std::string for this. It is a lot easier to deal with binary data in a vector. You can reference the underlying pointer using &vec[0] if you need access to a C-style array. I would also make sure that your file abstraction use std::ios_base::binary for the file mode under the hood as well.

D.Shawley
VERY good point about setting binary mode on the input.
catfood
As far as I can tell there is no option in PhysFS to open a file in binary mode. The lack of proper examples on how to use PhysFS is why I thought that it was a problem with me reading the file into memory instead of the display problems. :-(
Simie
Yeah, I was going to say that too: don't read the whole file into memory. Unless, I guess, you're quite certain it will be very small.
catfood
@Simie: I'm guessing that you are not allowed to use `std::ifstream` instead? or does `PhysFS` do something more than read a file.
D.Shawley
I'm using PhysFS for the archive support, multi-directory search paths@catfood: I wasn't aware there was a way to stream textures? How else would you do it?
Simie