whiteferrari
whiteferrari

Reputation: 5

How to read data from a file into an array of a class

This assignment involves making a program that will read in a car agency and its cars(+car information) from a text file.

Specifically I am supposed to read in a car agency name, a car agency zip code, and then a car's year, make, model, its sensors, and its availability.

I'm having a really difficult time reading in the sensors due to the way it looks in the text file with '{' and '}'. Also the max sensors is 3, but there isn't always 3 for each car.

I've created Sensor, Car, and Agency classes.

The input file (HighTechAgency.txt):

Enterprise 89502
2014 Toyota Tacoma 115.12 {gps} 1
2012 Honda CRV 85.10 {camera lidar} 0
2011 Toyota Rav4 65.02 {} 0
2009 Dodge Neon 45.25 {camera lidar radar} 1
2015 Ford Fusion 90.89 {lidar} 0

Sensor.h:

#ifndef SENSOR_H
#define SENSOR_H
#define MAX_CHAR 256

class Sensor{
    char type [MAX_CHAR];
    float extracost;
    static int gps_cnt, camera_cnt, lidar_cnt, radar_cnt;
public:
    Sensor();
    Sensor(char[]);
    Sensor(Sensor & other); 
    char* getType();
    float getExtraCost();
    void setType(char[]);
    void setExtraCost(char[]);
    void sensorCnt(char[]);
    static int getGPSCnt();
    static int getCameraCnt();
    static int getLidarCnt();
    static int getRadarCnt();
    static void resetGPSCnt();
    static void resetCameraCnt();
    static void resetLidarCnt();
    static void resetRadarCnt();
};

bool operator==(Sensor & sensor1, Sensor & sensor2);

#endif

Sensor.cpp:

#include "Sensor.h"
#include "myStrings.h"

int Sensor::gps_cnt = 0;
int Sensor::camera_cnt = 0;
int Sensor::lidar_cnt = 0;
int Sensor::radar_cnt = 0;

Sensor::Sensor(){
    type[0] = '\0';
    extracost = 0;
}

Sensor::Sensor(char sensorName[]){
    setType(sensorName);
}

Sensor::Sensor(Sensor & other){ 
    setType(other.type);
}

char* Sensor::getType(){
    return type;
}

float Sensor::getExtraCost(){
    return extracost;
}

void Sensor::setType(char typeSensor[]){
    myStrings::strCpy(type,typeSensor);
    sensorCnt(typeSensor);
    setExtraCost(typeSensor);
}

void Sensor::setExtraCost(char newType[]){
    if(myStrings::strCmp(newType,"gps") == 0){
        extracost = 5;
    }
    if(myStrings::strCmp(newType, "camera") == 0){
        extracost = 10;
    }
    if(myStrings::strCmp(newType, "lidar") == 0){
        extracost = 15;
    }
    if(myStrings::strCmp(newType, "radar") == 0){
        extracost = 20;
    }
    if(myStrings::strCmp(newType, "") == 0){
        extracost = 0;
    }
}

void Sensor::sensorCnt(char sensorType[]){
    if(myStrings::strCmp(sensorType,"gps") == 0){
        gps_cnt++;
    }
    if(myStrings::strCmp(sensorType, "camera") == 0){
        camera_cnt++;
    }
    if(myStrings::strCmp(sensorType, "lidar") == 0){
        lidar_cnt++;
    }
    if(myStrings::strCmp(sensorType, "radar") == 0){
        radar_cnt++;
    }
}

int Sensor::getGPSCnt(){
return gps_cnt;
}

int Sensor::getCameraCnt(){
    return camera_cnt;
}

int Sensor::getLidarCnt(){
    return lidar_cnt;
}

int Sensor::getRadarCnt(){
    return radar_cnt;
}

void Sensor::resetGPSCnt(){
    gps_cnt = 0;
}

void Sensor::resetCameraCnt(){
    camera_cnt = 0;
}

void Sensor::resetLidarCnt(){
    lidar_cnt = 0;
}

void Sensor::resetRadarCnt(){
    radar_cnt = 0;
}

bool operator==(Sensor& sensor1, Sensor& sensor2){
    return (myStrings::strCmp(sensor1.getType(),sensor2.getType()) == 0);
}

Car.h:

#ifndef CAR_H
#define CAR_H
#define MAX_SENS 3

#include "Sensor.h"

