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, lets 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:

Record# Tool name Quantity Cost
3 Electric sander 7 57.98
17 Hammer 76 11.99
24 Jig saw 21 11.00
39 Lawn mower 3 79.50
56 Power saw 18 99.99
68 Screwdriver 106 6.99
77 Sledge hammer 11 21.50
83 Wrench 34 7.50

problem i am having is when i go to enter the items listed above, the first entry stores correctly, but each consecutive entry gets fubared. Some please take a look at my code for me and tell me whats going on. Brain is fried and only have a day left to get this done.

thx in advance

#include <iostream>
#include <fstream>
#include <iomanip>
#include <stdlib.h> 
#include <conio.h> 
#include <string.h>
#include <windows.h>

using namespace std;

typedef struct _tooldata
{
   int toolNum;   
   char toolName[128];
   int quantity;
   double cost;

} toolData;


void textFile(fstream &readFromFile);
int enterChoice();
void updateRecord(fstream &updateFile);
void newRecord(fstream &insertInFile);
void deleteRecord(fstream &deleteFromFile);
void outputLine(ostream &output, toolData tool);

void gotoxy(int x, int y)
{
   COORD ord;
   ord.X = x;
   ord.Y = y;
   SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), ord);
}

void clrscr()
{
   system("CLS");
}


static char FILENAME[] = "tools.dat";

void CreateDataFile()
{
   fstream testFileExists(FILENAME, ios::in | ios::out);
   if (!testFileExists) 
   {
      ofstream outtools(FILENAME);
      toolData *data = new toolData();
      strcpy(data->toolName, "");
      data->quantity = 0;
      data->cost = 0.0;
      data->toolNum = 0;
      for(int i = 0; i < 100; i++)
         outtools.write((char*)data, sizeof(*data));
      outtools.close();
   }
   else
      testFileExists.close();
}

int main()
{
   CreateDataFile();

   fstream inOuttools(FILENAME, ios::in | ios::out);

   inOuttools.seekg(0,ios::end); 
   long location = inOuttools.tellg(); 
   cout << "Size of hardware.dat = " << location;

   int choice;

   while ( ( choice = enterChoice() ) != 5 ) {

      switch (choice)
	  {
         case 1: textFile(inOuttools); break;
         case 2: updateRecord(inOuttools); break;
         case 3: newRecord(inOuttools); break;
         case 4: deleteRecord(inOuttools); break;
         default: cerr << "Incorrect choice" << endl; break;
      }

      inOuttools.clear(); // resets end-of-file indicator
   }
   clrscr();
   return 0;
}

// Prompt for and input menu choice
int enterChoice(void)
{
   cout << endl << "Enter your choice" << endl
      << "1. stored a formatted text file of accounts" << endl
      << " called hardware.dat for printing" << endl
      << "2. update inventory" << endl
      << "3. add a new item" << endl
      << "4. delete an item" << endl
      << "5. end program" << endl << "? ";

   int menuChoice;
   cin >> menuChoice;
   return menuChoice;
}

// Create a random access data file called <tools.dat>
void makeDirectAccessFile(fstream &inOuttools)
{
   toolData blanktool = {0, "", 0, 0.0};

   for (int i = 1; i <= 100; i++)
      inOuttools.write((char *)&blanktool, sizeof(toolData));

   cout << endl << endl
      << "A direct access file called <hardware.dat> has been " << endl
      << "created with four fields initialized as follows:"
      << endl << endl
      << " toolNum = 0" << endl
      << " toolName[15] = \"\"" << endl
      << " long quantity = \"\"" << endl
      << " cost = 0.0" << endl;

   cout << endl << "Press any key to return to the menu." << endl << endl;
   getch();
}


void textFile(fstream &readFromFile)
{
   ofstream outPrintFile("print.txt", ios::out);

   if (!outPrintFile) {
      cerr << "File could not be opened." << endl;
      exit(1);
   }

   clrscr();

   cout << setiosflags(ios::left) << setw(6) << "Tool"
      << setw(16) << "Tool Name" << setw(11) << "Quantity"
      << setiosflags(ios::right) << setw(10) << "Cost" << endl;

   outPrintFile << setiosflags(ios::left) << setw(6) << "Tool"
      << setw(16) << "Tool Name" << setw(11) << "Quantity"
      << setiosflags(ios::right) << setw(10) << "Cost" << endl;
   readFromFile.seekg(0);

   toolData tool;
   readFromFile.read((char *)&tool, sizeof(toolData));

   while (!readFromFile.eof()) {
      if (tool.toolNum != 0) {
         outputLine(outPrintFile, tool);
         outputLine(cout, tool); 
      } 

      readFromFile.read((char *)&tool, sizeof(toolData));
   } 
}

