0

Entire question :

Question 3

You are the owner of a hardware store and need to keep an inventory that can tell you what different tools you have, how many of each you have on hand and the cost of each one. Write a program that initializes the random-access file "hardware.dat" to 100 empty records, let you input the data concerning each tool, enables you to list all your tools, lets you delete a record for a tool that you no longer have and lets you update any information in the file. The tool identification number should be the record number. Use the following information to start your file.

enter image description here


My Code :

int question_3()
{
    cout << "Question 3" << endl;

    fstream hardware;
    hardware.open("hardware.dat" , ios::binary | ios::out);


//Create 100 blank objects---------------------------------------------------------------
    if (!hardware)
    {
        cerr << "File could not be opened." << endl;
        exit(1);
    }

    HardwareData myHardwareData;

    for (int counter = 1; counter <= 100; counter++)
    {
        hardware.write(reinterpret_cast< const char * >(&myHardwareData), sizeof(HardwareData));
    }

    cout << "Successfully create 100 blank objects and write them into the file." << endl;

    hardware.close();
    hardware.open("hardware.dat" , ios::binary | ios::out | ios::in);


//Write data-----------------------------------------------------------------------------
    int record;
    int quantity;
    float cost;
    string tool_name;

    cout << endl;
    cout << "Enter record number (1 to 100, 0 to end input) : ";
    cin >> record;

    while (record != 0)
    {
        cin.sync();
        cout << "Enter tool name : ";           getline(cin, tool_name);
        cout << "Enter quantity : ";            cin >> quantity;
        cout << "Enter cost : ";                cin >> cost;

        myHardwareData.setRecord(record);
        myHardwareData.setToolName(tool_name);
        myHardwareData.setQuantity(quantity);
        myHardwareData.setCost(cost);

        hardware.seekp((myHardwareData.getRecord() - 1) * sizeof(HardwareData));
        hardware.write(reinterpret_cast<const char *>(&myHardwareData), sizeof(HardwareData));

        cout << endl
            << "Enter record number (1 to 100, 0 to end input) : ";
        cin >> record;
    }

    cout << "Successfully write all input data into the file." << endl;


//Read data----------------------------------------------------------------------------
    cout << endl;
    outputDataLineHead();
    hardware.read(reinterpret_cast<char *>(&myHardwareData), sizeof(HardwareData));
    int counter = 0;
    cout << setprecision(2) << fixed;
    while (hardware && !hardware.eof())
    {
        if (myHardwareData.getRecord() != 0)
            outputDataLine(cout, myHardwareData);

        hardware.seekp(counter++ * sizeof(HardwareData));
        hardware.read(reinterpret_cast<char *>(&myHardwareData), sizeof(HardwareData));
    }

    return 0;
}


//Function for showing data in line form.-----------------------------------------------
void outputDataLineHead()
{
    cout << left << setw(17) << "Record No." 
         << left << setw(17) << "Tool Name"
         << left << setw(17) << "Quantity"
         << left << setw(17) << "Cost" << endl; 
}

void outputDataLine(ostream &output, const HardwareData &Object_in_file)
{
    output << left << setw(17) << Object_in_file.getRecord()
           << left << setw(17) << Object_in_file.getToolName()
           << left << setw(17) << Object_in_file.getQuantity()
           << left << setw(17) << Object_in_file.getCost() << endl;
}

HardwareData.h :

#ifndef HAREWAREDATA_H
#define HAREWAREDATA_H

#include <iostream>
using std::string;

class HardwareData
{
public :
    HardwareData(string name = "", int recd = 0, int qutity = 0, float cot = 0.0)
    {
        setToolName(name);
        setRecord(recd);
        setQuantity(qutity);
        setCost(cot);
    }

    void setToolName(string name)
    {
        const char *nameValue = name.data();
        int length = 0;
        length = (length < 15 ? length : 14);
        strncpy(tool_name, nameValue, length);
        tool_name[length] = '\n';
}

string getToolName() const
{
    return tool_name;
}

void setRecord(int recd)
{
    record = recd;
}

int getRecord() const
{
    return record;
}

void setQuantity(int qutity)
{
    quantity = qutity;
}

int getQuantity() const
{
    return quantity;
}

void setCost(float cot)
{
    cost = cot;
}

float getCost() const
{
    return cost;
}

private :
    char tool_name[15];
    int record;
    int quantity;
    float cost;
};