class Car{
    char make[MAX_CHAR];
    char model[MAX_CHAR];
    int year;
    Sensor sensor[MAX_SENS];
    float baseprice, finalprice;
    bool available;
public:
    Car();
    Car(char[], char[], int, float, Sensor[], bool);
    Car(Car & other);
    char* getMake();
    char* getModel();
    int getYear();
    float getBasePrice();
    float getFinalPrice(); 
    bool getAvailable();
    void setMake(char[]);
    void setModel(char[]);
    void setYear(int);
    void setBasePrice(float);
    void setAvailable(bool);
    void updatePrice();
    void print();
    float estimateCost(int);
    void addSensor(Sensor[]);
};

#endif

Car.cpp:

#include <iostream>
#include "Car.h"
#include "myStrings.h"

Car::Car(){
    make[0] = '\0';
    model[0] = '\0';
    year = 0;
    baseprice = 0;
    finalprice = 0;
    available = false;
}

Car::Car(char newMake[], char newModel[], int newYear, float newBasePrice, Sensor newSensor[], bool newAvailable){
    setMake(newMake);
    setModel(newModel);
    setYear(newYear);
    setBasePrice(newBasePrice);
    setAvailable(newAvailable);
    addSensor(newSensor);
}

Car::Car(Car & other){
    setMake(other.make);
    setModel(other.model);
    setYear(other.year);
    setBasePrice(other.baseprice);
    setAvailable(other.available);
    addSensor(other.sensor);
}

char* Car::getMake(){
    return make;
}

char* Car::getModel(){
    return model;
}

int Car::getYear(){
    return year;
}

float Car::getBasePrice(){
    return baseprice;
}

float Car::getFinalPrice(){
    return finalprice;
}

bool Car::getAvailable(){
    return available;
}

void Car::setMake(char m_make[]){
    myStrings::strCpy(make,m_make);
}

void Car::setModel(char m_model[]){
    myStrings::strCpy(model,m_model);
}

void Car::setYear(int m_year){
    year = m_year;
}

void Car::setBasePrice(float m_baseprice){
    baseprice = m_baseprice;
}

void Car::setAvailable(bool m_available){
    available = m_available;
}

void Car::updatePrice(){
    for (int i = 0; i < MAX_SENS; i++){
        finalprice += sensor[i].getExtraCost();
    }
    finalprice += baseprice;
}

void Car::print(){
    updatePrice();
    std::cout << year << " " << make << " " << model << ", $" << baseprice << " per day,";
    for (int i = 0; i < MAX_SENS; i++){
        std::cout << " " << sensor[i].getType() << " ";
    }
    std::cout << " Available: " << std::boolalpha << available;
    std::cout << std::endl;
}

float Car::estimateCost(int days){
    return (finalprice * days); 
}

void Car::addSensor(Sensor sensorAdd[]){
    for (int i = 0; i < MAX_SENS; i++){
        char temp[MAX_CHAR];
        myStrings::strCpy(temp,sensorAdd[i].getType());
        sensor[i].setType(temp);
    }
}

Agency.h:

#ifndef AGENCY_H
#define AGENCY_H
#define MAX_CARS 5

#include "Car.h"

class Agency{
    char name[MAX_CHAR];
    int zipcode;
    Car inventory[MAX_CARS];
public:
    Agency();
    char* getName();
    int getZipcode();
    void setName(char[]);
    void setZipcode(int);
    void readAllData(Car []);
    void printAllData(Car []);
    void printAvailableCars(Car []); 
};

#endif

Agency.cpp:

#include <iostream>
#include <fstream>
#include "Agency.h"
#include "myStrings.h"
#define MAX_LEN 25

Agency::Agency(){
    name[0] = '\0';
    zipcode = 0;
}

char* Agency::getName(){
    return name;
}

int Agency::getZipcode(){
    return zipcode;
}

void Agency::setName(char newName[]){
    myStrings::strCpy(name, newName);
}

void Agency::setZipcode(int newZipcode){
    zipcode = newZipcode;
}

