1

Trying to write a binary file from a struct. Unfortunately, it is writing out excessive spaces since my strings are all 20 characters long. I would use pointers, but my textbook specifically states not to use pointers (i.e. Remember: Only use fixed-size data members when writing to a binary file. Do not use pointers or classes that contain pointers as data members).

To make matters worse, the numbers I am trying to print out (i.e. 8 and 40) just appear as an 'A' and 'B'.

The output looks like this

Employee.dat (Binary file I am writing to)

 Pauline             Nordin              A    B

I want it to look like this, however.

Pauline Nordin 8.00 40.00

Here is my code:

Excerpt from protocol.cpp

    //Open file
    std::ifstream BinaryOpen("employee.txt", std::ios::in /*| std::ios::out*/ | std::ios::binary);

    //Check if file is open
    if(BinaryOpen.is_open())
    {
        //Priming read
        BinaryOpen >> favoriteEmployees[numberOfEmployees].firstName;
        BinaryOpen >> favoriteEmployees[numberOfEmployees].lastName;
        BinaryOpen >> favoriteEmployees[numberOfEmployees].hourlyWage;
        BinaryOpen >> favoriteEmployees[numberOfEmployees].hoursWorked;

        numberOfEmployees++;
        //Read file
        while(!BinaryOpen.eof())
        {
            BinaryOpen >> favoriteEmployees[numberOfEmployees].firstName;
            BinaryOpen >> favoriteEmployees[numberOfEmployees].lastName;
            BinaryOpen >> favoriteEmployees[numberOfEmployees].hourlyWage;
            BinaryOpen >> favoriteEmployees[numberOfEmployees].hoursWorked;

            numberOfEmployees++;

            //Close file
            BinaryOpen.close();
        }

        //Write to binary file
        std::ofstream BinaryWrite("employee.dat", std::ios::out | std::ios::binary);

        //Check if file opened
        if(BinaryWrite.is_open())
        {
            BinaryWrite.write(reinterpret_cast <char *>(favoriteEmployees),
                              sizeof(Employee) * numberOfEmployees);
            //Close file
            BinaryWrite.close();
        }
        else
            std::cout << "\nWrite file did not open! " << std::endl;
    }
    else
        std::cout << "\nFile did not open! " << std::endl;
    }

Below are all the files for my program:

employee.txt (i.e. file I am reading from)

Pauline Nordin 8.00 40.00

main.cpp

#include <iostream>
#include "protocol.h"
#include "employee.h"

int main()
{
int menuChoice = 0;
int numberOfEmployees = 0;

//Create array of employees
Employee favoriteEmployees[NUMBER_OF_EMPLOYEES];

    //To prevent garbage being printed out
for(int i = 0; i < BUFFER_LENGTH; i++)
{
    favoriteEmployees[0].firstName[i] = 0;
    favoriteEmployees[0].lastName[i] = 0;
    favoriteEmployees[0].hourlyWage = 0;
    favoriteEmployees[0].hoursWorked = 0;
}

PrintMenu();
GetMenuChoice(menuChoice);
ExecuteMenuChoice(menuChoice, favoriteEmployees, numberOfEmployees);

return 0;
}

protocol.h

#ifndef PROTOCOL_H
#define PROTOCOL_H

#include "employee.h"

const int NUMBER_OF_EMPLOYEES = 10;

//Function declarations
void PrintMenu();
void GetMenuChoice(int &menuChoice);
void ExecuteMenuChoice(int menuChoice, Employee favoriteEmployees[], int &numberOfEmployees);

#endif

protocol.cpp

#include <fstream>
#include <iostream>
#include "employee.h"
#include "protocol.h"

//Function definitions
void PrintMenu()
{
std::cout << "\n\nChapter 17 -- Learn By Doings " << std::endl;
std::cout << "\n1. Learn By Doing 17.2 " << std::endl;
std::cout << "2. Learn By Doing 17.3 " << std::endl;
std::cout << "3. Learn By Doing 17.4 " << std::endl;
std::cout << "4. Exit " << std::endl;
std::cout << ' ' << std::endl;
}

void GetMenuChoice(int &menuChoice)
{
std::cin >> menuChoice;
}

void ExecuteMenuChoice(int menuChoice, Employee favoriteEmployees[], int &numberOfEmployees)
{
switch(menuChoice)
{
case 1:
    {
        //Open file in append mode
        std::ifstream BinaryOpen("name.txt", std::ios::app | std::ios::binary);

        //Open file in write mode
        /*std::ofstream BinaryOpen("name.txt", std::ios::out | std::ios::binary);*/

        //Check if file is open
        if(BinaryOpen.is_open())
        {
            //Perform appropriate file operatings
            std::cout << "\nFile opened! " << std::endl;

            //Close file
            BinaryOpen.close();
        }
        //Else
        else
            std::cout << "\nFile did not open! " << std::endl;
    }
    break;
case 2:
    {
    //Open file
        std::ifstream BinaryOpen("employee.txt", std::ios::in /*| std::ios::out*/ | std::ios::binary);

    //Check if file is open
    if(BinaryOpen.is_open())
    {
        //Priming read
        BinaryOpen >> favoriteEmployees[numberOfEmployees].firstName;
        BinaryOpen >> favoriteEmployees[numberOfEmployees].lastName;
        BinaryOpen >> favoriteEmployees[numberOfEmployees].hourlyWage;
        BinaryOpen >> favoriteEmployees[numberOfEmployees].hoursWorked;

        numberOfEmployees++;
        //Read file
        while(!BinaryOpen.eof())
        {
            BinaryOpen >> favoriteEmployees[numberOfEmployees].firstName;
            BinaryOpen >> favoriteEmployees[numberOfEmployees].lastName;
            BinaryOpen >> favoriteEmployees[numberOfEmployees].hourlyWage;
            BinaryOpen >> favoriteEmployees[numberOfEmployees].hoursWorked;

            numberOfEmployees++;

            //Close file
            BinaryOpen.close();
        }
        //Write to binary file
        std::ofstream BinaryWrite("employee.dat", std::ios::out | std::ios::binary);

        //Check if file opened
        if(BinaryWrite.is_open())
        {
            BinaryWrite.write(reinterpret_cast <char *>(favoriteEmployees),
                              sizeof(Employee) * numberOfEmployees);
            //Close file
            BinaryWrite.close();
        }
        else
            std::cout << "\nWrite file did not open! " << std::endl;
    }
    else
        std::cout << "\nFile did not open! " << std::endl;
    }
    break;
case 3:
    break;
case 4:
    break;
default:
    std::cout << "\nInvalid input.  Please enter an integer from 1 to 4. " << std::endl;
}
}

