Hey, everyone, I am another student seeking help for a programming class. What I am having trouble with specifically is reading from a file.

The program itself involves entering in some store products with information such as: product name, product ID, etc. After everything is entered, the program is supposed to redisplay all of the information stored if required to do so.

My teacher taught us about the basics of using the (file).open and (file).read functions to read and write to and from notepad files, but I am having trouble reading more than one line from the file. I have saved at least 2 lines of data into the notepad file, but when displaying, I can only display one of the lines and have no idea how to display the other one. I have divided my code into 2 functions in addition to the main one, with the display code being in display_product().

213-227 -> This is where I write the data into the file using binary since my teacher recommended it.
241-313 -> This is where I try to read from the file back into the program, but have no idea how to read parts of it.

-- Also, sorry for my horrible coding and labelling, I am really bad at programming and still trying to learn.

______________________________________________________
//	Mysterious Man
//	April 2nd, 2008
//	Menu System 2

#include "Main(1).h"

void main()
{
	init_graphics();
	int choice;
	char waste[50];

	set_color (cBLUE,cBLACK);

top:
	clear_screen();
	set_color (cMAGENTA,cBLACK);
	

	cout<< "\t\t\t  Grocery Products Database\t\t\t\t";
	cout.flush();
	cout<< "\n--------------------------------------------------------------------------------";
	cout.flush();
	set_color (cBLUE,cBLACK);
	cout<< "\n\t\tAdd a New Product\t\t(1)";
	cout.flush();
	cout<< "\n\t\tList Products\t\t\t(2)";
	cout.flush();
	cout<< "\n\t\tUpdate Products\t\t\t(3)";
	cout.flush();
	cout<< "\n\t\tSearch Products\t\t\t(4)";
	cout.flush();
	cout<< "\n\t\tQuit\t\t\t\t(5)";
	cout.flush();

	set_color (cRED, cBLACK);
	cout<< "\n\n\tMake your choice: ";
	cout.flush();
	set_color (cBLUE, cBLACK);

	choice= getche();

	switch (choice)
	{
		case '1':
			add_product();
			break;
		case '2':
			display_product();
			break;
		case '3':
			break;
		case '4':
			break;
		case '5':
			cout<< "\n\nThankyou for using the program...\n\n";
			cout.flush();
			return;
			break;
		default:
			cout<< "\n\nPlease enter a valid number...\n";
			cout.flush();
			wait (2000);
			break;
	}
	goto top;
}

