How to read and write a ppm file? - c++

I try to read a ppm file aand create a new one identical. But when I open them with GIMP2 the images are not the same.
Where is the problem with my code ?
int main()
{
FILE *in, *out;
in = fopen("parrots.ppm","r");
if( in == NULL )
{
std::cout<<"Error.\n";
return 0;
}
unsigned char *buffer = NULL;
long size = 0;
fseek(in, 0, 2);
size = ftell(in);
fseek(in, 0, 0);
buffer = new unsigned char[size];
if( buffer == NULL )
{
std::cout<<"Error\n";
return 0;
}
if( fread(buffer, size, 1, in) < 0 )
{
std::cout<<"Error.\n";
return 0 ;
}
out = fopen("out.ppm","w");
if( in == NULL )
{
std::cout<<"Error.\n";
return 0;
}
if( fwrite(buffer, size, 1, out) < 0 )
{
std::cout<<"Error.\n";
return 0;
}
delete[] buffer;
fcloseall();
return 0;
}
Before that I read the ppm file in a structure and when I wrote it I get the same image but the green was more intense than in the original picture. Then I tried this simple reading and writing but I get the same result.

int main()
Missing includes.
FILE *in, *out;
C style I/O in a C++ program, why? Also, declare at point of initialization, close to first use.
in = fopen("parrots.ppm","r");
This is opening the file in text mode, which is most certainly not what you want. Use "rb" for mode.
unsigned char *buffer = NULL;
Declare at point of initialization, close to first use.
fseek(in, 0, 2);
You are supposed to use SEEK_END, which is not guaranteed to be defined as 2.
fseek(in, 0, 0);
See above, for SEEK_SET not guaranteed to be defined as 0.
buffer = new unsigned char[size];
if( buffer == NULL )
By default, new will not return a NULL pointer, but throw a std::bad_alloc exception. (With overallocation being the norm on most current operating systems, checking for NULL would not protect you from out-of-memory even with malloc(), but good to see you got into the habit of checking anyway.)
C++11 brought us smart pointers. Use them. They are an excellent tool to avoid memory leaks (one of the very few weaknesses of C++).
if( fread(buffer, size, 1, in) < 0 )
Successful use of fread should return the number of objects written, which should be checked to be equal the third parameter (!= 1), not < 0.
out = fopen("out.ppm","w");
Text mode again, you want "wb" here.
if( fwrite(buffer, size, 1, out) < 0 )
See the note about the fread return value above. Same applies here.
fcloseall();
Not a standard function. Use fclose( in ); and fclose( out );.
A C++11-ified solution (omitting the error checking for brevity) would look somewhat like this:
#include <iostream>
#include <fstream>
#include <memory>
int main()
{
std::ifstream in( "parrots.ppm", std::ios::binary );
std::ofstream out( "out.ppm", std::ios::binary );
in.seekg( 0, std::ios::end );
auto size = in.tellg();
in.seekg( 0 );
std::unique_ptr< char[] > buffer( new char[ size ] );
in.read( buffer.get(), size );
out.write( buffer.get(), size );
in.close();
out.close();
return 0;
}
Of course, a smart solution would do an actual filesystem copy, either through Boost.Filesystem or the standard functionality (experimental at the point of this writing).

Related

Weird seek behaviour in C and C++ [duplicate]