void updateRecord(fstream &updateFile)
{
   int account;

   clrscr();

   do {
      cout << "Enter inventory number to update (1 - 100): ";
      cin >> account;
   } while (account < 1 || account > 100);

   updateFile.seekg((account - 1) * sizeof(toolData));

   toolData tool;
   updateFile.read((char *)&tool, sizeof(toolData));

   if (tool.toolNum != 0) {
      outputLine(cout, tool);
      cout << endl << "Enter charge (+) or payment (-): ";

      float transaction;
      cin >> transaction;
      tool.cost += transaction;
      outputLine(cout, tool);
      updateFile.seekp((account - 1) * sizeof(toolData));
      updateFile.write((char *)&tool, sizeof(toolData));
   }
   else
      cerr << "Acount #" << account << " has no information." << endl;
}

void newRecord(fstream &insertInFile)
{
   char text[80];
   clrscr();

   cout << "Enter new item number (1 - 100): ";

   int account;
   cin >> account;

   insertInFile.seekg((account - 1) * sizeof(toolData));

   toolData tool;
   insertInFile.read((char *)&tool, sizeof(toolData));

   if (tool.toolNum == 0)
   {
      cout << "Enter the tool name and press <ENTER>: ";
      cin.getline(text,80); 
      cin.getline(text,80); 

      if ( strlen(text) > 14) 
      { 
         strncpy (tool.toolName, text, 14); 
         tool.toolName[14] = '\0'; 
         cout << "Name is too long. It has been shortened to "
            << tool.toolName << "." << endl;
      } 

      else

      { 
         strcpy(tool.toolName, text);
         tool.toolName[14] = '\0'; 
      } 

      cout << "Enter the quantity and press <ENTER>: ";
      cin >> tool.quantity;

      cout << "Enter the cost as a decimal value and press <ENTER>: ";
      cin >> tool.cost;
	
	  tool.toolNum = account;
      insertInFile.seekp((account - 1) * sizeof(toolData));
      insertInFile.write((char *)&tool, sizeof(toolData));
   }
   else 
      cerr << "Account #" << account
      << " already contains information." << endl;
}

void deleteRecord(fstream &deleteFromFile)
{
   clrscr();

   cout << "Enter account number to delete (1 - 100): ";

   int account;
   cin >> account;

   deleteFromFile.seekg((account - 1) * sizeof(toolData));

   toolData tool;
   deleteFromFile.read((char *)&tool, sizeof(toolData));

   if (tool.toolNum != 0) {
      toolData blanktool = {0, "", 0, 0};
      deleteFromFile.seekp((account - 1) * sizeof(toolData));
      deleteFromFile.write((char *)&blanktool, sizeof(toolData));
      cout << setiosflags(ios::left) << setw(6) << "tool"
         << setw(16) << "Last Name" << setw(11) << "First Name"
         << setiosflags(ios::right) << setw(10) << "cost" << endl;
      outputLine(cout, tool);
      cout << "Account #" << account << " deleted." << endl;
   }
   else
      cout << "Account #" << account << " is empty." << endl;
}

void outputLine(ostream &output, toolData tool)
{
      output << setiosflags(ios::left) << setw(6) << tool.toolNum
      << setw(16) << tool.toolName << setw(11) << tool.quantity
      << setiosflags(ios::fixed | ios::showpoint | ios::right)
      << setw(10) << setprecision(2) << tool.cost << endl;
}

You have way too much code involving user input in the same functions as the file reads and writes. If you are advanced enough to use read, write, sizeof, and other fairly low-level function calls, you're advanced enough to partition the program into different segments. This isn't just being nit-picky. The fact that they are intertwined is going to make the debugging process much harder because you can't tell whether the problem is with the user input or the file write or the file red or whatever. You can't solve the problem till you narrow the problem down. You also can't use a function to write to / read from the file without user input with all those cin statements in there, which means you can't hard-code records in there and debug by bypassing all the user-input code and see if that works. So you need to redesign functions like this...

void deleteRecord(fstream &deleteFromFile)

to this...

void deleteRecord(fstream &deleteFromFile, int account)

Get the code reading user input out of that function. It doesn't belong there. Nor do the cout statements. That applies to your other functions too. It'll be much easier to debug that way.

some how, when i go back to display the "accounts" i put in, the compiler reads the first record correctly but then all the following "accounts", the compiler will give the address location in memory in stead of the value of the address. lines 130-161 of my code.

i have tried taking the pointers out as well as tried converting the data to another form..nothing works.

it compiles fine and i receive no errors or warnings through visual studio 2010.

