How properly to read data from binary file to char array - c++

How can i properly write data from binary file to new char array.
I know that this question was asked several times here, but still i couldn't figure out how to do it properly.
This what i have so far..
struct Computer_Details {
char computer_type[99];
int release_year;
float price;
};
Computer_Details pc_details;
cout << "Enter Computer Type: ";
cin.getline(pc_details.computer_type, 255);
cout << "Enter Computer Release Date: ";
cin >> pc_details.release_year;
cout << "Enter Computer Price: ";
cin >> pc_details.price;
cout << "\n\n";
//Create File
ofstream file;
file.open("PC_Database.data", ios::binary | ios::app);
if (!file) cout << "Couldn't open file\n";
else {
file.write((char*)&pc_details, sizeof(Computer_Details));
file.close();
}
ifstream readFile;
readFile.open("PC_Database.data", ios::binary);
if (!readFile) cout << "Couldn't Open File\n";
else {
readFile.seekg(0, ios::end);
int fileSize = readFile.tellg();
int pcCount = fileSize / sizeof(Computer_Details);
readFile.seekg(0, ios::beg);
Computer_Details *pc_details = new Computer_Details[pcCount];
readFile.read((char*)pc_details, pcCount * sizeof(Computer_Details));
char *buff = new char[299];
for (int i = 0; i < pcCount; i++)
{
//write to buff char
}
readFile.close();
}

Try
std::ifstream input(szFileName, std::ios::binary);
data = std::vector<char>(std::istreambuf_iterator<char>(input),
(std::istreambuf_iterator<char>()));
char* charArray = &data[0];
size_t arraySize = data.size();
data vector's buffer is the needed char array. Its constructor's arguments are two iterators. The first one is the current reading position in ifstream (begin of stream in this case). The second one's constructor is default and it is treated as an end iterator.

Probably problem is size of your structure,
check sizes for structure and compare it to size of this structure:
struct Computer_Details {
char computer_type[100];
int release_year;
float price;
};
Same problem is when you trying to read/write structure which contains bool variable between two other types like int.
Try this :
readFile.read((char*)pc_details->computer_type, sizeof(Computer_Details::computer_type));
readFile.read((char*)pc_details->release_year, sizeof(Computer_Details::release_year));
readFile.read((char*)pc_details->price, sizeof(Computer_Details::price));
edit: look at examples in this comment: https://stackoverflow.com/a/119128/7981164

My guess is that you want to shove pc_details into the buff so you can send it somewhere and reconstruct the data.
If that is the case, you could do this:
for( int i=0; i < pcCount; i++ )
{
memcpy( buff, (char*)pc_details, sizeof(computer_details));
buff += sizeof(computer_details);
pc_details++;
}
However, when doing this you must be mindful of alignment and provide
padding accordingly. And your code should check your array bounds.

Related

How do I store a txt file into a char array?

