Read whole std::ifstream to heap - c++

I cannot work out why this isn't working. From what I can tell, it doesn't appear to be reading the whole image file... Though I cannot tell. I basically have some raw image that I'd like to read onto the heap.
unsigned char* ReadImageFromFile(const char* FILENAME, unsigned int SIZE_BYTES)
{
unsigned char *data = (unsigned char*) malloc(SIZE_BYTES);
std::ifstream image(FILENAME);
image.read((char*) data, SIZE_BYTES);
image.close();
return data;
}

1) open the file in binary mode
2) don't return a raw pointer that needs to be freed
std::string readImageFromFile(const char* filename)
{
std::ifstream image(filename, std::ios::binary);
std::ostringstream data;
data << image.rdbuf();
return data.str();
}
Or if your prefer to write error-prone code (seems to be popular with the embedded crowd) you could do it this way:
char* readImageFromFile(const char* filename)
{
std::ifstream image(filename, std::ios::binary);
std::ostrstream data;
data << image.rdbuf();
data.freeze();
return data.str();
}
Of course there's a good reason strstreams are deprecated.

Try std::ifstream image(FILENAME, std::ios_base::binary); (note the second argument to ifstream constructor).

Related

Reading and Writing with char* to file

I am wondering how can you write data of type char*,int,double using char* and also reading a whole file line by line using again char* ? I know it can be done with std:string really beautiful but I am interested with char*. I have created a Write() method which writes char* successfully
but I don't know how to adjust it for ints and doubles, also I have started creating Read() method to read each line and save it to char* then print it to the console but I don't know how to implement it.
#include <iostream>
#include <fstream>
#include <cstring>
using namespace std;
void Write(char* fileName, char* pData)
{
ofstream file (fileName, ios::out | ios::app );
if (file.is_open())
{
size_t len = strlen(pData);
file.write(pData, len);
}
}
void Read(char* fileName, char* pData)
{
ifstream file(fileName, ios::in );
if(file.is_open())
{
file.read((char*)&pData, sizeof(pData));
file.close();
}
}
int main()
{
char* fileName = "E:\\cpp\\CarsIO\\data.txt";
char* data = "hello\n";
Write(fileName , data);
char* read = "";
Read(fileName , read);
return 0;
}
I have created a Write() method which writes char* successfully but I don't know how to adjust it for ints and doubles [...]
First of all, the function std::ofstream::write is intended for unformatted (binary) I/O. Since you are outputting text, it would be easier to use the formatted I/O functions, for example operator <<, like this:
void Write( char* fileName, char* pData )
{
ofstream file( fileName, ios::out | ios::app );
if ( file.is_open() )
{
file << pData;
}
if ( !file )
{
//TODO: handle error
}
}
In order for the function Write to print data of type int, you can simply create an appropriate overloaded function Write, like this:
void Write( char* fileName, int data )
{
ofstream file( fileName, ios::out | ios::app );
if ( file.is_open() )
{
file << data;
}
if ( !file )
{
//TODO: handle error
}
}
In order to make it also print the data type double, you can create an additional overloaded function for this data type. You can simply copy the function above and change int data to double data.
However, now you have 3 overloaded functions, one for the data type char *, one for int and one for double. That is a lot of unnecessary code duplication. It would be less messy to make a single template function, which can handle all three data types:
template <typename T>
void Write( char* fileName, T data )
{
ofstream file( fileName, ios::out | ios::app );
if ( file.is_open() )
{
file << data;
}
if ( !file )
{
//TODO: handle error
}
}
[...] also I have started creating Read() method to read each line and save it to char* then print it to the console but I don't know how to implement it.
The function std::ifstream::read is intended for unformatted (binary) input, not for formatted text input. Since you insist on using char* instead of std::string, I recommend that you use the function std::istream::getline in order to read exactly one line of text input, like this:
void Read( char* fileName, char* pData, std::streamsize count )
{
ifstream file(fileName, ios::in );
if(file.is_open())
{
file.getline( pData, count );
}
if ( !file )
{
//TODO: handle error
}
}
In your code, you were simply using sizeof(pData) to pass the size of the memory buffer. This will not work, because this will give you the size of the pointer pData (which is probably 4 or 8 bytes), instead of the size of the memory buffer. That is why the function Read must take an additional parameter which specifies the size of the memory buffer.
In your code, you are calling the function Read like this:
char* read = "";
Read(fileName , read);
This code is wrong, for two reasons:
You must ensure that the memory buffer is large enough to store the read data. This would be handled automatically when using std::string, but you must handle this yourself when using char*.
The line char* read = ""; makes the pointer read point to an (empty) string literal. String literals are read only. That is why C++ requires that pointers to string literals are declared as const char * instead of char *. You cannot pass a pointer to a read-only string literal to the function Read, because that function will attempt to write to that string literal, which causes undefined behavior.
In order to call the function Read and print the result, you can use the following code:
char buffer[100];
Read( fileName, buffer, sizeof buffer );
std::cout << buffer << "\n";

Read binary data stored in a vector into a struct