I did a sample project to read a file into a buffer.
When I use the tellg() function it gives me a larger value than the
read function is actually read from the file. I think that there is a bug.
here is my code:
EDIT:
void read_file (const char* name, int *size , char*& buffer)
{
ifstream file;
file.open(name,ios::in|ios::binary);
*size = 0;
if (file.is_open())
{
// get length of file
file.seekg(0,std::ios_base::end);
int length = *size = file.tellg();
file.seekg(0,std::ios_base::beg);
// allocate buffer in size of file
buffer = new char[length];
// read
file.read(buffer,length);
cout << file.gcount() << endl;
}
file.close();
}
main:
void main()
{
int size = 0;
char* buffer = NULL;
read_file("File.txt",&size,buffer);
for (int i = 0; i < size; i++)
cout << buffer[i];
cout << endl;
}
tellg does not report the size of the file, nor the offset
from the beginning in bytes. It reports a token value which can
later be used to seek to the same place, and nothing more.
(It's not even guaranteed that you can convert the type to an
integral type.)
At least according to the language specification: in practice,
on Unix systems, the value returned will be the offset in bytes
from the beginning of the file, and under Windows, it will be
the offset from the beginning of the file for files opened in
binary mode. For Windows (and most non-Unix systems), in text
mode, there is no direct and immediate mapping between what
tellg returns and the number of bytes you must read to get to
that position. Under Windows, all you can really count on is
that the value will be no less than the number of bytes you have
to read (and in most real cases, won't be too much greater,
although it can be up to two times more).
If it is important to know exactly how many bytes you can read,
the only way of reliably doing so is by reading. You should be
able to do this with something like:
#include <limits>
file.ignore( std::numeric_limits<std::streamsize>::max() );
std::streamsize length = file.gcount();
file.clear(); // Since ignore will have set eof.
file.seekg( 0, std::ios_base::beg );
Finally, two other remarks concerning your code:
First, the line:
*buffer = new char[length];
shouldn't compile: you have declared buffer to be a char*,
so *buffer has type char, and is not a pointer. Given what
you seem to be doing, you probably want to declare buffer as
a char**. But a much better solution would be to declare it
as a std::vector<char>& or a std::string&. (That way, you
don't have to return the size as well, and you won't leak memory
if there is an exception.)
Second, the loop condition at the end is wrong. If you really
want to read one character at a time,
while ( file.get( buffer[i] ) ) {
++ i;
}
should do the trick. A better solution would probably be to
read blocks of data:
while ( file.read( buffer + i, N ) || file.gcount() != 0 ) {
i += file.gcount();
}
or even:
file.read( buffer, size );
size = file.gcount();
EDIT: I just noticed a third error: if you fail to open the
file, you don't tell the caller. At the very least, you should
set the size to 0 (but some sort of more precise error
handling is probably better).
In C++17 there are std::filesystem file_size methods and functions, so that can streamline the whole task.
std::filesystem::file_size - cppreference.com
std::filesystem::directory_entry::file_size - cppreference.com
With those functions/methods there's a chance not to open a file, but read cached data (especially with the std::filesystem::directory_entry::file_size method)
Those functions also require only directory read permissions and not file read permission (as tellg() does)
void read_file (int *size, char* name,char* buffer)
*buffer = new char[length];
These lines do look like a bug: you create an char array and save to buffer[0] char. Then you read a file to buffer, which is still uninitialized.
You need to pass buffer by pointer:
void read_file (int *size, char* name,char** buffer)
*buffer = new char[length];
Or by reference, which is the c++ way and is less error prone:
void read_file (int *size, char* name,char*& buffer)
buffer = new char[length];
...
fseek(fptr, 0L, SEEK_END);
filesz = ftell(fptr);
will do the file if file opened through fopen
using ifstream,
in.seekg(0,ifstream::end);
dilesz = in.tellg();
would do similar

How can I use fread to read in a file value by value?