void Agency::readAllData(Car data[]){
    char inputFile[MAX_LEN];
    int tempYear;
    char tempMake[MAX_CHAR];
    char tempModel[MAX_CHAR];
    float tempPrice;
    char tempSensor[MAX_CHAR];
    Sensor sens[MAX_SENS];
    bool tempAvailable;
    std::ifstream inputStream;

    std::cout << "Enter input file name: ";
    std::cin >> inputFile;

    inputStream.open(inputFile);

    if(inputStream.is_open()){

        std::cout << std::endl;
        std::cout << "*" << inputFile << " has been read.*" << std::endl;
        std::cout << std::endl;
        inputStream >> name >> zipcode;

        for(int i = 0; i < MAX_CARS; i++){
            inputStream >> tempYear >> tempMake >> tempModel >> tempPrice >> tempSensor >> tempAvailable;

            inventory[i].setYear(tempYear);
            inventory[i].setMake(tempMake);
            inventory[i].setModel(tempModel);
            inventory[i].setBasePrice(tempPrice);

            for (int j = 0; j < MAX_SENS; j++){
                sens[j].setType(tempSensor);
                myStrings::strCpy(tempSensor,sens[j].getType());
                inventory[i].addSensor(sens);
            }
        inventory[i].setAvailable(tempAvailable);
        }
    }

if(!inputStream.is_open()){
        std::cerr << "Failed to open file: " << inputFile << std::endl;
    }
}

void Agency::printAllData(Car data[]){
    std::cout << name << " " << zipcode;
    std::cout << std::endl;

    for(int i = 0; i < MAX_CARS; i++){
        inventory[i].print();
    }
    std::cout << std::endl;
}

void Agency::printAvailableCars(Car data[]){
    std::cout << name << " " << zipcode;
    for(int i = 0; i < MAX_CARS; i++){
        if(inventory[i].getAvailable() == 1){
        std::cout << inventory[i].getYear() << " " << inventory[i].getMake() << " " << inventory[i].getModel() << ", " << "$" << inventory[i].getFinalPrice() << " per day" << std::endl;
        }
    }
    std::cout << std:: endl;    
}

Proj4.cpp (driver): (testing reading input for now)

#include <iostream>
#include <fstream>
#include "Agency.h"

int main()
{
    Agency data;
    Car cars[9];
    data.readAllData(cars);
    data.printAllData(cars);
    return 0;
}

This is what is getting printed to the terminal.