Consider the following code:
#pragma pack(2)
struct file_data {
uint8_t data0;
uint16_t data1;
uint8_t data2;
}
#pragma pack()
file_data readFile(const std::string& pathFile) {
file_data result;
std::ifstream(pathFile, std::ios::in | std::ios::binary);
if(!file.is_open()) return file_data();
file.read(reinterpret_cast<char*>(result), sizeof(file_data));
file.close();
return result;
}
int main(int argc, char* argv[]) {
file_data = readFile("the/path/to/the/file");
return 0;
}
In plain English, the code reads the file to the variable result, of type struct file_data, and returns it.
However, say I have already read the bytes of a file and stored them in a std::vector of type int8_t. I want to then write that data to an instance of file_data. I do not want to do this field by field, as the structure may change.
This has been my workaround:
file_data readData(const std::vector<int8_t>& bytes) {
std::stringstream ss;
for(int8_t byte : bytes) ss.write(reinterpret_cast<const char*>(&byte), sizeof(int8_t));
file_data result;
ss.read(reinterpret_cast<char*>(&result), sizeof(file_data));
return result;
}
So, first the vector is written back to a stream, then read into the struct.
I feel like this is highly inefficient, and there is some knowledge I am missing here. Can someone please provide a better solution to my readData() method?
Aside: int8_t might not be char, you should use it instead.
file_data readData(const std::vector<char>& bytes) {
file_data result;
std::copy_n(bytes.data(), sizeof(file_data), reinterpret_cast<char*>(&result));
return result;
}
This works similarly with std::string and std::array<char, N> (for N >= sizeof(file_data))

Writing data structure with char* and std::list to binary files