For all you know, the file itself is corrupted, in which case it's garbage in, garbage out. If you haven't confirmed that the file itself is correct, don't assume it is, in which case don't assume you have a read error. It could be a write error instead. It could also be writing just fine and you're telling it to write crap. See my last post. You need to figure out which of the many things it could be that it is.

To do that, you need to start sticking a lot of debugging code in there and writing a few test programs. Write a program that initializes an array of 100 objects to whatever you're typing in in MEMORY, not a file, then write it to a file and rename the file. That gives you a known good file. Then run the program you have now and insert your hardware into inventory and write to a file. Then compare the two files (using a command line, not C++) and make sure they're the same. Also make a hex dump of the files and inspect it visually to make sure the file is right. If and only if that's all good, start debugging the file reading.

tested it and even open the file directly. all the information being written to the file is in the correct format, even when you go to delete a record using the program it displays correctly. Just trying to trouble shoot why its not displaying correctly in this one function. Everything else is working fine, whats the old saying. if it ain't broke don't fix it.

tested it and even open the file directly. all the information being written to the file is in the correct format, even when you go to delete a record using the program it displays correctly. Just trying to trouble shoot why its not displaying correctly in this one function. Everything else is working fine, whats the old saying. if it ain't broke don't fix it.

Wrong. Test it harder and test it better. It is broke, it's not written in the correct format, it's not working fine, so you need to fix it. It doesn't save correctly. You can test your display function all you want and you won't solve it. Garbage in, garbage out.

See attachment. See address 0x1b0 in the file. This is supposed to be 0. If it's not zero, it should be 0x04. It is 0x40, just like address 0x8f is. Problem? You bet.

See addresses 0x1a9 and 0x1aa. I assume "0x0d0a" is familiar to you. If not, look at the ASCII table and note the key line in your program ("windows.h"). Now could "0x0d0a" show up legitimately in a double? Sure, but odds are that that this is a Windows line-feed, particularly since the record appears to be one longer than it should be. There's no reason for the program to write a line feed, but you are writing it somewhere. Your job is to figure out why.

Edited 4 Years Ago by VernonDozier: n/a

Attachments Screenshot_01_08_2012__090708.jpg 219.05 KB

okay. went back and cleaned it up. I hope its easier to read. Now all my problems seem to be centered around lines 34 and 35. This is due tomorrow so any help in figureing this out is greatly appericated.

#include <iostream>
#include <iomanip>
#include <fstream>
#include <cstring>
#include <cctype>
#include <cstdlib>
using namespace std;

void initializeFile( fstream & );
void inputData( fstream & );
void listTools( fstream & );
void updateRecord( fstream & );
void insertRecord( fstream & );
void deleteRecord( fstream & );
int instructions();

const int LENGTH = 30;

struct Data
{
	int partNumber;
	char toolName[ LENGTH ];
	int inStock;
	double unitPrice;

};

