Why do structs disappear in my file when i write to it - c++

Whenever I write a struct to a binary file, any structs after it go away, although any structs written before it remain.
I'm opening the file stream in binary output mode and my structs contain only primitive datatypes.
I also made sure to make separate file streams for each operation.
Output:
create players
765
51
save 1
765
save 2
765
51
** struct definition**
struct player{
int UUID;
};
Function saving the structs
//updates player information in the player database
bool savePlayer(player playerData){
//count how manny playrs are in file
// Create our objects.
fstream countstream;
int count = 0;
countstream.open ("player.bin", ios::binary | ios::in);
if(countstream.is_open()){
countstream.seekg(0, ios::end); //set position to end
count = countstream.tellg()/sizeof(player);
//retuns number of players in file by getting
//the index of the position and dividing it by the size of each player
//no loops required :D
}
countstream.close();
bool found = false;
//if file is not empty,look through it
if(count > 0){
player playerTable[count];
fstream readstream;
readstream.open ("player.bin", ios::binary | ios::in);
//build table
for(int i = 0; i < count; i++){
readstream.seekg(i, ios::beg); //set position to end
readstream.read(reinterpret_cast <char *> (&playerTable[i]),
sizeof(player));
readstream.close();
}
//check table
for(int i = 0; i < count; i++){
if(playerTable[i].UUID == playerData.UUID){
found = true;
playerTable[i] = playerData;
}
}
//write table back to file
if(found){
fstream writestream; //create writestream
writestream.open ("player.bin", ios::binary | ios::out);
for(int i = 0; i < count; i++){
writestream.seekg(i, ios::beg); //set position to player
writestream.write(reinterpret_cast <char *> (&playerTable[i]),
sizeof(player));
if(!writestream.fail()){
writestream.close();
return true;
}
else{
writestream.close();
return false;
}
readstream.close();
}
}
}
//append if not found
if(!found){
fstream appendstream;
appendstream.open ("player.bin", ios::binary | ios::out |
ios::app);
appendstream.write(reinterpret_cast <char *> (&playerData),
sizeof(player));
appendstream.close();
if(!appendstream.fail()){
appendstream.close();
return true;
}
else{
appendstream.close();
return false;
}
}
return false;
}
Any suggestions will be appreciated.

You're making this much harder than necessary. You don't need to read all the data into an array. You can read each item into a variable, and check if it's the one you want to replace. And by opening the file in both read and write mode, you can simply overwrite that entry in the file while reading it.
bool savePlayer(player playerData){
player curPlayer;
fstream stream;
stream.open ("player.bin", ios::binary | ios::in | ios::out);
while(stream.read(reinterpret_cast<char*>(&curPlayer), sizeof(curPlayer))){
if (curPlayer.UUID == playerData.UUID) {
stream.seekg(-(sizeof curPlayer), ios::cur); // back up to location of current player
break;
}
}
stream.write(reinterpret_cast<char*>(&playerData), sizeof playerData);
stream.close();
return false;
}

Related

Huffman Decoding Function Uncompressing One Character Repeatedly

