To read data from a file, I create heap memory then pass the variable pointer to a function so fread() will put the file data into the pointer. But when the function returns, there is no data in the new created memory.
int main(...) {
MyFile File;
File.Open(...);
int filesize = File.Tell();
char* buffer = new buffer[filesize]; // Create some memory for the data
File.Read((char**)&buffer);
// Now do something with the buffer. BUT there is trash in it.
File.Close();
delete [] buffer;
}
size_t File::Read(void* buf) {
...
::fseek(fStream, 0, SEEK_END);
int fileSize = ::ftell(fStream); // Get file size.
::fseek(fStream, 0, SEEK_SET);
::fread(buf, 1, fileSize, fStream);
return (fileSize);
}
Yes, I can put char * myBuffer = new char[fileSize]; inside of File::Read(...) before ::fread(myBuffer, 1, fileSize, fStream);,
but I should not have to do this because I already have heap memory
(buffer) in main().
You're reading your file contents into the pointer buffer, not the array it points to.
You're overcomplicating things anyway. You don't need a pointer to a pointer, or a void*. You can simply pass a char* to Read. You should really also pass the size of the buffer pointed to into Read as well. Otherwise you risk overflowing your buffer.
int main() {
MyFile File;
File.Open(/*.....*/);
int filesize = File.Tell()
char* buffer = new buffer[filesize]; // Create some memory for the data
File.Read(buffer, filesize);
// Now do something with the buffer. BUT there is trash in it.
File.Close();
delete [] buffer;
}
size_t File::Read(char* buf, size_t count) {
// ......
// No need to find the size of the file a second time
// Return the actual number of bytes read
return ::fread(buf, 1, count, fStream);
}
I changed my function to:
size_t nvFile::Read( char * pszBuffer, const size_t uiCount ) ...
Thank you Miles Budnek! I did not think enought of my problem. I am opening a binary file and it is a byte (char), so it not have to be void *. (I put on my 'cone-of-shame' for not thinking.)
Thank you for help and makeing me think more. :)
Related
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
So i have this function that reads file, allocates memory, and puts file's content into buffer. I constantly get garbage data in the end though, so i need a way to null terminate the buffer.
#include "GetText.h"
void GetText(const char* filename, char** buffer)
{
FILE* file = fopen(filename,"rb");
long file_lenght;
if(file)
{
fseek(file, 0, SEEK_END);
file_lenght = ftell(file);
rewind(file);
*buffer = (char*) malloc(file_lenght + 1);
fread(*buffer, 1, file_lenght, file);
*buffer[file_lenght] = '\0'; //This line crashed program
fclose(file);
}
}
Since the bracket operator has higher precedence than pointer dereference you need to dereference buffer before indexing, like this:
(*buffer)[file_lenght] = '\0';
In your program you also need to make sure malloc was successful.
I need to create a custom reading callback function that can read contents of a file in the form of a std::string into a uint8_t * buf. I tried multiple different methods found around the internet and on stackoverflow but sometimes it works and other the the program infinitely loops or stops execution half way.
I have no problems with amr/3gp files but all wav/pcm files are causing some problems for some reason. All I know its something to do with the reading function I have so far.
Ideally I would like to be able to give the program any type of file and then it converts it.
This is how I am calling the readCallback function from the code:
//create the buffer
uint8_t * avio_ctx_buffer = NULL;
//allocate space for the buffer using ffmpeg allocation method
avio_ctx_buffer = (uint8_t *) av_malloc(avio_ctx_buffer_size);
//Allocate and initialize an AVIOContext for buffered I/O.
//audio variable contains the contents of the audio file
avio_ctx = avio_alloc_context(avio_ctx_buffer, avio_ctx_buffer_size,0, &audio, &readCallback, NULL, NULL);
Here is the callback function that works on some types of files:
static int readCallback(void* opaque, uint8_t * buf, int buf_size){
std::string * file =static_cast<std::string *>(opaque);
if(file->length() == 0){
return AVERROR_EOF; //if we reach to the end of the string, return
// return End of file
}
// Creating a vector of the string size
std::vector<uint8_t> array(file->length());
//Copying the contents of the string into the vector
std::copy(file->begin(),file->end(),array.begin());
//Copying the vector into buf
std::copy(array.begin(),array.end(),buf);
return file->length();
}
After tyring some stuff for awhile, I got a solution using std::stringstream and it works well with several formats I tested with so far: 3gp/amr,wav/pcm,mp3.
Here a the snippet of code:
//Create a string stream that contains the audio
std::stringstream audio_stream(audio);
//create the buffer
uint8_t * avio_ctx_buffer = NULL;
//allocate space for the buffer using ffmpeg allocation method
avio_ctx_buffer = (uint8_t *) av_malloc(avio_ctx_buffer_size);
//Allocate and initialize an AVIOContext for buffered I/O.
//Pass the stringstream audio_stream
avio_ctx = avio_alloc_context(avio_ctx_buffer, avio_ctx_buffer_size,0,&audio_stream, &readCallback, NULL, NULL);
The callback function:
static int readFunction1(void* opaque, uint8_t * buf, int buf_size){
//Cast the opaque pointer to std::stringstream
std::stringstream * me =static_cast<std::stringstream *>(opaque);
//If we are at the end of the stream return FFmpeg's EOF
if(me->tellg() == buf_size){
return AVERROR_EOF;
}
// Read the stream into the buf and cast it to char *
me->read((char*)buf, buf_size);
//return how many characters extracted
return me->tellg();
}
I'm trying to read a jpg file from disk, and copy to a char buffer. The problem is that on the bytes there are some NULL character, and I'm having problems when I read the char buffer.
This is the current code:
char* readImg(char* filename)
{
FILE * pFile;
char jpgBuffer[20048];
long lSize;
errno_t result = fopen_s (&pFile,filename,"rb");
if (result != 0) {
printf("Error \n");
}
fseek (pFile , 0 , SEEK_END);
lSize = ftell (pFile);
rewind (pFile);
result = fread (jpgBuffer,1,lSize,pFile);
fclose (pFile);
jpgBuffer[lSize] = '\0';
return jpgBuffer;
}
and the call to the function is:
char* img = readImg("img.jpg");
then, I need to encode into base64, but if I want to know the size of the image buffer with strlen(), I'm getting a size of 4, because the 5 character is a "0".
How can I avoid the NULL characters into image buffer?
You may change your function prototype.
long readImage(const char* filename, char* buf, long bufSize)
{
FILE * pFile;
long lSize;
errno_t result = fopen_s (&pFile,filename,"rb");
if (result != 0) {
printf("Error \n");
}
fseek (pFile , 0 , SEEK_END);
lSize = ftell (pFile);
rewind (pFile);
if(bufSize < lSize)
{
printf("buf too small, needs %lu\n", lSize);
fclose(pFile);
return -1;
}
result = fread (buf,1,lSize,pFile);
fclose (pFile);
return lSize;
}
Then you get img data & actual size of it.
If you don't mind using malloc, you can alloc memory for buffer in your function.
long readImage(const char* filename, char** pbuf)
{
FILE * pFile;
long lSize;
errno_t result = fopen_s (&pFile,filename,"rb");
if (result != 0) {
printf("Error \n");
}
fseek (pFile , 0 , SEEK_END);
lSize = ftell (pFile);
rewind (pFile);
*pbuf = (char*)malloc(lSize * sizeof(char));
result = fread (*buf,1,lSize,pFile);
fclose (pFile);
return lSize;
}
call this function like following, and you need to remember free buffer.
char* buf = NULL;
long len = 0;
len = readImage(filename, &buf);
...
free(buf);
Use the lSize you determine in readImg(...) as the file size. strlen is for null terminated strings. dont use strlen to determine image size in bytes.
Note also, that you should assign jpgBuffer as a heap variable via new[] using lSize as the byte size. That way you can return a pointer to heap memory. Your current stack variable jpgBuffer will be invalid after the function returns, and hence the pointer to it. This way you also dont have to worry about the situation where you need more bytes than your hardcoded value (which you currently dont check!).
You will also want to return the lSize via an input parameter pointer/ref variable so you have a bounds to iterate over later on.
You can't use strlen to something that is not a string. You should return the size from the readIamge function. e.g.
char* readImg(char* filename, int *size);
If you program in C++ your should instead return an instance of a kind of an image class. This would avoid the splattered location of results.
Further you should never return the address of a local variable as your jpgBuffer. This variable will be overwritten aftern return from function.
You can return your data as a char* without any size information.
You can't return your jpgBuffer, locally allocated.
Change your function into:
int readImg(char* filename, unsigned char* buffer)
{
//...
result = fread (buffer,1,lSize,pFile);
return lSize;
}
If you can, allocate your buffer in the calling function, it will simplify your code.
However, it you want to avoid buffer overflow, you should pass the size of the allocated buffer to your read function, and return an error/throw an exception if the file size is greater than the size you allocated.
int readImg(char* filename, unsigned char* buffer, size_t aSize)
{
//...
fseek (pFile , 0 , SEEK_END);
lSize = ftell (pFile);
if (lSize > aSize)
{
// Manage error case
}
//...
result = fread (buffer,1,lSize,pFile);
return lSize;
}
Keep in mind that this way of coding is more C-style than C++-style. If you wish and can use C++, take advantage of C++ constructs, standard library to improve your code.
jpgBuffer is a "binary" buffer, not a "string". Put a null char at the end is non-sense. You should use the result variable to read your buffer.
None of the posted answers I've read work, so I'm asking again.
I'm trying to copy the string data pointed to by a char pointer into a char array.
I have a function that reads from a ifstream into a char array
char* FileReader::getNextBytes(int numberOfBytes) {
char *buf = new char[numberOfBytes];
file.read(buf, numberOfBytes);
return buf;
}
I then have a struct :
struct Packet {
char data[MAX_DATA_SIZE]; // can hold file name or data
} packet;
I want to copy what is returned from getNextBytes(MAX_DATA_SIZE) into packet.data;
EDIT: Let me show you what I'm getting with all the answers gotten below (memcpy, strcpy, passing as parameter). I'm thinking the error comes from somewhere else. I'm reading a file as binary (it's a png). I'll loop while the fstream is good() and read from the fstream into the buf (which might be the data array). I want to see the length of what I've read :
cout << strlen(packet.data) << endl;
This returns different sizes every time:
8
529
60
46
358
66
156
After that, apparently there are no bytes left to read although the file is 13K + bytes long.
This can be done using standard library function memcpy, which is declared in / :
strcpy(packet.data, buf);
This requires file.read returns proper char series that ends with '\0'. You might also want to ensure numberOfBytes is big enough to accommodate the whole string. Otherwise you could possibly get segmentation fault.
//if buf not properly null terminated added a null char at the end
buf[numberofbytes] = "\0"
//copy the string from buf to struc
strcpy(packet.data, buf);
//or
strncpy(packet.data, buf);
Edit:
Whether or not this is being handled as a string is a very important distinction. In your question, you referred to it as a "string", which is what got us all confused.
Without any library assistance:
char result = reader.getNextBytes(MAX_DATA_SIZE);
for (int i = 0; i < MAX_DATA_SIZE; ++MAX_DATA_SIZE) {
packet.data[i] = result[i];
}
delete [] result;
Using #include <cstring>:
memcpy(packet.data, result, MAX_DATA_SIZE);
Or for extra credit, rewrite getNextBytes so it has an output parameter:
char* FileReader::getNextBytes(int numberOfBytes, char* buf) {
file.read(buf, numberOfBytes);
return buf;
}
Then it's just:
reader.getNextBytes(MAX_DATA_SIZE, packet.data);
Edit 2:
To get the length of a file:
file.seekg (0, ios::end);
int length = file.tellg();
file.seekg (0, ios::beg);
And with that in hand...
char* buffer = new char[length];
file.read(buffer, length);
Now you have the entire file in buffer.
strlen is not a valid way to determine the amount of binary data. strlen just reads until it finds '\0', nothing more. If you want to read a chunk of binary data, just use a std::vector, resize it to the amount of bytes you read from the file, and return it as value. Problem solved.