Creating a program that searches through text files and provides the necessary information

66 Views Asked by At

I am having trouble implementing a search function for each option case with my code. I am not sure how to actually search the files for the information that I need the code to output. I will attach the code I have so far.

Cars.h file

class Cars {
   protected:
      string make, model;
      double price;
      int miles;
   public:
      Cars(string make, string model, double price, int miles);
      string getMake();
      string getModel();
      double getPrice();
      int getMiles();
      virtual void displayInfo();
};

Cars.cpp file

Cars::Cars(string make, string model, double price, int miles) {
    this -> make = make;
    this -> model = model;
    this -> price = price;
    this -> miles = miles;
}

string Cars::getMake() {
    return make;
}

string Cars::getModel() {
    return model;
}

double Cars::getPrice() {
    return price;
}

int Cars::getMiles() {
    return miles;
}

void Cars::displayInfo() {
    cout << "Make: " << make << endl;
    cout << "Model: " << model << endl;
    cout << "Price: $" << price << endl;
    cout << "Miles: " << miles << endl;
}

Sedans.h file

class Sedans : public Cars {
    private:
        bool hybrid;
    public:
        Sedans(string make, string model, double price, int miles, bool hybrid);
        void displayInfo() override;
};

Sedans.cpp file

Sedans::Sedans(string make, string model, double price, int miles, bool hybrid) : Cars(make, model, price, miles) {
    this -> make = make;
    this -> model = model;
    this -> price = price;
    this -> miles = miles;
    this -> hybrid = hybrid;
}

void Sedans::displayInfo() {
    Cars::displayInfo();
    cout << "Hybrid: " << (hybrid ? "yes" : "no") << endl;
}

Trucks.h file

class Trucks : public Cars {
    private:
        string engine;
    public:
        Trucks(string make, string model, double price, int miles, string engine);
        void displayInfo() override;
};

Trucks.cpp file

Trucks::Trucks(string make, string model, double price, int miles, string engine) : Cars(make, model, price, miles) {
    this -> make = make;
    this -> model = model;
    this -> price = price;
    this -> miles = miles;
    this -> engine = engine;
}

void Trucks::displayInfo() {
    Cars::displayInfo();
    cout << "Engine: " << engine << endl;
}

main.cpp file

using namespace std;

int main() {
    ifstream sedansFile;
    sedansFile.open("sedans-1.txt");
    ifstream trucksFile;
    trucksFile.open("trucks-1.txt");

    int option;
    string searchText;
    bool exit = false;

    cout << "**********SEARCH MENU***********" << endl;
    cout << "Search Make..............Press 1" << endl;
    cout << "Search Model.............Press 2" << endl;
    cout << "Search Price.............Press 3" << endl;
    cout << "Search Miles.............Press 4" << endl;
    cout << "Exit.....................Press 5" << endl;

    while (!exit) {
        cout << "\nSelect Option: ";
        cin >> option;

        if (option == 5) {
            exit = true;
            continue;
        }

        cout << "Enter Search Text: ";
        cin.ignore();
        getline(cin, searchText);
        cout << endl;

    }

    return 0;
}

Basically I need this output:

Select Option: 1

Enter Search Text: Toyota
Make: Toyota
Model: Tundra
Miles: 68058
Price: $35348.00
Engine: V6

Make: Toyota
Model: Camry
Miles: 20597
Price: $35348.00
Hybrid: yes

Each option will give different search criteria and give me the cars that match.

The text files are as follows:

Sedans-1.txt

Toyota Camry 35348 20597 yes
Lexus ES 55609 4693 yes
Honda Accord 22987 11768 no

Trucks-1.txt

Toyota Tundra 34271 68058 V6
Ford F-150 55609 9785 V8
Nissan Titan 33990 2677 V6

I can't use vector or pushback in this problem. Ideally I want the most basic way to solve this without getting too involved. Solve as if a beginner is coding this.

1

There are 1 best solutions below

0
tbxfreeware On

I am having trouble implementing a search function for each option case with my code. I am not sure how to actually search the files for the information that I need the code to output.

I can't use vector or pushback in this problem.

Hmm... no vector. Okay. So the plan is to open the files, and make a sequential scan of all the records each time a search is requested.

This answer focuses on the search routines. At the end, demonstration code for one of them, function display_trucks_for_given_make, is given. Along the way, several other functions are developed.

  • Utility functions – Functions such as to_int, to_double, and trim_whitespace_view are taken from my toolbox.

  • Stream extraction operatoroperator>>, for class Trucks, reads a single record from the file.

Convert strings into numbers

When price and miles are read from a file, they will first be stored as strings. Those strings will be converted, respectively, into a double and int using the functions below.

Note: a commenter correctly pointed out that dollar amounts are best converted to pennies, and stored as int values. Otherwise, round-off errors can be problematic. This answer, however, works with type double. Conversion to pennies is left up to the OP.

For my own work, I use a template function that converts any numeric type. The template function returns a std::optional, which is empty when the conversion fails.

For this answer, which is geared towards beginners, I will ditch the template, and return a bool, to indicate success or failure. The number itself is returned in a reference parameter.

Prior to conversion, trim_whitespace_view is called to strip off any leading or trailing spaces.

The conversion is carried out using std::from_chars, and must consume the entire input string. If there are any stray characters leftover, conversion fails.

#pragma once
// tbx.utility.h

#include <string>
#include <string_view>