**employee.h**
#ifndef EMPLOYEE_H
#define EMPLOYEE_H

const int BUFFER_LENGTH = 20;

struct Employee
{
char firstName[BUFFER_LENGTH];
char lastName[BUFFER_LENGTH];
float hourlyWage;
float hoursWorked;
};

#endif
MrPickle5
  • 522
  • 4
  • 9
  • 31
  • firstly A and B may be the character reprisentation of the numbers you are storing. secondly its prob storing 1 char for each character in the char array so there would be some space. If you wanted to force it to use some smarts you could try to store only the active chars and then use 1 char to store the string size for when your reading. – Sellorio Mar 01 '13 at 01:19
  • @MrUniverse How would I only store the active chars?? – MrPickle5 Mar 01 '13 at 01:25
  • Like a said, you know what the real length is im assuming. 2 ways: ugly way is you make another char array of the known size and copy the active chars, way 2 is to manually ostream each active char 1 by one after one another. In both cases, start with a char with the value = the number of active characters in the following string. That way when you read the file you knwo how far to go before getting the next 'word' – Sellorio Mar 01 '13 at 02:27

3 Answers3

0

That is probably a bad way to do this. Like Mr. Universe was saying in the comments those arrays have all those additional blanks in them that have no data. I would change the case 2 code to something more like the following:

//#include <iomanip> for the stream formatting used when printing to the file
case 2:
    {
        //Open file
        std::ifstream BinaryOpen("employee.txt", std::ios::in /*| std::ios::out*/ | std::ios::binary);

        //Check if file is open
        if(BinaryOpen.is_open())
        {
            //Read file
            char eol[10];
            while(BinaryOpen.good())
            {
                BinaryOpen >> favoriteEmployees[numberOfEmployees].firstName;
                BinaryOpen >> favoriteEmployees[numberOfEmployees].lastName;
                BinaryOpen >> favoriteEmployees[numberOfEmployees].hourlyWage;
                BinaryOpen >> favoriteEmployees[numberOfEmployees].hoursWorked;
                BinaryOpen >> eol;

                numberOfEmployees++;
            }

            //Close file
            BinaryOpen.close();

            //Write to binary file
            std::ofstream BinaryWrite("employee.dat", std::ios::out | std::ios::binary);

            //Check if file opened
            if(BinaryWrite.is_open())
            {
                for (int i = 0; i < numberOfEmployees; i++)
                {
                    BinaryWrite << favoriteEmployees[i].firstName << " ";
                    BinaryWrite << favoriteEmployees[i].lastName << " ";
                    BinaryWrite << setiosflags(std::ios::fixed) << std::setprecision(2) << favoriteEmployees[i].hourlyWage << " ";
                    BinaryWrite << setiosflags(std::ios::fixed) << std::setprecision(2) << favoriteEmployees[i].hoursWorked << std::endl;
                }

                //Close file
                BinaryWrite.close();
            }
            else
                std::cout << "\nWrite file did not open! " << std::endl;
        }
        else
            std::cout << "\nFile did not open! " << std::endl;
    }
    break;
ThePosey
  • 2,734
  • 2
  • 19
  • 20
0

As in my comment above. I have whipped up a quick example which will give you an idea of how to do this.

void SaveString(fstream& file_stream, string output)
{
    // Note that any good string class will have an implicit conversion to char*
    if (output.size() == 0)
        return;

    file_stream << (unsigned char)output.size();

    for (unsigned char i=0; i<output.size(); i++)
        file_stream << output[i];

    // DONE (in theory)
}

Then all you need to do for the get is get 1 unsigned char at the start of your strings and then use that number in a for loop to getting chars and adding them to the string your loading into.

nneonneo
  • 171,345
  • 36
  • 312
  • 383
Sellorio
  • 1,806
  • 1
  • 16
  • 32
0

This is why it's printing garbage:

BinaryWrite.write(reinterpret_cast <char *>(favoriteEmployees),
    sizeof(Employee) * numberOfEmployees);

Those probably aren't spaces you are seeing, but NULL characters or other unprintables.

What you need to do is write the file fields:

BinaryWrite << favoriteEmployees[i].firstName << " ";
BinaryWrite << favoriteEmployees[i].lastName << " ";
BinaryWrite << std::setprecision(2) << favoriteEmployees[i].hourlyWage << " ";
BinaryWrite << std::setprecision(2) << favoriteEmployees[i].hoursWorked << std::endl;

It's probably a bit misleading to call these binary files, because they really just contain text. An actual binary file would contain fixed-length, length-prefixed or zero-terminated strings, and binary representations of numbers (which you inadvertently did in your original code).

nneonneo
  • 171,345
  • 36
  • 312
  • 383