I have a program that produces a Huffman tree based on ASCII character frequency read in a text input file. The Huffman codes are stored in a string array of 256 elements, empty string if the character is not read. This program also encodes and compresses an output file.
I am now trying to decompress and decode my current output file which is opened as an input file and a new output file is to have the decoded message identical to the original text input file.
My thought process for this part of the assignment is to recreate a tree with huffman codes and then while reading 8 bits at a time, traverse through tree until I reach a leaf node where I will have updated an empty string(string answer) and then output it to my output file.
My problem: After writing this function I see that only one character in between all of the other characters of my original input file gets output repeatedly. I am confused as to why this is the case because I am expecting the output file to be identical to the original input file.
Any guidance or solution to this problem is appreciated.
(For encodedOutput function, fileName is the input file parameter, fileName2 is the output file parameter)
(For decodeOutput function, fileName2 is the input file parameter, fileName 3 is output file parameter)
code[256] is a parameter for both of these functions and holds the Huffman code for each unique character read in the original input file, for example, the character 'H' being read in the input file may have a code of "111" stored in the code array for code[72] at the time it is being passed to the functions.
freq[256] holds the frequency of each ascii character read or holds 0 if it is not in original input file.
void encodeOutput(const string & fileName, const string & fileName2, string code[256]) {
ifstream ifile;
ifile.open(fileName, ios::binary);
if (!ifile)
{
die("Can't read again");
}
ofstream ofile;
ofile.open(fileName2, ios::binary);
if (!ofile) {
die("Can't open encoding output file");
}
int read;
read = ifile.get();
char buffer = 0, bit_count = 0;
while (read != -1) {
for (unsigned b = 0; b < code[read].size(); b++) { // loop through bits (code[read] outputs huffman code)
buffer <<= 1;
buffer |= code[read][b] != '0';
bit_count++;
if (bit_count == 8) {
ofile << buffer;
buffer = 0;
bit_count = 0;
}
}
read = ifile.get();
}
if (bit_count != 0)
ofile << (buffer << (8 - bit_count));
ifile.close();
ofile.close();
}
// Work in progress
void decodeOutput(const string & fileName2, const string & fileName3, string code[256], const unsigned long long freq[256]) {
ifstream ifile;
ifile.open(fileName2, ios::binary);
if (!ifile)
{
die("Can't read again");
}
ofstream ofile;
ofile.open(fileName3, ios::binary);
if (!ofile) {
die("Can't open encoding output file");
}
priority_queue < node > q;
for (unsigned i = 0; i < 256; i++) {
if (freq[i] == 0) {
code[i] = "";
}
}
for (unsigned i = 0; i < 256; i++)
if (freq[i])
q.push(node(unsigned(i), freq[i]));
if (q.size() < 1) {
die("no data");
}
while (q.size() > 1) {
node *child0 = new node(q.top());
q.pop();
node *child1 = new node(q.top());
q.pop();
q.push(node(child0, child1));
} // created the tree
string answer = "";
const node * temp = &q.top(); // root
for (int c; (c = ifile.get()) != EOF;) {
for (unsigned p = 8; p--;) { //reading 8 bits at a time
if ((c >> p & 1) == '0') { // if bit is a 0
temp = temp->child0; // go left
}
else { // if bit is a 1
temp = temp->child1; // go right
}
if (temp->child0 == NULL && temp->child1 == NULL) // leaf node
{
ans += temp->value;
temp = &q.top();
}
ofile << ans;
}
}
}
(c >> p & 1) == '0'
Will only return true when (c >> p & 1) equals 48, so your if statement will always follow the else branch. The correct code is:
(c >> p & 1) == 0

c++ i get duplicated info when i read a binary file

Im writing a vector of size three and when i read i get a vector of size 4 with the last index being a duplicate of the index 2.
Heres my code.
void IOManager::WriteBin(const string &filename, vector<userRank> highScorers, int rank) {
ofstream fsalida(filename, ios::out | ios::binary);
if (fsalida.is_open())
{
for (int i = 0; i < highScorers.size();i++) {
fsalida.write(reinterpret_cast<char*>(&highScorers[i]), sizeof(highScorers[i]));
}
//highScorers.size() is 3
fsalida.close();
}else cout << "Unable to open file for writing\n";
}
vector<userRank> IOManager::ReadBin(const string &filename) {
ifstream fentrada(filename, ios::in | ios::binary);
if (fentrada.is_open())
{
vector<userRank>bestPlayers;
for (int i = 0; fentrada.good(); i++) {
userRank tempUser;
fentrada.read(reinterpret_cast<char*>(&tempUser), sizeof(tempUser));
bestPlayers.push_back(tempUser);
}
//bestPlayers.size() is 4!!!!!! Im losing my mind
fentrada.close();
return bestPlayers;
}
else cout << "Unable to open file for reading\n";
}
Here's my UserRank struct
struct userRank
{
char userName [5];
int score;
};
A wild userRank apperars for some reason, does anybody know why?
I suggest reorganizing the read function:
userRank tempUser;
for (int i = 0;
fentrada.read(reinterpret_cast<char*>(&tempUser), sizeof(tempUser));
i++)
{
bestPlayers.push_back(tempUser);
}
Search the internet for "stackoverflow c++ why eof in while is bad".

guessing datatype of data in .txt file