namespace tbx
{
    bool to_double(std::string_view sv, double& value) noexcept;
    bool to_int(std::string_view sv, int& value) noexcept;
    auto to_lower(std::string s) noexcept -> std::string;
    void to_lower_in_place(std::string& s) noexcept;
    auto trim_whitespace_view(std::string_view sv) noexcept -> std::string_view;
}
// end file: tbx.utility.h
// tbx.utility.cpp
#include <algorithm>
#include <cctype>
#include <charconv>
#include <string>
#include <string_view>
#include "tbx.utility.h"

namespace tbx
{
    //==================================================================
    // to_double
    //==================================================================
    bool to_double(std::string_view sv, double& value) noexcept
    {
        sv = tbx::trim_whitespace_view(sv);
        auto const end{ sv.data() + sv.size() };
        auto [ptr, ec] = std::from_chars(sv.data(), end, value);
        return ec == std::errc{} && ptr == end;
    }
    //==================================================================
    // to_int
    //==================================================================
    bool to_int(std::string_view sv, int& value) noexcept
    {
        sv = tbx::trim_whitespace_view(sv);
        auto const end{ sv.data() + sv.size() };
        auto [ptr, ec] = std::from_chars(sv.data(), end, value);
        return ec == std::errc{} && ptr == end;
    }
    //==================================================================
    // to_lower
    //==================================================================
    std::string to_lower(std::string s) noexcept
    {
        tbx::to_lower_in_place(s);
        return s;
    }
    //==================================================================
    // to_lower_in_place
    //==================================================================
    void to_lower_in_place(std::string& s) noexcept
    {
        std::transform(s.begin(), s.end(), s.begin(),
            [](unsigned char c) {
                return static_cast<char>(std::tolower(c));
            }
        );
    }
    //==================================================================
    // trim_whitespace_view
    //==================================================================
    auto trim_whitespace_view(std::string_view sv) noexcept -> std::string_view
    {
        // Trim leading and trailing whitespace from string_view `sv`.
        auto const first{ sv.find_first_not_of(" \f\n\r\t\v") };
        if (first == std::string_view::npos)
            return {};
        auto const last{ sv.find_last_not_of(" \f\n\r\t\v") };
        enum : std::string_view::size_type { one = 1u };
        return sv.substr(first, (last - first + one));
    }
}
// end file: tbx.utility.cpp

Stream extraction functions

The data files both are structured with only one "word" per field. None of the fields include spaces.

In the future, perhaps they could be changed to CSV files (i.e., "comma-separated values"), which would allow a field to contains spaces. With CSV files, the delimiter character, which is used to separate fields, is (usually) a comma.

The files sedans-1.txt and trucks-1.txt can both be treated as CSV files where the delimiter is the space character. Later on, by simply changing the delimiter to a comma, the functions below would work with true CSV files.

The example below shows how to code a stream extraction operator for the Trucks file. It is implemented as a hidden friend. That is why it is coded within class Trucks.

If extraction fails for any of the fields, or one of the numeric fields fails to convert, operator>> throws a std::runtime_error.

I have taken the liberty of adding a default constructor to class Trucks. That allows you to construct an empty Trucks object, prior to invoking the stream extraction operator.

Note, as well, that I have included header tbx.utility.h, from the preceding section.

The four search functions declared at the end of Trucks header are discussed in the next section.

#pragma once
// Trucks.h
#include <iostream>
#include <sstream>
#include <string>
#include "tbx.utility.h"
#include "Cars.h"

class Trucks : public Cars {
private:
    std::string engine;
public:
    Trucks();
    Trucks(std::string make, std::string model, double price, int miles, std::string engine);
    void displayInfo() override;

    friend std::istream& operator>> (std::istream& ist, Trucks& truck)
    {
        char const delim{ ' ' };  // for CSV, change delim to ','
        if (std::string s; std::getline(ist, s))
        {
            std::istringstream iss{ s };
            std::string make, model, price_str, miles_str, engine;
            double price{};
            int miles{};
            if (!std::getline(iss, make, delim) ||
                !std::getline(iss, model, delim) ||
                !std::getline(iss, price_str, delim) || !tbx::to_double(price_str, price) ||
                !std::getline(iss, miles_str, delim) || !tbx::to_int(miles_str, miles) ||
                !std::getline(iss, engine)
                )
                throw std::runtime_error("Truck: extraction failed: \"" + s + '"');

            // "Strong guarantee" - "all or nothing"
            truck.make = make;
            truck.model = model;
            truck.price = price;
            truck.miles = miles;
            truck.engine = engine;
        }
        return ist;
    }
};

void display_trucks_for_given_make(
    std::string const& make,
    std::string const& file_name);

void display_trucks_for_given_model(
    std::string const& model,
    std::string const& file_name);

void display_trucks_for_given_price(
    double & price,
    std::string const& file_name);

void display_trucks_for_given_miles(
    int const& miles,
    std::string const& file_name);

// end file: Trucks.h

The operator for the Sedans file is similar.

A typical search function

Here is an example of one of the search functions. This one searches the Trucks file for a given make, and displays data for any truck that matches. Note that the search functions are all free functions, declared outside of any class.

Two functions from the utility toolbox, tbx::to_lower and tbx::to_lower_in_place, convert their string arguments to lower case. Before comparing a vehicle make against the searchText, both are converted to lower case.

The file is opened anew every time a search function is called. Upon exit from a search function, the file is closed automatically (by its destructor).


void display_trucks_for_given_make(
    std::string make,
    std::string const& file_name)
{
    tbx::to_lower_in_place(make);
    std::ifstream ist{ file_name };
    if (!ist.is_open())
        throw std::runtime_error(
            "Could not open file: \"" + file_name + '"');
    Trucks truck;
    while (ist >> truck)
        if (tbx::to_lower(truck.getMake()) == make)
            truck.displayInfo();
}

The other search functions are similar.