I used fwrite to store some data and now I'm trying to use fread to read the data from the txt file for processing. I want to read the values individually but I can't figure out how you'd do that. This is what I have tried:
#include <stdio.h>
#include <stdlib.h>
int main ()
{
FILE * pFile;
long lSize;
unsigned short * buffer;
size_t result;
pFile = fopen ( "myfile.txt" , "rb" );
// obtain file size:
fseek (pFile , 0 , SEEK_END);
lSize = ftell (pFile);
rewind (pFile);
// allocate memory to contain the whole file:
buffer = (unsigned short *) malloc (sizeof(unsigned short)*lSize);
// copy the file into the buffer:
result = fread (buffer,1,lSize,pFile);
printf("%uz\n", result);
// terminate
fclose (pFile);
free (buffer);
return 0;
}
The above program compiles fine but when I run it with ./a.out I get a Segmentation fault. When I run it with sudo ./a.out I don't get seg fault but nothing prints out.
Any idea what I can do to fix it?
Problems I see:
Allocating more memory than needed
After
lSize = ftell (pFile);
lSize is set to the number of characters in the file, not the number of unsigned shorts. Hence, you need
buffer = malloc(lSize);
See Do I cast the result of malloc?. If you are using a C++ compiler (as your C++ tag seems to imply), you need to cast the return value of malloc.
Wrong format specifier
printf("%s\n", result);
uses the wrong format specifier to print result. You need to use
printf("%zu\n", result);
That line is the most likely culprit for the segmentation fault you are seeing.
Reading objects one by one
You can certainly use:
size_t count = lSize/sizeof(short);
for ( size_t i = 0; i < count; ++i )
{
unsigned short number;
result = fread (&number, sizeof(unsigned short), 1, pFile);
}
You can also use:
size_t count = lSize/sizeof(short);
for ( size_t i = 0; i < count; ++i )
{
result = fread (buffer+i, sizeof(unsigned short), 1, pFile);
}

C++: how can avoid the segmentation violation when I try to read a file?

I wrote this function, I want that simply reads a binary file and returns a vector with the content of the file. I follow some tutorial about how use "fread" but I have a segmentation violation. Can you help me to understand this?
const std::vector<uint32_t> read() {
//obtain file size
fseek( fBinaryFile, 0, SEEK_END );
long lsize = ftell( fBinaryFile );
rewind( fBinaryFile );
uint32_t pDataBuffer[sizeof( uint32_t )*lsize];
//read file
fread( pDataBuffer, sizeof( uint32_t ), lsize, fBinaryFile );
std::vector<uint32_t> cVector( pDataBuffer, pDataBuffer + sizeof( uint32_t )*lsize );
closeFile();
return cVector;
}
As already said in a comment to the question, you confuse the number of bytes in the file with the number of 32bit integers, resulting in reading garbage into the end of your vector. While this technically leads to undefined behavior, it is unlikely to cause the crash you describe. In fact, I don't see what is causing the crash in the code you show, but given the state of the code, I'll have to assume the code you do not show to contain similar problems.
This is C++. Why do you deal with C's file accessors? Reading a sequence of objects would be much easier with stream iterators.
Why do you first read into a buffer, and copy that into a vector, rather then reading into the vector right away?
I have carefully rewritten parts of your code to address these issues. HTH.
// CAUTION! Brain-compiled code ahead
std::vector<uint32_t> read()
{
std::fseek( fBinaryFile, 0, SEEK_END );
const long numBytes = std::ftell( fBinaryFile );
std::rewind( fBinaryFile );
std::vector<uint32_t> cVector;
cVector.resize( numBytes/sizeof(uint32_t) );
if( !cVector.empty() ) {
std::fread( &cVector[0], sizeof(uint32_t), lsize, fBinaryFile );
std::closeFile();
}
return cVector;
}
You might have noticed that I removed the const from the function's return type. This is because it is mostly useless.
First, variable length arrays are not part of the C++ standard, even if some compilers accept it as an extension.
There's a confusion in the number of elements and sizes that you process: lsize will be the length of the file in bytes, but you allocate an array of uint32_t of sizeof(uint32_t)*lsize elements. That's sizeof(uint32_t)*sizeof(uint32_t) bigger than the data available in the file ! Keep in mind that this size must be smaller than SIZE_MAX.
I suspect that there are some memory overflow problems. Therefore I'd recommed you to use standard constructs:
uint32_t *pDataBuffer = new uint32_t[sizeof( uint32_t )*lsize];
and take care of the possibility that there's not enough memory.
By the way, you try to read lsize uint32_t. So you read much more than currently available.
P.S: maybe you could consider calculating the size with ftell( fBinaryFile ) / sizeof(uint32_t) ? And also make sure that the size is not 0.
Assuming fBinaryFile was obtained with FILE *fBinaryFile = fopen(..., "rb");, and that you ensured that fBinaryFile is not NULL, the following should work :
FILE *fBinaryFile = fopen(..., "rb");
if (fBinaryFile == 0) {
throw ...; // do not proceed if file could not be opened
}
fseek( fBinaryFile, 0, SEEK_END );
long lsize = ftell( fBinaryFile );
rewind( fBinaryFile );
If your file contains lsize/4 uint32_t values:
lsize /= sizeof( uint32_t );
uint32_t pDataBuffer[lsize];
//read file
fread( pDataBuffer, sizeof( uint32_t ), lsize, fBinaryFile );
std::vector<uint32_t> cVector( lsize );
for(int i=0; i<lsize; i++) cVector[i] = pDataBuffer[i]
close(fBinary);
return cVector;
If you file contains lsize byte values than you want to process as uint32_t :
unsigned char pDataBuffer[lsize];
//read file
fread( pDataBuffer, sizeof( unsigned char ), lsize, fBinaryFile );
std::vector<uint32_t> cVector( lsize );
for(int i=0; i<lsize; i++) cVector[i] = pDataBuffer[i]
close(fBinary);
return cVector;