void add_product() 
{
	char waste[50];

	//	Add a new product to our file 
	fstream outfile;
	char yesno;
	product prod;

	clear_screen();
	set_color ((color)9,(color)0);	
	
	set_cursor_pos (34,1);
	cout<< "Add New Product";
	cout.flush();

	set_color ((color)15,(color)0);
	set_cursor_pos(10,3);
	cout<< "Please enter sales person's first name: ";
	cout.flush();
	cin.width(20);	//	Maximum 20 characters allowed 

	cin>> prod.fname;	
	cin.ignore(50, '\n');

	//	Properly format first name
	_strlwr (prod.fname);

	if (prod.fname[0]> 96 && prod.fname[0]< 123)
	{
		prod.fname[0]-=32;
	}

	if (cin.gcount() < 20) cin.ignore (20< '\n');

	set_cursor_pos(10,4);
	cout<< "Please enter sales person's last name: ";
	cout.flush();
	cin.width(20);	//	Maximum 20 characters allowed 

	cin>> prod.lname;	
	cin.ignore(50, '\n');

	//	Properly format last name
	_strlwr (prod.lname);

	if (prod.lname[0]> 96 && prod.lname[0]< 123)
	{
		prod.lname[0]-=32;
	}

	if (cin.gcount() <30) cin.ignore (30< '\n');

	//	Entering the product name which has a max of 50 characters
	set_cursor_pos (10,5);
	cout<< "Please enter the product name: ";

	cin.getline (prod.prodname, 50);
	if (cin.gcount() <50) cin.ignore (50< '\n');

	set_cursor_pos (10,6);
	cout<< "Please enter the product ID number: ";
	cin.width (7);

	cin>> prod.prodnum;
	cin.ignore (50< 'n');

	//	Check to see if all ID numbers are digits 
	bool ok;
	
	do
	{
		ok= false;	//	false means no error
		for (int i= 0; i<6; i++)
		{
			if (prod.prodnum[i]< 48 || prod.prodnum[i]> 57)
			{
				ok= true;	//	not a number
			}
			if (ok=true)
			{
				set_cursor_pos (10,8);
				cout<< "Please enter the product ID number: ";
				cin.width (7);

				cin>> prod.prodnum;
				cin.ignore (50, '\n');
			}
		}
	} while (ok=true);

	//	Enter Regular and Sales Price into header
	set_cursor_pos (10,10);
	cout<< "Enter regular price of product "<< prod.prodname<< ": ";
	cin.width (7);

	cin>> prod.price.regprice;
	cin.ignore (50, '\n');

	set_cursor_pos (10,11);
	cout<< "Enter sales price of product "<< prod.prodname<< ": ";
	cin.width (7);

	cin>> prod.price.saleprice;
	cin.ignore (50, '\n');

	//	If the reg price is 0, set discount to 0
	if (prod.price.regprice<= 0 || prod.price.regprice== prod.price.saleprice)
	{
		prod.price.discount= 0;
	}
	else
	{
		prod.price.discount= 100-(((prod.price.saleprice)/(prod.price.regprice))* 100);
	}

	// Review the data for input
	clear_screen();

	set_cursor_pos (34,1);
	cout<< "Add a New Product";
	cout.flush();
	prod.active= 1;	//	this is an active record

	//	Forcing the  money values to be converted to two decimal places & displaying
	cout.setf (ios::fixed, ios::floatfield);
	cout.precision (2);

	set_cursor_pos (8,3);
	cout<< "Save the following information?\n";
	cout.flush();
	cout<< "\nFirst Name:\t\t\t"<< prod.fname<< "\nLast Name:\t\t\t"<< prod.lname;
	cout.flush();
	cout<< "\nProduct Name:\t\t\t"<< prod.prodname<< "\nProduct ID:\t\t\t"<< prod.prodnum;
	cout.flush();
	cout<< "\nRegular Product Price:\t\t$ "<< prod.price.regprice<< "\nProduct Sales Price:\t\t$ " << prod.price.saleprice;
	cout.flush();
	cout<< "\nProduct Discount:\t\t"<< prod.price.discount<< "%";

	cout<< "\n\nChoose yes or no (y/n)? ";
	yesno= cin.get();
	cin.ignore (50, '\n');

	if (yesno== 'y' || yesno== 'Y')
	{
		outfile.open ("Product_Database.txt", ios::out | ios::app | ios::binary);
		if (!outfile)
		{
			cout<< "*** File Error Lol ***"<< endl;		//	endl = endline = "\n"
			wait (1000);
			return;
		}

		//	Write to file - In Binary 
		outfile.write ((char *) &prod, sizeof (prod));
		cout<< "\nData Saved...\n";
		wait (1000);
		outfile.close();
	}

	else if (yesno!= 'Y' || yesno!= 'y')
	{
		cout<< "\nReturning to Main Menu...";
		wait (2000);
		return;
	}
	waste[49]= yesno;
	
	return;
}