I'm looking to store a txt file with 52 characters that have no spaces into a char array. What I have below only outputs garbage. I would appreciate on some insight on how to solve this.
`
int main()
{
fstream fin, fout;
int maxSize = 9999; // Max length for text file.
int sizeArray = 0; //Stores length of message.txt file.
char storeCharacter[maxSize]; //Array that stores each individual character.
fin.open("message.txt");
if(fin.fail())
{
cout << "Input file failed to open (wrong file name/other error)" << endl;
exit(0);
}
sizeArray = fileLength(fin, storeCharacter, maxSize); //Assigns size using fileLength function.
cout << sizeArray << endl;
char txtCharacters[sizeArray];
storeInArray(fin, txtCharacters, sizeArray);
for(int i=0; i<=sizeArray; i++)
{
cout << txtCharacters[i];
}
fin.close();
fout.close();
return 0;
}
int fileLength(fstream& fin, char storeCharacter[], int length)
{
char nextIn;
int i = 0;
fin >> nextIn;
while(!fin.eof())
{
storeCharacter[i] = nextIn;
i++;
fin >> nextIn;
}
return i; //returns the file size.
}
void storeInArray(fstream& fin, char arr[], int length)
{
int i = 0;
char nextIn;
while(!fin.eof() && i!=length )
{
fin >> nextIn;
arr[i] = nextIn;
i++;
}
}
`
I tried to use a while and for loop to store the txt file characters into a char array. I was expecting it to work since I have done a similar thing with a txt file full of integers. Instead garbage gets outputted instead of the contents of the text file.
first error here is that VLA is not a standard c++ feature. Do not use it
char txtCharacters[sizeArray];
also do not do
while(!fin.eof()
read Why is iostream::eof inside a loop condition (i.e. `while (!stream.eof())`) considered wrong?
next
fileLength reads to the end of the file but you do not rewind the file after that. This function loads the file into an array anyway so why the read it (or try to) into a second array.
also
for(int i=0; i<=sizeArray; i++)
you mean
for(int i=0; i<sizeArray; i++)
way simpler is to read into std::vector, no need to calculate initial size. Just push_back each char
From the world of old-school, we use fopen, fread and fclose:
#include <stdio.h>
int read_file(const char* path, char* data, int max_length)
{
FILE *fp = fopen(path, "rb");
if (!fp) return 0;
int n = fread(data, 1, max_length, fp);
fclose(fp);
return n;
}
int main()
{
char data[1024] = { };
int l = read_file("message.txt", data, 1024);
printf("length = %d\n", l);
printf("text = %s\n", data);
return 0;
}
For the following message.txt (the alphabet twice with a trailing new line character, i.e. 26 + 26 + 1 = 53 bytes)
ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz
I get the following output:
length = 53
text = ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz
Somethings you'll note:
The read_file is implemented as a refactor of fopen, fread and fclose
We open the file in read-only binary mode
If the file didn't exist or there was a reason why we couldn't open, we early exit with 0 bytes read
We read up to a maximum of max_length and return the actual bytes read
We make sure we close the file before exiting
In the main I declare data as 1024 bytes, i.e. 1K which is more than enough
I ensure that the data has been zero-initialized, so, if nothing populates it, it will contain NUL characters
I use printf statements to display what has been read
To do the same thing using std::ifstream, I would simply make use of std::string and std::getline as follows:
#include <iostream>
#include <fstream>
using namespace std;
int main()
{
ifstream fin("message.txt", ios::in | ios::binary);
string data, line;
if (fin.is_open()) {
while (getline(fin, line)) {
data += line + "\n";
}
fin.close();
}
cout << "length = " << data.length() << "\n";
cout << "text = " << data << "\n";
return 0;
}

I can't seem to figure out why my read/write to a file functions are not working

I've been doing my project and the last thing I need to do is to save to and start reading a structure array from a file on startup of the program, but I can't figure out why the code isn't loading the information of the file. I know that it does save something since I can open the .dat file and read it in a text editor.
I apologize for the terrible style, but I'm still new. That's a sample of just that function in the code:
#include <iostream>
#include <string>
#include<fstream>
using namespace std;
struct property {
int num;
char nBrok[50];
char type[10];
string adress;
char outlook[20];
double price;
double size;
int nRooms;
int floor;
int status;
};
fstream fp;
void fileWrite(property bDanni[], int n) {
fp.open("dbase.dat", ios::binary | ios::out);
if (!fp) {
cout << "\n Error in file \n"; exit(1);
}
fp.write((char*)bDanni, sizeof(property) *n);
fp.close();
}
int fileRead(property bDanni[]) {
long pos; int n = 0, i; property b;
fp.open("dbase.dat", ios::binary | ios::in);
if (!fp) {
cout << "\n file does not exist\n"; return n;
}
fp.seekg(0l, ios::end);
pos = fp.tellg();
fp.close();
n = pos / (sizeof(property));
fp.open("dbase.dat", ios::binary | ios::in);
if (!fp) {
cout << "\n Error in file \n"; exit(1);
}
for (i = 0; i < n; i++) {
fp.read((char*)&b, sizeof(property));
bDanni[i] = b;
}
fp.close();
return n;
}
int main() {
property bDanni[100];
char answer;
int total = 0;
cout << "Do you wat to read from the save file?(y/n): ";
cin >> answer;
if (answer == 'y') {
int total = fileRead(bDanni);
}
}
The problem is that a C++ std::string is much more complex than a char array. The implementation is not mandated by the standard, but in common implementation, the string element contains a pointer to a character array. That means that your code only stores into the file a pointer value instead of a character string.
In C++ idiom, the std::string type is said not to be trivially copyable. And the fread-fwrite method can only be used with trivially copyable types.
That means that you will have to use serialization to replace the raw byte representation of a std::string with a sequence of bytes that represent the useful content of the object, something that you will be able to use at read time to construct back the object. Not really complex but beyond a mere fwrite.

Convert binary file to hex notation

I would like to obtain this hex notation for a binary I enter in parameter:
The output I obtain and what I want:
This is the code I written, I don't have the good hex number (for the part after 5A) , what I am doing wrong ? How to convert properly the byte I read to hex ?
Thanks.
int main(int argc, char *argv[])
{
std::string parameter = "The\\Path\\To\My\exe.exe";
ifstream::pos_type size;
char * memblock;
ifstream file(parametre, ios::in | ios::binary | ios::ate);
if (file.is_open())
{
size = file.tellg();
memblock = new char[size];
file.seekg(0, ios::beg);
file.read(memblock, size);
file.close();
cout << "the complete file content is in memory" << endl;
string str = string(memblock, size);
string hexContent = "";
int maxColumn = 0;
std::stringstream ss;
int column = 0;
for (int i = 0; i < size; ++i)
{
ss << std::hex << (int)str[i];
if (column == 8)
{
ss << '\n';
column = 0;
}
column++;
}
std::string mystr = ss.str();
cout << mystr;
}
return 0;
}
Looks like char is signed on your system and you are the victim of sign extension. For example 0x90 is a negative, so when it's converted into an int, that negativity has to be carried through, resulting in 0xffffff90.
Solution
Read the file into unsigned char, or uint8_t from <cstdint> if it is available, instead of an array of char.
char * memblock;
becomes
uint8_t * memblock;
then
memblock = new char[size];
becomes
memblock = new uint8_t[size];
and don't convert it into a string later.
string str = string(memblock, size);
is pointless, you could just as easily have read from memblock, and undoes the unsigned-ness we've established earlier. Just read out of memblock
Do not forget to
delete[] memblock;
when you are done. That leads to
Better solution
Use a std::vector. It cleans up after itself.
std::vector<uint8_t> memblock(size);
file.seekg(0, ios::beg);
file.read(reinterpret_cast<char*>(memblock.data()), size);
//or file.read(reinterpret_cast<char*>(&memblock[0]), size); if no or data method

Problems with fstream reads ok but not write user input string

I cannot find the problem in my code. readFile function works well, but writeFile function does not make any changes in the file:
#include <iostream>
#include <fstream>
using namespace std;
const int BUF_SIZE = 1024;
void readFile(fstream &file, char buffer[BUF_SIZE]);
void writeFile(fstream &file);
void readFile(fstream &file, char buffer[BUF_SIZE])
{
int position;
cout << "Please enter a position to read from the file some info" << endl;
cin >> position;
file.seekg(position, ios::beg);
file.read((char*) buffer, BUF_SIZE); // <<<<<
for(int i = 0; i < file.gcount(); i++){
cout << buffer[i];
}
}
void writeFile(fstream &file)
{
char temp[100] = "HHHH";
//cout << "Please enter some info to add to the file" << endl;
file.write((char*) &temp, 100);
for(int i = 0; i < file.gcount(); i++){
cout << temp[i];
}
}
int main(int argc, char const *argv[])
{
char buffer[BUF_SIZE];
if (argc != 2){
cout << "Program usage: prog_name file_name";
return 1;
}
fstream file(argv[1], ios::in | ios::out | ios::binary | ios::app);
if (!file){
cout << "File can not open or doesn't exist";
return 1;
}
//Try to read & write some info from/to file in particular position
readFile(file, buffer);
writeFile(file);
file.close();
return 0;
}
When I create a new ostream it works well, but I want to understand why fstream in/out mode works in my code only for reading.
I see several problems:
The reason behind the writing problem is probably because you reach the end of the file (is the file smaller than BUF_SIZE bytes?). This sets the EOF bit, which makes any write operations to fail. You have to clear that bit before (use the std::fstream::clear method):
void readFile(fstream &file, char buffer[BUF_SIZE])
{
int position;
cout << "Please enter a position to read from the file some info" << endl;
cin >> position;
file.seekg(position, ios::beg);
file.read(buffer, BUF_SIZE);
for(int i = 0; i < file.gcount(); i++){
cout << buffer[i];
}
file.clear(); // clears EOF
}
The line file.write((char*) &temp, 100); is wrong since you are actually passing a point to the temp variable, which is also a pointer, but it is camouflaged by the cast. These ones are OK: file.write(temp, 100); or file.write(&temp[0], 100);
When printing the written characters, you are using std::fstream::gcount, which literally means get count (amount of characters read in the last get operation). You are writing (put) not reading (get). Indeed, you are actually indicating how many bytes you are willing to write, so use it:
file.write(temp, 100);
for(int i = 0; i < 100; i++){
cout << temp[i];
}
Finally, you are always writing 100 characters, probably including some garbage from the buffer. As I see that you want to let the user choose what to write (the commented line), you can instead:
const size_t size = strlen(temp);
file.write(temp, size);
for(size_t i = 0; i < size; i++){
cout << temp[i];
}
In addition, some suggestions:
Use a std::string to read the user input, in this way you avoid a possible buffer overflow (if the user enters more than 100 characters).
cin.ignore(numeric_limits<streamsize>::max(), '\n'); // read the link bel
string temp;
getline(cin, temp); // need #include <string>
file.write(temp.c_str(), temp.size());
You will probably want to read this answer to learn more about the first line (basically it avoids the getline to be skipped after using cin >> position).
Avoid the for loop to print the user input. For both the buffer and the std::string options you can just cout << temp << endl;.

How to read a binary files and print 3's on screen?

I want to read a binary file of integer type and print the occurrence of the number of 3's in the file. I somehow wrote a program to open and read a binary file.
Here is the couple of problems I am facing:
If I try to print the file on my terminal, the execution continues
forever and the loop never ends.
I have no idea of how to filter out 3's from it.
Here is my code:
#include <iostream>
#include <fstream>
using namespace std;
int main () {
streampos size;
char * memblock;
ifstream file ("threesData.bin", ios::in|ios::binary|ios::ate);
if (file.is_open())
{
size = file.tellg();
memblock = new char [size];
file.seekg (0, ios::beg);
file.read (memblock, size);
file.close();
cout << "the entire file content is in memory";
for (int i = 0; i < size; i += sizeof(int))
{
cout << *(int*)&memblock[i] << endl;
}
delete[] memblock;
}
else
cout << "Unable to open file";
return 0;
}
Here is a way to implement your requirements:
int main()
{
unsigned int quantity = 0U;
ifstream file ("threesData.bin", ios::in|ios::binary|ios::ate);
uint8_t byte;
while (file >> byte)
{
if (byte == 3U)
{
++ quantity;
}
}
cout << "The quantity of 3s is: " << quantity << endl;
return 0;
}
The first step should always get a simple version working first. Only optimize if necessary.
Allocating memory for a file and reading the entire file is an optimization. For example, your platform may not have enough available memory to read the entire file into memory before processing.