I am trying to write a data structure as such:
struct dataEntry
{
std::list<int> listTiles;
char* pData;
int nSize;
}
to a binary file.
I used ofstream to write to a binary file:
Write(char* fileName, const dataEntry& dataStruct)
{
ofstream binFile("fileName, ios::out | ios::binary | ios::trunc);
if(binFile.open())
{
binFile.write((char*)&dataStruct, sizeof(dataStruct));
binFile.close();
}
}
I used the same method to read back the binary file:
Read(char* fileName, const dataEntry& dataStruct)
{
ifstream binFile("fileName, ios::in| ios::binary );
if(binFile.open())
{
binFile.read((char*)&dataStruct, sizeof(dataStruct));
binFile.close();
}
}
However, i cannot iterate through the list after i read the binary file. It gave me an exception saying that the "list iterator outside range".
2nd problem is that when i tried to read the binary file the 2nd time, the "pData" is not what I have entered.
int Main()
{
char* name = "C:\\file.dat";
char* buf = "ABCDEFG";
dataEntry newData;
newData.listTiles.push_back(1);
newData.listTiles.push_back(2);
newData.nSize = 5;
newData.pData = buf;
Write(name, newData);
Read(name, newData);
buf = newData.pData; // wrong value when read 2nd time
newData.listTiles.remove(2); // crashed here
}
Check advanced c++ course on Udemy, the instructor said you cannot store pointers to binary files, because when you try to read them back the pointers (addresses) you used before writing them will not still be reserved for the list. So you have to save the data not the pointers

Reading Binary Files into an array of ints c++

I have a method which writes a binary file from an int array. (it could be wrong too)
void bcdEncoder::writeBinaryFile(unsigned int packedBcdArray[], int size)
{
fstream binaryIo;
binaryIo.open("PridePrejudice.bin", ios::out| ios::binary | ios::trunc);
binaryIo.seekp(0);
binaryIo.write((char*)packedBcdArray, size * sizeof(packedBcdArray[0]));
binaryIo.seekp(0);
binaryIo.close();
}
I need to now read that binary file back. And preferably have it read it back into another array of unsigned ints without any information loss.
I have something like the following code, but I have no idea on how reading binary files really works, and no idea how to read it into an array of ints.
void bcdEncoder::readBinaryFile(string fileName)
{
// myArray = my dnynamic int array
fstream binaryIo;
binaryIo.open(fileName, ios::in | ios::binary | ios::trunc);
binaryIo.seekp(0);
binaryIo.seekg(0);
binaryIo.read((int*)myArray, size * sizeof(myFile));
binaryIo.close();
}
Question:
How to complete the implementation of the function that reads binary files?
If you're using C++, use the nice std library.
vector<unsigned int> bcdEncoder::readBinaryFile(string fileName)
{
vector<unsigned int> ret; //std::list may be preferable for large files
ifstream in{ fileName };
unsigned int current;
while (in.good()) {
in >> current;
ret.emplace_back(current);
}
return ret;
}
Writing is just as simple (for this we'll accept an int[] but an std library would be preferable):
void bcdEncoder::writeBinaryFile(string fileName, unsigned int arr[], size_t len)
{
ofstream f { fileName };
for (size_t i = 0; i < len; i++)
f << arr[i];
}
Here's the same thing but with an std::vector
void bcdEncoder::writeBinaryFile(string fileName, vector<unsigned int> arr)
{
ofstream f { fileName };
for (auto&& i : arr)
f << i;
}
To simplify read operation consider storing size (i.e the number of elements in the array) before the data:
void bcdEncoder::writeBinaryFile(unsigned int packedBcdArray[], int size)
{
fstream binaryIo;
binaryIo.open("PridePrejudice.bin", ios::out| ios::binary | ios::trunc);
binaryIo.seekp(0);
binaryIo.write(&size, sizeof(size));
binaryIo.write((char*)packedBcdArray, size * sizeof(packedBcdArray[0]));
binaryIo.close();
}
The read would look something like:
void bcdEncoder::readBinaryFile(string fileName)
{
std::vector<unsigned int> myData;
int size;
fstream binaryIo;
binaryIo.open(fileName, ios::in | ios::binary | ios::trunc);
binaryIo.read(&size, sizeof(size)); // read the number of elements
myData.resize(size); // allocate memory for an array
binaryIo.read(myData.data(), size * sizeof(myData.value_type));
binaryIo.close();
// todo: do something with myData
}
Modern alternative using std::array
Here's a code snippet that uses more modern C++ to read a binary file into an std::array.
const int arraySize = 9216; // Hard-coded
std::array<uint8_t, arraySize> fileArray;
std::ifstream binaryFile("<my-binary-file>", std::ios::in | std::ios::binary);
if (binaryFile.is_open()) {
binaryFile.read(reinterpret_cast<char*>(fileArray.data()), arraySize);
}
Because you're using an std::array you'll need to know the exact size of the file during compile-time. If you don't know the size of the file ahead of time (or rather, you'll need to know that the file has at least X bytes available), use a std::vector and look at this example here: https://stackoverflow.com/a/36661779/1576548
Thanks for the tips guys, looks like I worked it out!! A major part of my problem was that half the arguments and syntax I added to the methods were not required, and actually messed things up. Here are my working methods.
void bcdEncoder::writeBinaryFile(unsigned int packedBcdArray[], int size, string fileName)
{
ofstream binaryIo;
binaryIo.open(fileName.substr(0, fileName.length() - 4) + ".bin", ios::binary);
if (binaryIo.is_open()) {
binaryIo.write((char*)packedBcdArray, size * sizeof(packedBcdArray[0]));
binaryIo.close();
// Send binary file to reader
readBinaryFile(fileName.substr(0, fileName.length() - 4) + ".bin", size);
}
else
cout << "Error writing bin file..." << endl;
}
And the read:
void bcdEncoder::readBinaryFile(string fileName, int size)
{
AllocateArray packedData(size);
unsigned int *packedArray = packedData.createIntArray();
ifstream binaryIo;
binaryIo.open(fileName, ios::binary);
if (binaryIo.is_open()) {
binaryIo.read((char*)packedArray, size * sizeof(packedArray[0]));
binaryIo.close();
decodeBCD(packedArray, size * 5, fileName);
}
else
cout << "Error reading bin file..." << endl;
}
With the AllocateArray being my class that creates dynamic arrays without vectors somewhat safely with destructors included.

Is there any possibility that this class may be unsafe?

I've made this class to read binary files and store their data.
FileInput.h:
#pragma once
#include <Windows.h>
#include <fstream>
using namespace std;
class FileInput
{
public:
FileInput(LPSTR Filename);
FileInput(LPWSTR Filename);
~FileInput();
operator char*();
explicit operator bool();
size_t Size;
private:
__forceinline void Read();
ifstream File;
char* Data;
};
FileInput.cpp
#include "FileInput.h"
FileInput::FileInput(LPSTR Filename)
{
File.open(Filename, ios::binary);
Read();
}
FileInput::FileInput(LPWSTR Filename)
{
File.open(Filename, ios::binary);
Read();
}
FileInput::~FileInput()
{
if (Data) delete[] Data;
}
FileInput::operator char*()
{
return Data;
}
FileInput::operator bool()
{
return (bool)Data;
}
void FileInput::Read()
{
if (!File)
{
Data = nullptr, Size = 0;
return;
}
File.seekg(0, ios::end);
Size = (size_t)File.tellg();
File.seekg(0, ios::beg);
Data = new char[Size];
File.read(Data, Size);
File.close();
}
Then I use it like this:
FileInput File(Filename); // This reads the file and allocates memory
if (!File) // This is for error checking
{
// Do something
}
if (File.Size >= sizeof(SomeType))
{
char FirstChar = File[0]; // Gets a single character
SomeStruct *pSomeStruct = reinterpret_cast<SomeStruct*>(&File[0]); // Gets a structure
}
So, is there any possibility that this class may be unsafe?
A reinterpret_cast<SomeStruct*>(&File) or other nonsense statement doesn't count.
EDIT: What I mean with unsafe is "to do unexpected or 'dangerous' things".
This is major overkill. If you want to copy a file into a buffer, you can use use an istreambuf_iterator:
std::ifstream inFile(fileName);
std::vector<char> fileBuffer ( (std::istreambuf_iterator<char>(inFile)),
std::istreambuf_iterator<char>() );
Then you can read from fileBuffer as necessary.