void display_product()
{
	fstream infile;
	product prod;
	int count;

	clear_screen();
	set_cursor_pos (31,2);
	cout<< "Displaying Products";
	cout.flush();

	// ------------------------- casting operator 
	infile.open ("Product_Database.txt", ios::in | ios::binary);
	if (!infile.is_open())
	{
		cout<< "The File Does Not Exist!";
		cout.flush();
		wait (2000);
		return;
	}

	set_cursor_pos (1,4);
	cout<< "   -------------------------------------------------------------------";
	cout.flush();

	set_cursor_pos (1,6);
	cout<< "   -------------------------------------------------------------------";
	cout.flush();

	set_cursor_pos (1,5);
	cout<< "   |First Name|";
	cout.flush();

	set_cursor_pos (18,5);
	cout<< "   |Last Name|";

	cout.flush();
	
	set_cursor_pos (36,5);
	cout<< "   |Product Name|";
	cout.flush();

	set_cursor_pos (56,5);
	cout<< "   |Product ID|";
	cout.flush();

	count= 0;
	
	// -------------- If using binary, must use:: infile.read((char *) &prod, sizeof (prod))
	do
	{
		infile.read ((char *) &prod, sizeof (prod));
		prod.count++;

		set_cursor_pos (4,8+count);
		cout<< "1. "<< prod.fname;
		cout.flush();

		set_cursor_pos (22,8+count);
		cout<< prod.lname;
		cout.flush();

		set_cursor_pos (40,8+count);
		cout<< prod.prodname;
		cout.flush();

		set_cursor_pos (60,8+count);
		cout<< prod.prodnum;
		cout.flush();

	} while (!infile.eof());

	// --------------
	infile.close();
	cout<< "\n";	
	wait (5000);
}
________________________________________________________________
HEADER

//	Grocery Sales List HEADER FILE 

#include <iostream.h>
#include <fstream.h>
#include <string.h>
#include <stdio.h>
#include "msoftcon.cpp"

//	Create the grocery file structure
struct sales
{
	double regprice;
	double saleprice;
	double discount;
};

struct product
{
	int active;			//	Is this record active= 1, inactive= -1
	char prodname[50];	                //	The product name
	char fname[20];		//	The sales person's first name 
	char lname[30];		//	The sales person's last name
	char prodnum[7];	                //	Product identification number
	sales price;		//	Lolol from above structure!

	int count;			//	The counter for displaying
	char yesnolol;		//	Press any key to continue after displaying

};

void add_product();
void display_product();

Can you verify that in fact more than one record is in the output file? Check it's size in bytes, or open it in an editor. You should be able to see the text strings entered- are they all there?

I can't find anything wrong with the writing or reading portions, other than your loop will process the last record twice.

Your use of the .eof( ) method will not cause the actual end of data to be signaled till you attempt to read past the end, which occurs after you've read in the last record, processed it, then try again. The previously stored data will be executed in the loop body again, then the while condition will halt the loop.

If you're going to use eof( ), the smarter way is:

infile.read ( ....... );
while( ! infile.eof( ) )
{
   ///process....
  
   infile.read( ...... );
}

Better still:

while( infile.read( ......) )
{
     //process
}

Thanks for your help vmane, but I can't edit the file yet since I am at home and have no working edition of c++ =|

Anyways, when I entered the following data into the add_product function from the dos program:
fname: Joe
lname: Brookshaw
Product Name: Pershing M1
Pro ID: 123456
Prod Price: 123
Prod Sales Price: 100

It showed up as:
t Joe ttttttttttt Brookshaw tttttttttttttt 123456 tttttt
in the notepad file. The t's were actually crosses, and it looked something like the above (I can't recall exactly). When I entered in a second similar entry, there was another section of the same format as the above added to the same line.

When I tried to display the info with one entry, it worked fine; the same can't be said for 2+ entries.

I have no clue why it won't work, so if anyone can provide further assistance, it would be much appreciated.

That shows the writing is occurring OK.

In the display function, the variable "count" is used to set line for output, if I read it right. You don't increment that variable that I can see, so each record read in is overwriting the previous on the screen. So, does the product structure really need a count member? What's its function, relative to the count variable in the function?

Ah yes, you are right, I forgot to do a count++ with it's own variable earlier in the fnction. The count variable is done so that after each line of entry is added, the next one would be on the next line (changing the y value).

The problem isn't with displaying and overlapping as far as I can tell... when I have 2 or more entries, as soon as I run my program and choose to display items, it shows up with an error message saying that the memory could not be read (2 errors of that actually).

Thanks for the count note, I will fix it tomorrow :)

Sounds like you have some debugging to do - just when do those errors occur? When first reading in, or when the loop (as presently shown) attempts to read one record too many?

The error actually showed when I was trying to read from the file right at the beginning of the loop. However, I started the program up the next day and the error ceased to show, so I guess I'm good O_O;