#endif

enter image description here

I want to show the data like the following :

Record No.        Tool Name          Quantity         Cost
4                 electric hammer    3                34.32

How to achieve this?


Thank you for your attention.

Casper
  • 3,259
  • 9
  • 37
  • 64

2 Answers2

1

I think your problem is while reading data.. Please check your variables if they get correct data or not.. You can check this with counting characters or try to printf them.

If they are not correct. You can use such an example which i used in below.

First of all i prefer you to read your line like this example ;

In this example i get coordinates of faces. You should change parameters.. In order not to read no need data

std::string str;
    while(std::getline(in, str))
    {
        sscanf(str.c_str(), "%d %f %f", &fiducial.number, &fiducial.x, &fiducial.y);

        coord_Num[fiducial.number] = fiducial.get_number();
        coord_X[fiducial.number] = fiducial.get_x();
        coord_Y[fiducial.number] = fiducial.get_y();

    }

If everything looks fine. You should check

void outputDataLine(ostream &output, const HardwareData &Object_in_file)
goGud
  • 3,647
  • 7
  • 31
  • 58
1

The core issue here is that you're reading and writing bytes to/from objects of type HardwareData when rather you should be creating inserters/extractors so you can implement correct I/O semantics. For example:

// Inside HardwareData class
friend std::ostream& operator<<(std::ostream&, const HardwareData&);
friend std::istream& operator>>(std::istream&,       HardwareData&);

These two declarations are for the inserter and extractor respectively. Input should consist of extracting into the record, tool_name, quantity and cost data members; and output should simply be an stream insertion which is trivial to implement.


It is often the problem when mixing formatted input with unformatted input that the residual newline inhibits further input. That seems to be the case here:

cin >> record;                                                             /*
^^^^^^^^^^^^^^                                                             */

while (record != 0)
{
    cin.sync();
    cout << "Enter tool name : ";           getline(cin, tool_name);
    //                                      ^^^^^^^^^^^^^^^^^^^^^^^^

    // ...
}

After cin >> record; finishes, there will be a newline left inside the stream. That newline will stop std::getline() from working correctly because std::getline() only reads until the newline.

The fix here is to ignore this new line by using the std::ws manipulator:

std::getline(std::cin >> std::ws, tool_name);
//           ^^^^^^^^^^^^^^^^^^^

Note: I talk about this in more detail here.

But this manual extraction isn't needed as we've already defined the inserter and extractor for our class. So all that's really needed is the following:

while (std::cin >> myHardwareData)
{
    hardware << myHardwareData;
}

or

std::copy(std::istream_iterator<HardwareData>(std::cin),
          std::istream_iterator<HardwareData>(),
          std::ostream_iterator<HardwareData>(hardware));

Noticed how I've also taken out the check for a 0 value of record in the while loop. That's because the extractor takes care of it by reflecting a 0 value of record as invalid input. It sets the stream state of the stream if this occurs, thus allowing ourselves to be ejected from the while if that happens:

std::istream& operator>>(std::istream& is, HardwareData& hd)
{
    cout << "Enter record number (1 to 100, 0 to end input) : ";

    if ((is >> record) && record != 0)
    {
          // ...
    } else
    {
        is.setstate(std::ios_base::failbit);
    }
    // ...
}

And the rest of your code be changed to:

std::cout << myHardwareData;
hardware >> myHardwareData;

std::cout << std::setprecision(2) << std::fixed;
while (hardware >> myHardwareData)
{
    if (myHardwareData.getRecord() != 0)
        std::cout << myHardwareData;
}

I don't really know what the seekps are for. If you elaborate on that, that would really help me adapt my code more accurately to your needs.

Community
  • 1
  • 1
0x499602D2
  • 87,005
  • 36
  • 149
  • 233