int main()
{
	int choice;
	char response;
	fstream file( "hardware.dat", ios::in | ios::out );

	void( *f[] )( fstream & ) = { listTools, updateRecord, 
		insertRecord, deleteRecord );

	if( !file )
	{
		cerr << "File could not be opened.\n";
		exit(0);
	}

	cout << "Should the file be initialized (y or n): ";
	cin >> response;

	while( toupper( response ) != 'y' && toupper( response ) != 'n' )
	{
		cout << "Invalid response.  enter y or n: ";
		cin >> response;
	}

	if( toupper( response ) == 'y')
	{
		initializeFile( file );
		inputData( file );
	}

	while(( choise = instructions() ) != 5
	{
		( *f[ choice - 1] )( file );
		file.clear();
	}

	file.close();

	system("pause");

	return 0;

}

void initializeFile( fstream &fRef )
{
	Data blankItem = { -1, "", 0, 0.0 };

	for( int i = 0; i < 100; i++ )
		fRef.write( reinterpret_cast< char * >( &blankItem ), sizeof( Data ) );
}

void inputData( fstream &fRef )
{
	Data temp;

	cout << "Enter the part number (0 - 99, -1 to end entry): ";
	cin >> temp.partNumber;

	while( temp.partNumber != -1 )
	{
		cout << "Enter the tool name: ";
		cin.ignore();
		cin.get( temp.toolName, LENGTH );

		cout << "Enter quantity and price: ";
		cin >> temp.inStock >> temp.unitPrice;

		fRef.seekp( (temp.partNumber ) * sizeof( Data ) );
		fRef.write( reinterpret_cast< char * >( &temp ), sizeof( Data) );

		cout << "Enter the part number (0 -99, -1 to end entry): ";
		cin >> temp.partNumber;
	}
}

int instructions()
{
	int choice;

	cout << "\nPick one of the following: "
		<< "\n1.	List all tools." << "\n2.	Update Record."
		<< "\n3.	Insert record." << "\n4.	Delete record."
		<< "\n5.	End program.\n";

	do
	{
		cout << "?";
		cin >> choice;

	}

	while( choice < 1 || choice > 5);

	return choice;
}

void listTools( fstream &fREf )
{
	Data temp;

	cout << setw( 7 ) << "Record #" << "\t" << setiosflags( ios::left )
		<< setw(30) << "Tool Name" << resetiosflags( ios::left )
		<< setw( 13 ) << "Quantity" << setw( 10 ) << "Cost\n";

	for( int count = 0; count < 100 && !fRef.eof(); count++)
	{
		fRef.seekg( count * sizeof( Data ) );
		fRef.read( reinterpret_cast< char * >( &temp ), sizeof( Data ));

		if( temp.partNumber >= 0 && temp.partNumber < 100)
		{
			cout.setf( ios::fixed | ios::showpoint );
			cout << setw( 7 ) << temp.partNumber << "\t"
				<< setiosflags( ios::left ) << setw(30) << temp.toolName
				<< resetiosflags( ios::left ) << setw( 13 ) << temp.inStock
				<< setprecision( 2 ) << setw( 10 ) << temp.unitPrice << '\n';
		}
	}
}

void updateRecord( fstream &fRef )
{
	Data temp;
	int part;

	cout << "Enter the part number for update: ";
	cin >> part;

	fRef.seekg( part * sizeof( Data ));
	fRef.read( reinterpret_cast< char * >( &temp ); sizeof( Data ) );

	if( temp.partNumber != -1)
	{
		cout << setw( 7 ) << "Record #" << "\t" << setiosflags( ios::left )
			<< setw( 30 ) << "Tool name" << resetiosflags( ios::left )
			<< setw( 13 ) << "Quantity" << setw( 10 ) << "Cost\n";

		cout.setf( ios::fixed | ios::showpoint );
		cout << setw( 7 ) << temp.partNumber << "\t"
			<< setiosflags( ios::left ) << setw( 30 ) << temp.toolName
			<< resetiosflags( ios::left ) << setw( 13 ) << temp.inStock
			<< setprecision( 2 ) << setw( 10 ) << temp.unitPrice << '\n'
			<< "Enter the tool name: ";

		cin.ignore();
		cin.get( temp.toolName, LENGTH );

		cout << "Enter quantity and price: ";
		cin >> temp.inStock >> temp.unitPrice;

		fRef.seekp( (temp.partNumber) * sizeof( Data ) );
		fRef.write( reinterpret_cast< char * >( &temp ), sizeof( Data ));

	}

	else
		cerr << "Cannot update.  The record is empty. \n";
}

void insertREcord( fstream &fRef )
{
	Data temp;
	int part;

	cout << "Enter the part number for insertion: ";
	cin >> part;
	
	fRef.seekg( (part) * sizeof( Data ) );
	fRef.read( reinterpret_cast< char * >( &temp ), sizeof( Data) );

	if( temp.partNumber == -1 )
	{
		temp.partNumber = part;
		cout << "Enter the tool name: ";
		cin.ignore();
		cin.get( temp.toolName, LENGTH );

		cout << "Enter quantity and price: ";
		cin >> temp.inStock >> temp.unitPrice;

		fRef.seekp( (temp.partNumber ) * sizeof( Data ) );
		fRef.write( reinterpret_cast< char * >( &temp ), sizeof( Data ) );
	}

	else
		cerr << "Can not insert.  The record contains information.\n";
}

void deleteRecord( fstream &fREf )
{
	Data blankItem = { -1, "", 0, 0.0 }, temp;
	int part;

	cout << "Enter the part number for deletion: ";
	cin >> part;

	fRef.seekg( (part) * sizeof( Data ) );
	fRef.read( reinterpret_cast< char * >( &temp ), sizeof( Data) );

	if( temp.partNumber != -1 )
	{
		fREf.seekp( part * sizeof( Data ) );
		fREf.write( reinterpret_cast< char * >( &blackItem ), sizeof( Data ) );
		cout << "Record deleted. \n";
	}

	else
		cerr << "Can not delete.  The record is empty.\n";

}

An array of function pointers adds an unnecessary level of complexity to a program that already doesn't work and needs debugging. My advice? Get it to work using a regular good-old-fashioned 4-case switch statement that calls the right function. You have a limited amount of time. You can either spend it debugging function pointers or debugging the actual writing / reading of the file. As to what you're doing wrong in line 34 and 35, I'm rusty on the syntax so I can't tell.

Edited 4 Years Ago by VernonDozier: n/a

This article has been dead for over six months. Start a new discussion instead.