There is one more thing that I would like to ask, and that is if it would be possible for you to help me with formatting. I recently started up my program and I tried to format a phone number entered in for the employee section, but it didn't work. I am not sure what I did wrong, but when I get to that section, if I don't enter in exactly 6 (or how many digits I would care to specify), the loop would be endless.

Can anyone point out what I did wrong for the formatting? I just want to get it to check if all characters are numbers at the moment, figuring out where to put the dashes in will be another problem =/

// Checking to make sure all characters entered are actually digits.
bool ok;
	
	do
	{
		ok= false;	//	false means no error
		for (int i= 0; i<6; i++)
		{
			if (prod.prodnum[i]< 48 || prod.prodnum[i]> 57)
			{
				ok= true;	//	not a number, turns bool on.
			}

			if (ok=true)
			{
                               // Forces user to re-enter number
				set_cursor_pos (10,8);
				cout<< "Please enter the phone number: ";
				cin.width (10);

				cin>> prod.prodnum;
				cin.ignore (50, '\n');
			}
		}
	} while (ok=true);             // If letters still found, loop again.

	cin>> prod.prodnum;
	cin.ignore (50< 'n');

First of all, in both places where you thought you were comparing the variable ok to the value true, you were actually assigning ok to be true. Which will always result in a true evaluation.
Use the double-equal sign if ( ok == true ) From an understandability standpoint, you use a variable with a name that has positive connotation, but in a negative way. Better to make the variable name agree with its state. OK being false as the good state is confusing. In my fixup below, I've changed it's use to better reflect the usage, using the NOT operator when you want to know if the process should continue, as in while ( !ok ) //keep doing something .

Also, when testing if some character is in a desired range, test against the character itself, not the ASCII value. First it's clearer what you're doing, and you're not limiting yourself to specifically the ASCII set.

The code below fixes the errors discussed. But, I think there's still some big logic problems. First, is this routine meant to evaluate the contents of array prod.prodnum[], or to be filling it in - you seem to be mixing the two process here. You have two statements doing input into the array (with no index ), this won't do anything. Clarify what you're trying to do, then do one thing at a time.

//still not all correct, awaiting better problem specification
// Checking to make sure all characters entered are actually digits.
// Checking to make sure all characters entered are actually digits.
bool ok;

do
{
    ok = true;	//	assume correct, false means  error
    for (int i= 0; i<6; i++)
    {
        if (prod.prodnum[i]< '0' || prod.prodnum[i]> '9' )
        {
            ok = false;	//	not a number, turns bool on.
        }

        if (!ok )
        {
            // Forces user to re-enter number
            set_cursor_pos (10,8);
            cout<< "Please enter the phone number: ";
            cin.width (10);

            cin>> prod.prodnum;
            cin.ignore (50, '\n');
        }
    }
} while (!ok);             // If letters still found, loop again.

cin>> prod.prodnum;
cin.ignore (50,  '\n');

Tell us what you're really trying to do, and we'll make more progress. And what's phone numbers got to with this?

First of all, in both places where you thought you were comparing the variable ok to the value true, you were actually assigning ok to be true. Which will always result in a true evaluation.
Use the double-equal sign if ( ok == true ) From an understandability standpoint, you use a variable with a name that has positive connotation, but in a negative way. Better to make the variable name agree with its state. OK being false as the good state is confusing. In my fixup below, I've changed it's use to better reflect the usage, using the NOT operator when you want to know if the process should continue, as in while ( !ok ) //keep doing something .

Alright, the first error was in the IF statement, I am sorry about that. I forgot that you need two equal operators for comparison and not just one (I was thinking about reading and writing again).

Also, when testing if some character is in a desired range, test against the character itself, not the ASCII value. First it's clearer what you're doing, and you're not limiting yourself to specifically the ASCII set.

My teacher told us that we can compare characters directly, but generally wanted us to use the ASCII chart wherever possible. That's why if you recall in my earlier post, when I formatted the name, I used the ASCII codes. I know that I had to use it there, but he just told us to use it when formatting numbers as well.

