Thread: Data Question about Reading Files: Encapsulated Inventory Program in C++

  1. #1
    Registered User
    Join Date
    Nov 2004
    Posts
    73

    Data Question about Reading Files: Encapsulated Inventory Program in C++

    Hi everyone,

    I have a question about an inventory program that I need to do that reads data from a text file and does many different processes with it. Here is a description just so you know what is going on in the program:

    Write a C++ program that will manage an inventory of a sports store. The program should be able to handle the fields - the product id, the product description, the manufacturer, the unit cost, the quantity in stock and the reorder level. You must use a C++ class to store the information and the data must be stored in private data members. Create accessor and mutator methods for each of the data members and a function to change the quantity in stock. The class must also contain a constructor function that will accept 6 parameters. The program can hold up to 100 records but no more than that.

    The program must provide the user with a menu that allows the user to do the following operations:

    * List the current inventory (sorted by product description).
    * List the current inventory (sorted by product id).
    * Add items to the stock of a product.
    * Remove items from the stock of a product.
    * Add a new product to the inventory.
    * Remove a product from the inventory
    * Calculate the total value of the inventory.
    * List those products needing to be reordered.
    * Quit.

    I'm doing the first section right now and my question is for reading in a file which is what I need to do to list the inventory on the screen, sorted by product description. Here is my code that I currently have just for that first section (first menu option):

    Code:
    #include <iostream>
    #include <string>
    #include <fstream>
    #include <cstdlib>
    #include <conio.h>
    #include <iomanip>
    
    class Inventory {
    
       private:
          int ProdID, qty_stock, reord_lev;
          string ProdDesc, manu;
          float unitcost;
          
       public:
          Inventory(int, string, string, float, int, int);
          void listcurinv_1();
          void listcurinv_2();
          void add_stock();
          void rmv_stock();
          void add_prod();
          void rmv_prod();
          void calc_inv();
          void list_reord();
    };
    
    Inventory::Inventory(int id, string desc, string manufac, float cost, int qty, int r_level) {          // constructor
       ProdID = id;
       ProdDesc = desc;
       manu = manufac;
       unitcost = cost;
       qty_stock = qty;
       reord_lev = r_level;
    }
    
    
    void Inventory::listcurinv_1() {
    
       // read records from data file
       int i = 0;
       ifstream infile("a:\\CO856 Assignment 1\\sportsstore.txt");
       while (!infile.eof() || i < 100) {
          infile.getline(Product[i].ProdID, 10, ',');
          infile.getline(Product[i].ProdDesc, 30, ',');
          infile.getline(Product[i].manu, 15, ',');
          infile.getline(Product[i].unitcost, 10, ',');
          infile.getline(Product[i].qty_stock, 5, ',');
          infile.getline(Product[i].reord_lev, 5, '\n');
          i++;
       }
        
       // sorting by product description
       string spare;
       for (int j = 0; j < i-1; j++) {
          for (int k = 0; k < i; k++) {
             if (Product[j].ProdDesc > Product[k].ProdDesc) {
                spare = Product[j].ProdDesc;
                Product[j].ProdDesc = Product[k].ProdDesc;
                Product[k].ProdDesc = spare;
             }
          }
       }
    
       // reset i record counter to 0
       i = 0;
    
       // display data from the file
       while (!infile.eof() || i < 100) {
          cout << setw(10) << Product[i].ProdID;
          cout << setw(30) << Product[i].ProdDesc;
          cout << setw(15) << Product[i].manu;
          cout << setw(10) << Product[i].unitcost;
          cout << setw(5) << Product[i].qty_stock;
          cout << setw(5) << Product[i].reord_lev << endl;
       }
    }
    I have every field in the data text file instantiated as a product of the inventory class (I think that is the correct terminology).

    Anyway, is that the correct way of reading in a file and assigning data from it? I would also like to know the correct way to declare "Product" in the program? As of right now, I don't have it defined yet because I'm not sure of where and how to declare it in the program.

    Also, can someone confirm whether that sorting routine by product description (ascending order) is correct?

    If anyone has any help, advice or comments on this question of mine, that would be greatly appreciated.

    Thanks.

  2. #2
    Registered User
    Join Date
    Apr 2003
    Posts
    2,663
    I have every field in the data text file instantiated as a product of the inventory class (I think that is the correct terminology).
    The terminology would be: "I used the data in the text file to instantiate objects of my Inventory class."

    Anyway, is that the correct way of reading in a file and assigning data from it?
    No. Here is your loop conditional:

    while (!infile.eof() || i < 100)

    If i is 1,000 and you haven't hit the end of file, does that conditional evaluate to true or false?

    In addition, your read statement should be in the loop conditional:

    while(infile.getline( ...... ) ...... )

    The getline() function returns a stream object. If you can still read from the stream and there are no error flags set--error flags will be set when eof is encountered or if there is some other error preventing you from reading from the stream---the stream object returned by getline() will evaluate to true otherwise it will evaluate to false.

    I would also like to know the correct way to declare "Product" in the program? As of right now, I don't have it defined yet because I'm not sure of where and how to declare it in the program.
    Since an Inventory object might consist of lots of products, you could declare Product as a member variable of Inventory.

    The way your Inventory class is written now, it looks more like a Product class.

    Also, can someone confirm whether that sorting routine by product description (ascending order) is correct?
    Does it work? There are sorting methods that are more efficient than others. You can explore the subject and theory of sorting as far as you want to take it.

  3. #3
    Registered User
    Join Date
    Nov 2004
    Posts
    73

    Re: 7stud

    The reason I have the while loop check for either EOF or i < 100 is to ensure that when either the file reaches the end OR the number of product records is 100 (one or the other), it will get out of the loop and continue on. The requirements are that 100 records at most be read from the file and displayed in the program. I'd only have to worry about the file having more than 100 records if the user decided to add a ton of products because right now my data file only has 23 record lines.

    As for putting all the getline statements to read the fields of each record in the while loop, first of all, would that even work? and second, is that not really bad programming style?

    Lastly, to declare Product as a member variable, would it be best to place the declaration of Product within main()? I think that's correct but my compiler is telling me that Product should somehow be declared within the method. Normally, I always put it in main() like I've done for almost all class programs but because I need to do file processing in this program and because I'm doing reading from and writing to the file in almost all the methods, I will need access to it in the methods as well.

  4. #4
    Registered User
    Join Date
    Apr 2003
    Posts
    2,663
    The reason I have the while loop check for either EOF or i < 100 is to ensure that when either the file reaches the end OR the number of product records is 100 (one or the other), it will get out of the loop and continue on. The requirements are that 100 records at most be read from the file and displayed in the program. I'd only have to worry about the file having more than 100 records if the user decided to add a ton of products because right now my data file only has 23 record lines.
    The question is: does your loop conditional actually prevent you from reading more than 100 lines? I realize that you think it does. To explore whether it actually does, you could set up a test while loop like this:
    Code:
    int x = 4;
    while(true || x<3)
    {
         cout<<"Does this display?"<<endl;
    }
    Essentially, you need to discover whether the following evaluates to true or false:

    true || false

    After you get the correct answer to that question, reread my previous post.

    As for putting all the getline statements to read the fields of each record in the while loop, first of all, would that even work? and second, is that not really bad programming style?
    Says who? What is worse, a program that won't always work correctly or a stylish program that fails?

    Lastly, to declare Product as a member variable, would it be best to place the declaration of Product within main()?
    Member variables are initialized in a constructor, and you have to send a constructor the variables it expects.
    Last edited by 7stud; 06-08-2005 at 05:11 PM.

  5. #5
    Registered User
    Join Date
    Nov 2004
    Posts
    73

    Re: 7stud

    I tried your while test loop to see if it would prevent me from reading strictly 100 records or not and it worked.

    The text displayed within the loop because the true statement was evaluated in the while condition.

    So basically I should include every single getline() statement for every field in the record of the file INSIDE the WHILE condition statement of the loop? I'm just asking to confirm that is the case.

  6. #6
    Registered User
    Join Date
    Apr 2003
    Posts
    2,663
    I tried your while test loop to see if it would prevent me from reading strictly 100 records or not and it worked.
    It couldn't have. You will read in 100,000 records if they exist.

    The text displayed within the loop because the true statement was evaluated in the while condition.
    The text displayed in the loop because "true || false" was evaluated and "true || false" evaluates to true.

    So basically I should include every single getline() statement for every field in the record of the file INSIDE the WHILE condition statement of the loop? I'm just asking to confirm that is the case.
    The main thing you are trying to avoid is an infinite while loop. With a loop conditional like this:

    while (!infile.eof())

    a stream error other than eof can occur, which will put your infile stream into error mode. If your infile stream goes into error mode, you won't be able to read from the file anymore. So, the file will never reach eof, and your while loop will enter an infinite loop. The infinite loop problem is solved by using the read as the loop conditional:

    while(infile.getline())

    With that conditional, the loop will terminate when eof is reached, which is considered a stream error, as well as when any other stream error occurs.

    With your original conditional:

    while (!infile.eof() || i < 100)

    you can fix your conditional so that it actually prevents you from looping more than 100 times. In that case, if your infile stream enters error mode, you still won't be able to read in any data, but your loop will terminate after 100 loops, so your while loop won't enter an infinite loop. However, if your infile stream enters error mode on loop number 5, your while loop will unnecessarily loop 95 more times before terminating. How you want to handle that is something you have to decide.

    You can fix your conditional to actually prevent more than 100 loops and leave it at that.

    Or, you can write code that will stop looping the moment there is a stream error.

    Or, you can employ something in between: you can put just one getline() in the while condition, and then if the stream enters error mode, the loop will attempt to keep reading with the other getline()'s, and then stop when it tries to read the next line, i.e. when the getline() in the while conditional tries to read in its chunk of data and fails. If your sense of style demands that you have no more than one getline() in the while condition, you can also read in a whole line of data in one shot, and then programmatically divide the data up into chunks between the commas.
    Last edited by 7stud; 06-09-2005 at 01:05 PM.

  7. #7
    VA National Guard The Brain's Avatar
    Join Date
    May 2004
    Location
    Manassas, VA USA
    Posts
    903
    while we are on the .eof() topic.. I have experimented with this method:

    Code:
    string input;
    int counter = 0;
    
    while(infile >> input)  //<--- Is it ok to do this?   
    {
         //Assume all list attributes are string/char
         
         list[counter].product  = input;
         infile >> input;
         list[counter].description = input;
         infile >> input;
         list[counter].quantity = input;
         infile >> input;
         list[counter].reorder  = input;
         counter++;
    }
    This seems to be working fine for me.. is this always a safe method to use? I don't think I have seen anyone else use this method so that's why I am asking.
    Last edited by The Brain; 06-09-2005 at 03:52 PM.
    • "Problem Solving C++, The Object of Programming" -Walter Savitch
    • "Data Structures and Other Objects using C++" -Walter Savitch
    • "Assembly Language for Intel-Based Computers" -Kip Irvine
    • "Programming Windows, 5th edition" -Charles Petzold
    • "Visual C++ MFC Programming by Example" -John E. Swanke
    • "Network Programming Windows" -Jones/Ohlund
    • "Sams Teach Yourself Game Programming in 24 Hours" -Michael Morrison
    • "Mathmatics for 3D Game Programming & Computer Graphics" -Eric Lengyel

  8. #8
    Registered User
    Join Date
    Apr 2003
    Posts
    2,663
    This seems to be working fine for me.. is this always a safe method to use? I don't think I have seen anyone else use this method so that's why I am asking.
    There are differerent ways to read input. getline() is one way, and using the operator>> is another way. They read input differently. However, they both return a stream object, and using either of them as the loop conditional has the same effect: if any error flags are set for the stream object, the stream object will evaluate to false, otherwise it will evaluate to true.

    Using either infile>>input or infile.getline() as the loop conditional is the standard method posted on these boards for reading input.

    You should note that if the input is formatted like this:

    something1,something2,something3,something4

    the operator>> won't work. In addition, these lines:
    Code:
    infile >> input;
    list[counter].description = input;
    infile >> input;
    list[counter].quantity = input;
    could be written more succinctly like this:
    Code:
    infile>>list[counter].description;
    infile>>list[counter].quantity;
    Last edited by 7stud; 06-09-2005 at 04:28 PM.

  9. #9
    Registered User
    Join Date
    Nov 2004
    Posts
    73
    I'm getting some compiler errors trying to read from the file. It is saying that 'record' has not been declared although I clearly declared it in main() as an instance of the class inventory as I've always done for programs involving classes.

    Here is my code as well as an attached .cpp version of the program for anybody wishing to run it. I also attached the .txt data file that I am reading in which as you will see is reading in every field separated by either a comma delimiter or new line which brings you to the next record in the file.

    Code:
    #include <iostream>
    #include <string>
    #include <fstream>
    #include <cstdlib>
    #include <conio.h>
    #include <iomanip>
    
    
    class Inventory {
    
       private:
          int ProdID, qty_stock, reord_lev;
          string ProdDesc, manu;
          float unitcost;
          
       public:
          Inventory(int, string, string, float, int, int);
          void listcurinv_1();
          void listcurinv_2();
          void add_stock();
          void rmv_stock();
          void add_prod();
          void rmv_prod();
          void calc_inv();
          void list_reord();
    };
    
    
    Inventory::Inventory(int id, string desc, string manufac, float cost, int qty, int r_level) {
       ProdID = id;
       ProdDesc = desc;
       manu = manufac;
       unitcost = cost;
       qty_stock = qty;
       reord_lev = r_level;
    }
    
    
    void Inventory::listcurinv_1() {
       int i = 0;
       ifstream infile("a:\\CO856 Assignment 1\\sportsstore.txt");
    
       // initial read of data
       infile.getline(record[i].ProdID, 10, ',');
       infile.getline(record[i].ProdDesc, 30, ',');
       infile.getline(record[i].manu, 15, ',');
       infile.getline(record[i].unitcost, 10, ',');
       infile.getline(record[i].qty_stock, 5, ',');
       infile.getline(record[i].reord_lev, 5);
       i++;
    
    
       // test for either EOF or 100 records in the file and stop reading 
          records when one of those statements is true
       while (!infile.eof() || i < 100) {
          infile.getline(record[i].ProdID, 10, ',');
          infile.getline(record[i].ProdDesc, 30, ',');
          infile.getline(record[i].manu, 15, ',');
          infile.getline(record[i].unitcost, 10, ',');
          infile.getline(record[i].qty_stock, 5, ',');
          infile.getline(record[i].reord_lev, 5);
          i++;
       }
    
       // sort by product description
       string spare;
       for (int j = 0; j < i-1; j++) {
          for (int k = 0; k < i; k++) {
             if (record[j].ProdDesc > record[k].ProdDesc) {
                spare = record[j].ProdDesc;
                record[j].ProdDesc = record[k].ProdDesc;
                record[k].ProdDesc = spare;
             }
          }
       }
    
       i = 0;
    
       // display records on screen
       while (!infile.eof() || i < 100) {
          cout << setw(10) << record[i].ProdID;
          cout << setw(30) << record[i].ProdDesc;
          cout << setw(15) << record[i].manu;
          cout << setw(10) << record[i].unitcost;
          cout << setw(5) <<  record[i].qty_stock;
          cout << setw(5) <<  record[i].reord_lev << endl;
       }
    }
    
    int main() {
       Inventory record[100];
       record.listcurinv_1();
       getche();
       return 0;
    }
    This is probably one of the last obsctales I need to get through to be well on my way with this program. How is it not recognizing 'record' as being declared when I have it declared in main()?

    If anyone has any advice or help to give for this problem, that would be great. Thanks.
    Last edited by goron350; 06-10-2005 at 03:03 PM.

  10. #10
    Registered User
    Join Date
    Apr 2003
    Posts
    2,663
    It is saying that 'record' has not been declared although I clearly declared it in main() as an instance of the class inventory
    This is the only way you can declare an instance of your Inventory class:

    Inventory myInv(10, "toothbrush", "Colgate", 1.99, 1, 100);

    Where exactly did you do that for record?

    Each element in record is an instance of your Inventory class. However, record is not an instance of your Inventory class--it's a C++ entity known as an array. Only instances of your class can call member functions.

    In addition, when you create an array, the compiler fills the array with default instances of your Inventory class. However, when you define a constructor for a class, the compiler won't automatically supply a default constructor for your class. In your Inventory class, you defined a constructor, so the compiler doesn't supply your class with a default constructor, and as a result you cannot create an array of Inventory objects--you will get an error saying there is no default constructor. To fix that, you need to define a default constructor for your class.
    Last edited by 7stud; 06-10-2005 at 09:02 PM.

  11. #11
    Registered User
    Join Date
    Nov 2004
    Posts
    73

    Re: 7stud

    OK. So should I just create a default constructor in addition to the other one I currently have or get rid of the other one and just have the default constructor?

  12. #12
    Registered User
    Join Date
    Apr 2003
    Posts
    2,663
    ...or get rid of the other one and just have the default constructor?
    Do that.

  13. #13
    Registered User
    Join Date
    Apr 2003
    Posts
    2,663
    while (!infile.eof() || i < 100)
    You are still not grasping how a computer evaluates a compound conditional. You say that the compound conditional does this:

    // test for either EOF or 100 records in the file and stop reading
    // records when one of those statements is true

    However, that is not what the computer does. The computer does not read your comment and based on what you say in the comment execute your wishes. I provided this example for you:
    Code:
    int x = 4;
    while(true || x<3)
    {
         cout<<"Does this display?"<<endl;
    }
    and you agreed the message gets displayed. In that compound conditional, x is not smaller than 3, so the compound conditional becomes:

    while(true || false)

    and you have witnessed the fact that the loop continues executing and displays the message. The only way the loop can continue excuting is if the compound conditional evaluates to true, right? So, even if you don't have a C++ book, you should be able to arrive at the conclusion that the compound conditional true||false evaluates to true based on your observation that the while loop continues excuting and the message inside the loop is displayed.

    Now, look at your conditional:

    while (!infile.eof() || i < 100)

    Suppose there are 500 records in the file, and you have read 100 of the records, and now i = 100. Has end of file been hit yet? No. So, infile.eof() returns false, and in your conditional you have:

    !infile.eof()

    so !infile.eof() is the same thing as !false, which equals true, and your conditional becomes:

    while(true || i<100)

    But, since i = 100, then i is not smaller than 100, so i<100 is false and the conditional becomes:

    while(true || false)

    Isn't that the identical compound conditional in the example above?? And, with that compound conditional didn't the loop continue executing and display the message??? How is your loop going to stop executing when i reaches 100??

    If you still can't figure it out, do an empirical test: create a file with 4,000 product descriptions. Run your loop, and display i inside the loop, e.g.

    cout<<i<<endl;

    so that i displays everytime time through the loop. See if your loop stops at 100 or 4,000. Of course, you might get a little weary typing in 4,000 product descriptions, so you could make the loop conditional i < 3 and put five product descriptions in the file. When does the loop stop?
    Last edited by 7stud; 06-10-2005 at 10:01 PM.

  14. #14
    Registered User
    Join Date
    Nov 2004
    Posts
    73

    Re: 7stud

    Yes, I understand the loop needs to be changed to make it read the data properly but BEFORE I even get that far I need to fix this problem with 'record' not being declared and unfortunately the default constructor didn't fix the problem. I can't even test the reading of the input file until it compiles and it won't because of that same 'record' has not been declared error.

    another error says request for member 'listcurinv_1' in 'record' which is of non-aggregate type 'Inventory[100]'

    Once I get past these two errors, then I'll be able to see what goes on with the file input and make the necessary changes to the loop.

  15. #15
    Registered User
    Join Date
    Apr 2003
    Posts
    2,663
    One of the first things you will earn about debugging, and how soon depends on how much blood you spill, is:

    ALWAYS CORRECT ANY ERRORS AS SOON AS YOU DISCOVER THEM.

    Known errors often take care of other errors. What errors are you getting now, and on which lines in the code?
    Last edited by 7stud; 06-10-2005 at 10:36 PM.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. reading data contents from files in c++
    By shaheel in forum C++ Programming
    Replies: 4
    Last Post: 04-13-2008, 09:02 PM
  2. Reading large complicated data files
    By dodzy in forum C Programming
    Replies: 16
    Last Post: 05-17-2006, 04:57 PM
  3. Question about IOStream and reading strings from files
    By kingpinzs in forum C++ Programming
    Replies: 22
    Last Post: 12-13-2005, 11:29 AM
  4. Program Crashing
    By Pressure in forum C Programming
    Replies: 3
    Last Post: 04-18-2005, 10:28 PM
  5. reading data format into program
    By lambs4 in forum C Programming
    Replies: 1
    Last Post: 10-23-2003, 02:27 PM