Enterprise 89502
2014 Toyota Tacoma, $115.12 per day, {gps}  {gps}  {gps}  Available: true
2012 Honda CRV, $85.1 per day, {camera  {camera  {camera  Available: false
2012 Honda CRV, $85.1 per day, {camera  {camera  {camera  Available: false
2012 Honda CRV, $85.1 per day, {camera  {camera  {camera  Available: false
2012 Honda CRV, $85.1 per day, {camera  {camera  {camera  Available: false

But I want is:

Enterprise 89502
2014 Toyota Tacoma, $115.12 per day, {gps}  Available: true
2012 Honda CRV, $85.1 per day, {camera  lidar}  Available: false
2011 Toyota Rav4, $65.02 per day, {}  Available: false
2009 Dodge Neon, $45.25 per day, {camera  lidar  radar}  Available: true
2015 Ford Fusion, $90.89 per day, {lidar}  Available: true

I know there is a problem where I'm reading my input. Specifically here in the function readAllData in Agency.cpp.

void Agency::readAllData(Car data[]){
    char inputFile[MAX_LEN];
    int tempYear;
    char tempMake[MAX_CHAR];
    char tempModel[MAX_CHAR];
    float tempPrice;
    char tempSensor[MAX_CHAR];
    Sensor sens[MAX_SENS];
    bool tempAvailable;
    std::ifstream inputStream;

    std::cout << "Enter input file name: ";
    std::cin >> inputFile;

    inputStream.open(inputFile);

    if(inputStream.is_open()){

        std::cout << std::endl;
        std::cout << "*" << inputFile << " has been read.*" << std::endl;
        std::cout << std::endl;
        inputStream >> name >> zipcode;

        for(int i = 0; i < MAX_CARS; i++){
            inputStream >> tempYear >> tempMake >> tempModel >> tempPrice >> tempSensor >> tempAvailable;

            inventory[i].setYear(tempYear);
            inventory[i].setMake(tempMake);
            inventory[i].setModel(tempModel);
            inventory[i].setBasePrice(tempPrice);

            for (int j = 0; j < MAX_SENS; j++){
                sens[j].setType(tempSensor);
                myStrings::strCpy(tempSensor,sens[j].getType());
                inventory[i].addSensor(sens);
            }
        inventory[i].setAvailable(tempAvailable);
        }
    }

if(!inputStream.is_open()){
        std::cerr << "Failed to open file: " << inputFile << std::endl;
    }
}

I think what's happening is that after it reads the price, it looks for the next set of characters and stores that one "word" into tempSensor.

For the first line:

2014 Toyota Tacoma 115.12 {gps} 1

It stores {gps} as tempSensor, and stores it three times.

Hence:

2014 Toyota Tacoma, $115.12 per day, {gps}  {gps}  {gps}  Available: true

For second line:

2012 Honda CRV 85.10 {camera lidar} 0 

It stores {camera as tempSensor. And stores {camera three times. Once it hits the whitespace, then it moves to tempAvailable, but it reads in lidar, and since it expects a bool input, then the it messes up the program and that's why it keeps printing the data for Honda.

Hence:

2012 Honda CRV, $85.1 per day, {camera  {camera  {camera  Available: false
2012 Honda CRV, $85.1 per day, {camera  {camera  {camera  Available: false
2012 Honda CRV, $85.1 per day, {camera  {camera  {camera  Available: false
2012 Honda CRV, $85.1 per day, {camera  {camera  {camera  Available: false

I know that i need to implement something so that it knows to look for 3 words with tempSensor, and to exclude the '{' and '}'.

I'm stuck on how to do that though.

Upvotes: 0

Views: 126

Answers (2)

PaulMcKenzie
PaulMcKenzie

Reputation: 35440

You could use std::istringstream and not have to use all of the logic you have now to parse the string. You can even keep all of your character arrays and not need to use std::string:

#include <sstream>
#include <iostream>

int main()
{
    char s[] = "2014 Toyota Tacoma 115.12 {gps} 1";
    int year;
    char make1[100], make2[100];
    double price;
    char category[100];
    int num;
    std::istringstream iss(s);
    iss >> year >> make1 >> make2 >> price >> category >> num;
    std::cout << year << "\n";
    std::cout << make1 << "\n";
    std::cout << make2 << "\n";
    std::cout << price << "\n";
    std::cout << category  << "\n";
    std::cout << num;    
}    

Output:

2014
Toyota
Tacoma
115.12
{gps}
1

Here is a full implementation of parsing the data if the curly brace data has more than one element. It basically sets pointers up within the string, and then feeds that part of the string to the std::istringstream to have it do its magic of parsing:

#include <sstream>
#include <iostream>
#include <cstring>

int main()
{
    char s[] = "2014 Toyota Tacoma 115.12 {gps gps2 gps3} 1";

    // get the position of the braces and save this to a string
    char *pos1 = strchr(s,'{') + 1; // opening brace pointer
    char *pos2 = strchr(s,'}');     // closing brace pointer
    char curly_string[100] = {};
    strncpy(curly_string, pos1, pos2-pos1); // copy everything between these pointers

    // The last value is always located after the closing brace
    char *numpos = pos2 + 1;

    // parse the individual entries
    int year;
    char make1[100], make2[100];
    double price;
    int num;

    // Output the stuff before the curly brace
    std::istringstream iss(s);
    iss >> year >> make1 >> make2 >> price; 
    std::cout << year << "\n";
    std::cout << make1 << "\n";
    std::cout << make2 << "\n";
    std::cout << price << "\n";

    // Output the curly string stuff and its contents
    iss.clear();
    iss.str(curly_string);
    char one_category[100];
    while (iss >> one_category)
        std::cout << one_category << "\n";

    // output the last item (the number)
    iss.clear();
    iss.str(numpos);
    iss >> num;
    std::cout << num;
}    

Output:

2014
Toyota
Tacoma
115.12
gps
gps2
gps3
1

Note that this is much easier with std::string as the type instead of char arrays. Using the char arrays is wasteful, since I had to declare arrays with 100 entries.

Upvotes: 1

Thomas Matthews
Thomas Matthews

Reputation: 57678

If you must use character arrays, then use istream::getline with a delimiter:

static const unsigned int MAX_LENGTH_MAKE_MODEL = 256;
char make_and_model[MAX_LENGTH_MAKE_MODEL];
inputStream.getline(make_and_model, MAX_LENGTH_MAKE_MODEL, ','];

Or you could read in the entire line, then search it and extract substrings:

static const unsigned int MAX_LINE_LENGTH = 4096;
char line_of_text[MAX_LINE_LENGTH];
inputStream.getline(line_of_text, MAX_LINE_LENGTH, '\n');
char * p_comma = strchr(line_of_text, ',');
//...

Also research the strtok function.

Upvotes: 0

Related Questions