Search code examples
c++data-structuresifstream

Trouble reading in data from a file using data structures


I have a program with car rental information and the company as well. I am trying to read it in and have it display on the terminal. However, I am only getting one company to print clearly, while the others print out only trash. I want to store the agencies with a car inventory of 5 as well, but don't exactly know how to store them without having all my information read in yet. I can only use C-Style strings also.

Here is the file I am reading in:

Hertz 93619
2014 Toyota Tacoma 115.12 1
2012 Honda CRV 85.10 0
2015 Ford Fusion 90.89 0
2013 GMC Yukon 110.43 0
2009 Dodge Neon 45.25 1

Alamo 89502
2011 Toyota Rav4 65.02 1
2012 Mazda CX5 86.75 1
2016 Subaru Outback 71.27 0
2015 Ford F150 112.83 1
2010 Toyota Corolla 50.36 1

Budget 93035
2008 Ford Fiesta 42.48 0
2009 Dodge Charger 55.36 1
2012 Chevy Volt 89.03 0
2007 Subaru Legacy 59.19 0
2010 Nissan Maxima 51.68 1

Section of my code where I need help:

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

struct car
{

        char agency[10];
        int zip;
        int year;
        char make[10];
        char model[10];
        float price;
        int available;

} ;

struct agency
{

    char company[10];
    int zip;
    int inventory[5];
};

void menu();


// Main Function
int main ()
{
    // declare variables
    const int carAmount = 15;
    int agencyAmount = 1;
    int choice;
    agency agencyLib[carAmount];
    car carLib[carAmount];
    char filename[10];
    ifstream carInData;
    bool menu1 = false;

    //prompt user for input file
    cout << " Enter file name: ";
    cin >> filename;

    // Start loop menu
    while(menu1 = true)
    {
        menu();
        carInData.open(filename);
        cin >> choice;

        if (carInData.is_open())
        {
            // read list of names into array

            for (int count = 0; count < agencyAmount; count++) 
            {
                carInData >> agencyLib[count].company >> agencyLib[count].zip;
                for (count = 0; count < carAmount; count++)
                {
                    carInData >> carLib[count].year >> carLib[count].make >> carLib[count].model >> carLib[count].price >> carLib[count].available;
                }

            }

        }

    switch (choice)
    {
        // Case 1 closes menu
        case 1:
            return 0;
            break;
        // Case 2 displays if car is available if 1, unavailable if 0
        case 2:
        // itterate through car array
            for(int count = 0; count < agencyAmount; count++)
            {
                cout << agencyLib[count].company << " " << agencyLib[count].zip << "\n";
                for(int count = 0; count < carAmount; count++)
                {
                    // Displays if car is available or not 
                    /*      if (carLib[count].available == 1)
                    cout << " Available ";
                    else
                        cout << " Unavailable ";
                    */
                    // Display Cars
                    cout << carLib[count].year << " " << carLib[count].make << " " << carLib[count].model << " " << carLib[count].price << "  " << "\n";
                }
            }
        }
    }
}

Solution

  • As a general preliminary remark, I think that even for learning purpose, this kind of exercise should better let you use std::strings instead of c-strings and std::vector for keeping a growing number of items.

    What's wrong in your code ?

    The first problem is that you use the same counter count to populate your agency array AND the car array. This will cause you very quickly to have a counter beyond the array boundaries and corrupt memory.

    Solution: rework your loop structure using 2 distinct counters.

    Next problem is that you don't identify the end of the car list of an agency. This makes it unrealistic to read more than one agency: you'll experience a failure on the stream reading that will prevent you getting anything usefull in your data.

    Solution: analyze failures on reading to identify going from cars ( first element should be a number) to a new agency ( first element is a string).

    In addition, you might have some strings which are longer than allowed by your character arrays, causing further memory corruption.

    Solution: limit the number of chars read using iomanip() to fix maximum width. This is strongly recommended unless you go for std::string

    Last issue: the variable length arrays are not a standard C++ feature, even if some popular compilers support it.

    Solution: Either use dynamic allocation with new/delete or opt for the purpose of this excercise to use a constant maximum size.

    Code snippet:

    Adapted, without choices, menus, etc. , the reading would look like:

    const int carAmount = 30;    // !!!
    const int agencyAmount = 10;  // !!!
    agency agencyLib[carAmount];
    car carLib[carAmount];
    ifstream carInData ("test.dat");
    int numCar = 0, numAgency = 0;        // !!! shows the real number of items available
    int count1, count2;                   // 
    
    cout << "Start reading" << endl;
    for (numAgency = numCar = 0; carInData && numAgency < agencyAmount; numAgency++) {
        if (!(carInData >> setw(sizeof(agencyLib[numAgency].company)) >> agencyLib[numAgency].company >> agencyLib[numAgency].zip))
            break;  // if nothing left, exit loop immediately
        for (; numCar < carAmount; numCar++) {
            carInData >> carLib[numCar].year >> setw(sizeof(carLib[numCar].make )) >>carLib[numCar].make 
                                             >> setw(sizeof(carLib[numCar].model))>>carLib[numCar].model 
                                             >> carLib[numCar].price >> carLib[numCar].available;
            if (carInData.fail()) { // here we expect a year, but get an agency string 
                carInData.clear(); 
                break;
            }
            strcpy(carLib[numCar].agency, agencyLib[numAgency].company);
            carLib[numCar].zip = agencyLib[numAgency].zip;
        }
    }
    

    And the subsequent display:

    cout << "Display agencies: " << endl; 
    for (count1 = 0; count1 < numAgency; count1++) {
        cout << agencyLib[count1].company << " " << agencyLib[count1].zip << "\n";
    }
    cout << "Cars: " << endl;
    for (count2 = 0; count2 < numCar; count2++) {
        cout << carLib[count2].agency << " " << carLib[count2].zip << ": "; 
        cout << carLib[count2].year << " " << carLib[count2].make << " " << carLib[count2].model << " " << carLib[count2].price << "  " << "\n";
    }
    

    Note that there's no link beteween agencies and cars (except the common fields), so the display just shows two distinct lists.