I have file with some data. I would like to decide what type of data there are: if everythng there is a number I want to push that to vector. But if there is even one data that is now a string I would like to push all of it to vector. I wrote something like this:
ifstream dataFile;
dataFile.open(fileName);
if(!dataFile.good()){
cout<<"File with data cannot be open"<<endl;
return 0;
}
cout<<"File open correctly..."<<endl;
bool isString = false;
vector<double> doubleData;
while (!dataFile.eof()){
double f;
dataFile>>f;
if(!dataFile.fail()){
doubleData.push_back(f);
}
else{
cout<<"it's string"<<endl; //(*) always
isString = true;
break;
}
}
if(isString){
dataFile.close(); //(**) reopen a file
dataFile.open(fileName);
vector<string> stringData;
while (!dataFile.eof()){
string s;
dataFile>>s;
if(!dataFile.fail()){
stringData.push_back(s);
}
}
cout<<"String data:"<<endl;
for (int i = 0; i< stringData.size();i++){
cout<<stringData[i]<<endl;
}
//showTime(stringData);
return 0;
}
cout<<"double data:"<<endl;
for (int i = 0; i< doubleData.size();i++){
cout<<doubleData[i]<<endl;
}
//showTime(doubleData);
return 0;
My code always reach line (*) at the end and therefore presume it's string data. I think it's when it reaches eof(). Am I right? How to fix it?
And one more: I need to re-open my file (*line (**)*) to read data into stringData properly. I can guess it is because I am at the end of file after previous while loop. Am I right? Is there any other, more elegant way to do this? Moving cursor in file to the beginning? I know there is something like fseek ( dataFile , 0 , SEEK_SET ); but I receive compiler error as my dataFile is ifstream instead of File*. Any suggestions?

Read file in as 32 bit binary data c++

I need to read a file in as binary data, making sure the file is an even number of 32 bit words (I can add padding bytes of 0s if I need).
I've fiddled around with ios::binary but is there something I'm missing? For instance:
string name1 = "first", name2 = "sec", name3 = "third";
int j = 0, k = 0;
ifstream ifs(name1.c_str());
ifs >> j;
ifs.close();
Is this something I need to utilize? I'm fairly new to the language.
std::ifstream ifs(name1.c_str(), std::ifstream::binary);
if (!ifs)
{
// error opening file
}
else
{
int32_t j;
do
{
j = 0;
ifs.read(&j, sizeof(j));
if (ifs)
{
// read OK, use j as needed
}
else
{
if (ifs.eof())
{
// EOF reached, use j padded as needed
}
else
{
// error reading from file
}
break;
}
}
while (true);
ifs.close();
}
I was able to read 32bits using a similar method as Remy Lebeau. This code is compatible with C++03.
#include <stdint.h>
#include <fstream>
// Rest of code...
std::ifstream file(fileName, std::ifstream::in | std::ifstream::binary | std::ifstream::beg);
int32_t word;
if(!file){
//error
} else {
while(file.is_open()){
if(file.eof()){ printf("END\n"); break; }
word = 0;
file.read((char*)&word,sizeof(word));
//Do something
printf("%d\n",word);
}
}
Note that I do not add padding if the file is not in exact increments of 32. I'll update the code if I add that functionality.

Having Open File Function Trouble

I'm new to c++ coding. I'm trying to write a function that opens specified ".txt" files(I fed up with coping/pasting multiple times).What I need to realize:
Specify filename;
read data and save to double(type) array;
return array;
As far as I understood, c++ can't return array, but it can return pointer. The problem is: how to use it? Any help will be appreciated. :)
P.S My draft code (it's working):
double arr[10];
fstream file;
file.open("input.txt");
if(file.is_open()){
while(file.good()){
for(int i = 0 ; i < 10 ; i++){
file >> arr[i];
}
}
file.close();
}else{
cout<<"[ERROR]: File \"input.txt\" wasn't found!"<<endl;
cout<<"[INFO]: Terminating program...";
Sleep(1000);
exit(0);
}
I dunno how to write as a function. Moreover I dunno how to use it
To start, try this:
std::vector<double> theFunction(const std::string &filename)
{
std::vector<double> arr(10);
std::fstream file(filename);
if (file)
{
for (int i = 0 ; i < 10 && file.good(); i++)
file >> arr[i];
}
return arr;
}
std::vector<double> result = theFunction("input.txt");
if (result.empty())
// Can not read the file