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

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".

Related

Why am i getting blank output after writing this filehandling code in c++?

I have made a tester class where I take questions from a question pool text file and put random questions from there to a docx file. I want to know why my code is giving me blank output in the docx file.
my random function is working fine. I am selecting two two questions from three questions file.
Here is my code - `
void test()
{
string line;
fstream question1("questiondesc.txt",ios::in | ios::out | ios::app);
fstream testgen("GeneratedTest.docx",ios::trunc | ios::in | ios::out);
testgen.open("GeneratedTest.docx");
if(!question1.is_open())
{
question1.open("questiondesc.txt");
}
int i,num;
for (i = 0; i < 2; i++) {
num = random(1,12);
for(int i =1;i<=num;i++)
{
getline(question1,line);
}
question1.clear();
question1.seekg(0, ios::beg);
testgen<<line<<endl;
}
question1.close();
ifstream question2("questionmcq.txt");
if(!question2.is_open())
{
question2.open("questionmcq.txt");
}
for (i = 0; i < 2; i++) {
num = random(1,26);
while(num%2==0)
{
num = random(1,26);
}
for(int i =1;i<=num;i++)
{
getline(question2,line);
}
testgen<<line<<endl;
getline(question2,line);
testgen<<line<<endl;
question2.clear();
question2.seekg(0, ios::beg);
}
question2.close();
ifstream question3("questionanalytical.txt");
if(!question3.is_open())
{
question3.open("questionanalytical.txt");
}
for (i = 0; i < 2; i++) {
num = random(1,12);
for(int i =1;i<=num;i++)
{
getline(question3,line);
}
question3.clear();
question3.seekg(0, ios::beg);
testgen<<line<<endl;
}
question3.close();
testgen.close();
}
There are errors in your code. I will show them as a comment in the below listing. Additionally I will show (onw of many, and maybe not the best ) solutions for your problem.
You should break down your problem into smaller pieces and design more functions. Then, life will be easier.
Additionally. You´should write comments. If you write comments, then you will detect the problems by yourself.
Your code with my remarks:
#include <iostream>
#include <fstream>
#include <string>
#include <random>
using namespace std; // NO NEVER USE
int random(int from, int to) {
std::random_device random_device;
std::mt19937 generator(random_device());
std::uniform_int_distribution<int> distribution(from, to);
return distribution(generator);
}
void test()
{
string line; // Line is not initialized an not needed here. Pollutes namespace
fstream question1("questiondesc.txt", ios::in | ios::out | ios::app); // Opening a file with these flags will fail. Use ifstream
fstream testgen("GeneratedTest.docx", ios::trunc | ios::in | ios::out);// Opening a file with these flags will fail. Use ofstream
testgen.open("GeneratedTest.docx"); // File was alread opened and failed. Reopening will not work. It failed alread
if (!question1.is_open()) // Use if "(!question1)" instead. There could be also other error bits
{ // Always check the status of any IO operation
question1.open("questiondesc.txt"); // Will never work. Failer already
}
int i, num; // Variable not initialized and not needed here. Name space pollution
for (i = 0; i < 2; i++) {
num = random(1, 12); // This function was not defined. I redefined it
for (int i = 1; i <= num; i++) // i=1 and i<= reaaly) not i=0 and i<num?
{
getline(question1, line); // Always check status of any IO function
}
question1.clear();
question1.seekg(0, ios::beg);
testgen << line << endl;
}
question1.close(); // The destructor of the fstream will close the file for you
ifstream question2("questionmcq.txt"); // Now you open the file as ifstream
if (!question2.is_open()) // Do check for all possible flags.: If (!question2)
{
question2.open("questionmcq.txt"); // Will not work, if it failed in the first time
}
for (i = 0; i < 2; i++) { // So 2 times
num = random(1, 26);
while (num % 2 == 0) // If numbers are equal
{
num = random(1, 26); // Get an odd number
}
for (int i = 1; i <= num; i++) // Usually from 0 to <num
{
getline(question2, line);
}
testgen << line << endl;
getline(question2, line);
testgen << line << endl;
question2.clear();
question2.seekg(0, ios::beg);
}
question2.close(); // No need to close. Destructor will do it for you
ifstream question3("questionanalytical.txt"); // Now you open the file as ifstream
if (!question3.is_open()) // Wrong check. Check for all flags
{
question3.open("questionanalytical.txt"); // Will not help in case of failure
}
// Now this is the 3rd time with the same code. So, put it into a function
for (i = 0; i < 2; i++) {
num = random(1, 12);
for (int i = 1; i <= num; i++)
{
getline(question3, line);
}
question3.clear();
question3.seekg(0, ios::beg);
testgen << line << endl;
}
question3.close();
testgen.close();
}
int main() {
test();
return 0;
}
And here one possible solution. With functions to handler similar parts of the code:
#include <iostream>
#include <string>
#include <fstream>
#include <random>
#include <vector>
#include <tuple>
// From the internet: https://en.cppreference.com/w/cpp/numeric/random/random_device
int random(int from, int to) {
std::random_device random_device;
std::mt19937 generator(random_device());
std::uniform_int_distribution<int> distribution(from, to);
return distribution(generator);
}
std::string readNthLineFromFile(std::ifstream& ifs, int n) {
// Reset file to the beginning
ifs.clear();
ifs.seekg(0, std::ios::beg);
// Default return string in case of error
std::string result{ "\n*** Error while reading a line from the source file\n" };
// If getline fails or ifs is in fail state, the string will be default
for (; std::getline(ifs, result) && (n != 0); n--);
// Give back the desired line
return result;
}
void generateQuestion(std::ifstream& sourceFileStream, std::ofstream& destinationFileStream, int n, const bool twoLines = false) {
// We want to prevent readin the same question again
int oldLineNumber = 0;
// For whatever reason, do this 2 times.
for (size_t i = 0U; i < 2; ++i) {
// If we want to read 2 consecutive lines, then we should not come up with the last kine in the file
if (twoLines & (n > 1)) --n;
// Get a random line number. But no duplicates in the 2 loops
int lineNumber{};
do {
lineNumber = random(1, n);
} while (lineNumber == oldLineNumber);
// For the next loop execution
oldLineNumber = lineNumber;
// Read the random line
std::string line{ readNthLineFromFile(sourceFileStream, lineNumber) };
// And write it to the destination file
destinationFileStream << line << "\n";
// If we want to read to lines in a row
if (twoLines) {
// Read next line
line = readNthLineFromFile(sourceFileStream, ++lineNumber);
// And write it to the destination file
destinationFileStream << line << "\n";
}
}
}
int main() {
const std::string destinationFilename{ "generatedTest.txt" };
const std::string questions1Filename{ "questiondesc.txt" };
const std::string questions2Filename{ "questionmcq.txt" };
const std::string questions3Filename{ "questionanalytical.txt" };
// Here we store the filenames and if one or 2 lines shall be read
std::vector<std::tuple<const std::string, const size_t, const bool>> source{
{ questions1Filename, 12U, false },
{ questions2Filename, 26U, true },
{ questions3Filename, 12U, false }
};
// Open the destination file and check, if it could be opened
if (std::ofstream destinationFileStream(destinationFilename); destinationFileStream) {
// Now open the first source file and generate the questions
for (const std::tuple<const std::string, const size_t, const bool>& t : source) {
// Open source file and check, if it could be opened
if (std::ifstream sourceFileStream(std::get<0>(t)); sourceFileStream) {
generateQuestion(sourceFileStream, destinationFileStream, std::get<1>(t), std::get<2>(t));
}
else {
std::cerr << "\n*** Error. Could not open source file '" << std::get<0>(t) << "'\n";
}
}
}
else {
std::cerr << "\n*** Error: Could not open destination file '" << destinationFilename << "'\n";
}
return 0;
}

Is it possible to write to multiple binary files in a loop

I am trying to open a binary file, read from it and then open multiple files with std::ofstreamand write into seperate files randomly. However, i get some garbage values in the written binary file. Is it not possible to write into multiple files paralel? What could be the cause of this issue?
Because when i create one ofstream and write everything, it seems okay. Here is my code for writing to binary:
//For reading from a binary file
std::ifstream fileInput;
fileInput.exceptions(std::ifstream::failbit | std::ifstream::badbit);
const int numberOfFiles = 5;
std::ofstream outfile[numberOfFiles];
std::stringstream sstm;
for (int i = 0; i < numberOfFiles; i++)
{
sstm.str("");
sstm << "test" << i;
outfile[i].open(sstm.str());
}
try
{
fileInput.open("TestBinary.dat", std::ios::binary);
float f;
int newLineCounter = 0;
int index = 0;
while (fileInput.read(reinterpret_cast<char*>(&f), sizeof(float)))
{
outfile[index].write(reinterpret_cast<const char*>(&f), sizeof(float));
newLineCounter++;
// Since i am reading 3D points
if (newLineCounter == 3)
{
index = rand() % numberOfFiles;
newLineCounter = 0;
}
}
for (int i = 0; i < numberOfFiles; i++)
{
outfile[i].close();
}
fileInput.close();
}
catch (std::ifstream::failure e) {
std::cerr << "Exception opening/reading/closing file\n";
}
when i read the file i get garbage values like this:
979383418452721018666090051403776.000000 500207915157676809436722056201764864.000000 2.16899e+17
You're opening your files in text mode. You need to open them in binary mode.
outfile[i].open(sstm.str(), ios_base::out | ios_base::binary);

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

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;
}

c++ seekp(0,ios::end) not working

Apologize for my poor English.
I am stuck by fstream in C++. Here is my code.
#include<iostream>
#include<fstream>
using namespace std;
struct TestStruct
{
int a;
char str[30];
TestStruct(int a_, const char* s)
{
a = a_;
strcpy_s(str,sizeof(char)*30, s);
}
TestStruct() = default;
};
int main()
{
fstream output("out.bin", ios::out|ios::binary);
output.seekp(0,ios::end);
cout << output.tellp() << endl;
for (int i = 0; i < 15; i++) {
TestStruct a(10*i, "asdadas");
output.write(reinterpret_cast<char*>(&a), sizeof(a));
}
output.close();
fstream input("out.bin", ios::in | ios::binary);
input.seekg(2 * sizeof(TestStruct), ios::beg);
for (int i = 0; i < 5; i++) {
TestStruct a;
input.read(reinterpret_cast<char*>(&a), sizeof(a));
cout <<"file_pointer"<<input.tellg()<<'\t'<<a.a << endl;
}
}
I use seekp(0,ios::end) to add new entry in the file. So the file should get lager when I run this code. But actually the file haven't change at all.
Here is the output:
> 0 <--tellp() always return 0
> file_pointer108 20
> file_pointer144 30
> file_pointer180 40
> file_pointer216 50
> file_pointer252 60
Add ios::app to the output's flags. You won't need to do output.seekp(0, ios::end); then.
While it may not seem like it seekp(0, ios::end) is actually working.
The reason it returns 0 is because you accidentally create a new empty file.
And the end cursor position of a new empty file is 0.
It creates a new file because of the file mode you use:
output("out.bin", ios::out|ios::binary);
https://en.cppreference.com/w/cpp/io/basic_filebuf/open

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