The code below fixes the errors discussed. But, I think there's still some big logic problems. First, is this routine meant to evaluate the contents of array prod.prodnum[], or to be filling it in - you seem to be mixing the two process here. You have two statements doing input into the array (with no index ), this won't do anything. Clarify what you're trying to do, then do one thing at a time.


Tell us what you're really trying to do, and we'll make more progress. And what's phone numbers got to with this?

Ok, first of all, please forgive me; I am still very new and bad at logic and programming. The DO loop is meant to do three things:
1] Set the ok variable to false (originally).

2] Using the FOR statement, it is supposed to check if each individual character is a digit or a character. If it is anything but a digit (using ASCII), the condition of the BOOL 'ok' variable is set to true, meaning that an error is reached.

3] In the next part, if ok was true (which means any space in prod.prodnum is not a digit), it will force the user to re-enter the entire phone number again. The cycle then repeats itself until 6 digits have been checked, and the FOR loop ends. At this point, if 'ok' is already true, then the entire loop begins over again.

I think that I know what my error was, that the prompt to re-enter the name should occur after the FOR loop, is that correct? I can't check since I am at home again.

Haha, the phone number was a little thing that I 'tried' to add in for extra credit. My teacher said that if we can someone format the phone number (throw in dashes after the parts of the phone number, turn it into all numbers, etc.), we can get better marks. I guess I was being too ambitious.

I still think we need to step back a bit - I'm still not clear on what you really want this routine to do. It has parts of two different processes. It is either:
a - validating that the contents of the prod.prodnum array are all digits
- or it is
b - getting input from the user that is stored in this array

In your for loop, you're examining a particular element of the array. If it's bad, you have the user enter a whole new array content, but pick up the validation at the next position (i increments)

I think the simplest approach is to get a full array-worth of input, then validate it all, and return to the input step if there's an error. Don't try to fix anything in the middle of the sequence, you'll confuse the user.

bool ok;
char data[7] = "";

do
{
    cout << "Enter the product number (6 digits ): ";
    cin.getline( data, 7 ); 
     //or cin >> data;  if you trust user not to enter too many chars

    ok = true;	//assume correct, false means  error

    for (int i = 0; i < 6; i++)
    {
        if (prod.prodnum[i] < '0' || prod.prodnum[i] > '9' )
        {
            ok = false;	//any error sets flag.
        }

       
    }
} while (!ok); // If letters still found, loop again.

As to using ASCII values or the explicit characters, I guess that's teacher's preference. He's got his, I teach mine. Of course, we could avoid the issue altogether by using the isdigit( ) function from the <cctype> library.

Thanks vmanes, I appreciate your help so far. I realized that what I did before was wrong (sort of) because I asked for a number to be entered in the middle of a loop while checking it.

Your solution seems more practical, as from what I understand, it gets the number, then checks for each value to see if they are digits. If there are some non-digits, the loops restarts.

This is what I can gather from your solution, but I still get the same problem: if I enter in something incorrect (ie. 4a23123491), it will prompt to re-enter another number; now, even if I enter another number that is correct, the loop becomes infinite and never ends?

My previous sample was a bit incomplete. It was meant to show the general path you needed.

This is more robust. It accounts for user entering more than 6 characters. One has to be careful with getline( ) - yes it gives us protection from reading too much into our array, but we must be aware that if the input is larger than expected, it sets the input stream into an error state and no further input will succeed. Till we use the clear( ) method and get rid of the remaining bad input.

#include <iostream>
#include <cstring>
using namespace std;

int main ( )
{
    bool ok;
    char data[7] = "";

    do
    {
        cout << "Enter the product number (6 digits ): ";
        cin.getline( data, 7 ); 
         if( !cin.good( ) )
        {
            cin.clear( );
            cin.ignore(100, '\n' );
        }
  
        ok = true;	//assume correct, false means  error

        for (int i = 0; i < 6; i++)
        {
            if (data[i] < '0' || data[i] > '9' )
            {
                ok = false;	//any error sets flag.
            }
        }
    } while (!ok); // If letters still found, loop again.

    cout << data << endl;

    return 0;
}
Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.