C++ Debug Assertion Error

So, I've been playing around with c++ a bit and decided to write a program that involves opening and writing to a file in binary mode. I am not too familiar with the iostream functionality of c++ (I mostly do API based programming), but I read several technical guides on the subject and wrote some code. The code is meant to open one file, read it's data to a buffer, and then convert that buffer to another format and write it to another file. The problem is that it keeps throwing a "Debug Assertion" error which apparently revolves around the invalid use of a null pointer. However, I couldn't make sense of it when I looked through the code. I probably just misused the iostream library or made a simple logic error. I need to have the separate SetMemBlock function as I plan on using the same base for formatting different output on a variety of functions. This is just my prototype. Anyways, here's my quick n' dirty class setup:
const DebugMode = true;
class A
{
public:
bool FileFunction( const char *, const char * );
protected:
bool SetMemBlock( char *, std::fstream &, std::streamoff & );
private:
std::fstream SrcFileStream;
std::fstream DestFileStream;
};
bool A::SetMemBlock( char* MemBlock, std::fstream & FileStream, std::streamoff & Size )
{
std::streamoff TempOff = 0;
//This is meant to check for a non-empty buffer and to see if the stream is valid.
if( MemBlock != 0 || !FileStream.is_open() )
return false;
TempOff = FileStream.tellg();
FileStream.seekg(0, std::ios::end);
Size = FileStream.tellg();
MemBlock = new( std::nothrow ) char[ (int) Size ];
if( MemBlock == 0 )
return false;
FileStream.seekg(0, std::ios::beg);
FileStream.read( MemBlock, (int) Size );
if( !FileStream )
return false;
FileStream.seekg(TempOff);
return true;
}
bool A::FileFunction( const char * SrcFile, const char * DestFile )
{
char * MemBlock = 0;
std::streamoff Size = 0;
SrcFileStream.open( SrcFile, std::ios::binary | std::ios::in );
DestFileStream.open( DestFile, std::ios::binary | std::ios::out );
if( !SrcFileStream.is_open() || !DestFileStream.is_open() )
return false;
if( DebugMode )
{
std::cout<<"Files opened succesfully...\nNow writing memory block..."<<std::endl;
}
if( !SetMemBlock( MemBlock, SrcFileStream, Size ) )
{
std::cout<<"An error occured when reading to memory block!"<<std::endl;
return false;
}
if( DebugMode )
{
std::cout<<"Memory block written..."<<std::endl;
}
DestFileStream.seekp( std::ios::beg );
DestFileStream.write( MemBlock, Size );
SrcFileStream.close();
DestFileStream.close();
delete[] MemBlock;
return true;
}
You're passing MemBlock to SetMemBlock by value. The function therefore just sets the value of a local copy, which has no effect on the calling function; the value of MemBlock in the calling function thus remains garbage. Using it as a pointer will probably then lead to an assertion (if you're lucky) or an out-and-out crash (if you're not.) You want to pass that argument by reference instead.
If you don't know what these terms mean, Google "pass by value" and "pass by reference". You really need to understand the difference!
Pass MemBlock by reference:
bool A::SetMemBlock( char*& MemBlock, std::fstream & FileStream, std::streamoff & Size )

Reading SDL_RWops from a std::istream

I'm quite surprised that Google didn't find a solution. I'm searching for a solution that allows SDL_RWops to be used with std::istream. SDL_RWops is the alternative mechanism for reading/writing data in SDL.
Any links to sites that tackle the problem?
An obvious solution would be to pre-read enough data to memory and then use SDL_RWFromMem. However, that has the downside that I'd need to know the filesize beforehand.
Seems like the problem could somehow be solved by "overriding" SDL_RWops functions...
I feel bad answering my own question, but it preocupied me for some time, and this is the solution I came up with:
int istream_seek( struct SDL_RWops *context, int offset, int whence)
{
std::istream* stream = (std::istream*) context->hidden.unknown.data1;
if ( whence == SEEK_SET )
stream->seekg ( offset, std::ios::beg );
else if ( whence == SEEK_CUR )
stream->seekg ( offset, std::ios::cur );
else if ( whence == SEEK_END )
stream->seekg ( offset, std::ios::end );
return stream->fail() ? -1 : stream->tellg();
}
int istream_read(SDL_RWops *context, void *ptr, int size, int maxnum)
{
if ( size == 0 ) return -1;
std::istream* stream = (std::istream*) context->hidden.unknown.data1;
stream->read( (char*)ptr, size * maxnum );
return stream->bad() ? -1 : stream->gcount() / size;
}
int istream_close( SDL_RWops *context )
{
if ( context ) {
SDL_FreeRW( context );
}
return 0;
}
SDL_RWops *SDL_RWFromIStream( std::istream& stream )
{
SDL_RWops *rwops;
rwops = SDL_AllocRW();
if ( rwops != NULL )
{
rwops->seek = istream_seek;
rwops->read = istream_read;
rwops->write = NULL;
rwops->close = istream_close;
rwops->hidden.unknown.data1 = &stream;
}
return rwops;
}
Works under the assumptions that istream's are never freed by SDL (and that they live through the operation). Also only istream support is in, a separate function would be done for ostream -- I know I could pass iostream, but that would not allow passing an istream to the conversion function :/.
Any tips on errors or upgrades welcome.
If you're trying to get an SDL_RWops struct from an istream, you could do it by reading the whole istream into memory and then using SDL_RWFromMem to get a struct to represent it.
Following is a quick example; note that it's unsafe, as no sanity checks are done. For example, if the file's size is 0, accessing buffer[0] may throw an exception or assert in debug builds.
// Open a bitmap
std::ifstream bitmap("bitmap.bmp");
// Find the bitmap file's size
bitmap.seekg(0, std::ios_base::end);
std::istream::pos_tye fileSize = bitmap.tellg();
bitmap.seekg(0);
// Allocate a buffer to store the file in
std::vector<unsigned char> buffer(fileSize);
// Copy the istream into the buffer
std::copy(std::istreambuf_iterator<unsigned char>(bitmap), std::istreambuf_iterator<unsigned char>(), buffer.begin());
// Get an SDL_RWops struct for the file
SDL_RWops* rw = SDL_RWFromMem(&buffer[0], buffer.size());
// Do stuff with the SDL_RWops struct