Как искать определенные строки построчно по ключевым словам - PullRequest
0 голосов
/ 19 апреля 2020

У меня есть список игр с их категориями в текстовом файле следующим образом

MMO League Of Legends 
MMO World Of Warcraft 
Strategy Civilization 
Strategy Hearthstone 
Arcade Street Fighter 
Arcade PacMan 
AR Beat Saber 
AR Superhot 

Мне нужно искать каждую из категорий и всякий раз, когда она находит определенную категорию, чтобы сделать объект этой категории Например,

if (type == "MMO") game = new MMO(name, description);

Как мне реализовать этот поиск (строка за строкой), чтобы он использовал все после категории в качестве имени. Другими словами, я хочу, чтобы оно сохраняло все имя объекта, а не только первое слово.

Спасибо за помощь!

Это моя основная функция

int main()
{

ifstream inFile;
//Open the file
inFile.open("Games.txt");
//Check that file was opened successfully
if (!inFile) {
    cout << "Unable to open file";
    exit(1); 
}



//Vector to store items
vector <Game*> games;

//All the items will be sorted using the Standard Library Sort() function.
void sort();


inFile.close(); 

}

1 Ответ

1 голос
/ 19 апреля 2020

Сначала я покажу вам пример кода, а затем объясню. Обратите внимание: это одно из многих возможных решений. И я добавил много отладочной информации, чтобы вы могли видеть, что происходит за кулисами.

#include <iostream>
#include <fstream>
#include <vector>
#include <string>
#include <sstream>
#include <algorithm>
#include <iterator>

// A class hierachy
class Game {
protected:
    // The name. This will be inherited from all other ganes. No need to define it there
    std::string name{};
public:
    // Constructor with some debug output
    Game(const std::string& n) : name(n) { std::cout << "DEBUG: Call constructor for Game with name '" << name << "'\n"; }

    // Virtual Destructor with some debug output
    virtual ~Game() { std::cout << "DEBUG: Call destructor for Game\n";  }

    // This is a virtual oure function. It will prevent a direct instantiation of Game
    virtual void print(std::ostream&) const = 0;
};

class MMO : public Game {
public:
    // Constructor with some debug output. Call base constructor from Game and set name
    MMO(const std::string& n) : Game(n) { std::cout << "DEBUG: Call constructor for MMO with name '" << name << "'\n"; }

    // Virtual Destructor with some debug output
    virtual ~MMO() override { std::cout << "DEBUG: Call destructor for MMO\n"; }

    // Virtual function overrides base function and will be used polymorph
    virtual void print(std::ostream& os) const override { os << name << " --> is of type MMO\n"; }
};

class Strategy : public Game {
public:
    // Constructor with some debug output. Call base constructor from Game and set name
    Strategy(const std::string& n) : Game(n) { std::cout << "DEBUG: Call constructor for Strategy with name '" << name << "'\n"; }

    // Virtual Destructor with some debug output
    virtual ~Strategy() override { std::cout << "DEBUG: Call destructor for Strategy\n"; }

    // Virtual function overrides base function and will be used polymorph
    virtual void print(std::ostream& os) const override { os << name << " --> is of type Strategy\n"; }
};

class Arcade : public Game {
public:
    // Constructor with some debug output. Call base constructor from Game and set name
    Arcade(const std::string& n) : Game(n) { std::cout << "DEBUG: Call constructor for Arcade with name '" << name << "'\n"; }

    // Virtual Destructor with some debug output
    virtual ~Arcade() override { std::cout << "DEBUG: Call destructor for Arcade\n"; }

    // Virtual function overrides base function and will be used polymorph
    virtual void print(std::ostream& os) const override { os << name << " --> is of type Arcade \n"; }
};

class AR : public Game {
public:
    // Constructor with some debug output. Call base constructor from Game and set name
    AR(const std::string& n) : Game(n) { std::cout << "DEBUG: Call constructor for AR with name '" << name << "'\n"; }

    // Virtual Destructor with some debug output
    virtual ~AR() override { std::cout << "DEBUG: Call destructor for AR\n"; }

    // Virtual function overrides base function and will be used polymorph
    virtual void print(std::ostream& os) const override { os << name << " --> is of type AR \n"; }
};

class GameList {
protected:
    std::vector<std::unique_ptr<Game>> data{};
public:

    // Override extractor operator
    friend std::istream& operator >> (std::istream& is, GameList& gl) {

        // Read all lines from the stream
        for (std::string type{}, name{}; std::getline((is >> type), name); ) {

            // Factory pattern. Create a class depending on the type
            if (type == "MMO") gl.data.emplace_back(std::make_unique<MMO>(name));
            else if (type == "Strategy") gl.data.emplace_back(std::make_unique<Strategy>(name));
            else if (type == "Arcade") gl.data.emplace_back(std::make_unique<Arcade >(name));
            else if (type == "AR") gl.data.emplace_back(std::make_unique<AR>(name));
            else std::cerr << "\n*** Error: invalid type read\n";
        }
        return is;
    }
    // Overide inserter operator
    friend std::ostream& operator << (std::ostream& os, const GameList& gl) {
        for (const std::unique_ptr<Game>& g : gl.data) {

            // This is a call of a polymorph function. G is a came and will call the correct function
            g->print(os);
        }
        return os;
    }
};

std::istringstream fileStream{ R"(MMO League Of Legends 
MMO World Of Warcraft 
Strategy Civilization 
Strategy Hearthstone 
Arcade Street Fighter 
Arcade PacMan 
AR Beat Saber 
AR Superhot )" };

int main() {
    // Define the Game list
    GameList gl;

    // Read games from file and add to game list
    fileStream >> gl;

    // Print out all games
    std::cout << gl;

    return 0;
}

Объяснение:

Сначала мы построим иерархию классов.

Мы начинаем с класса «Game», а затем выводим из него 4 класса: «MMO», «Strategy», «Arcade» и «AR».

Все деривации публикуются c, так что мы можем наследовать защищенные и публичные c члены и функции.

Базовый класс содержит переменную "name". И это наследуется всеми производными классами. Поэтому нет необходимости определять его там снова.

Базовый класс имеет чисто виртуальную функцию, обозначаемую = 0 после определения функции. Это означает, что вы не можете создать / создать экземпляр класса "Game" вообще. Но вы можете определить функции, такие как конструктор и деструктор. Все деструкторы в каждом классе просто сгенерируют какой-либо отладочный вывод.

Конструктор базового класса установит «имя» игры. И конструкторы производных классов будут вызывать конструктор базового класса и устанавливать с ним «имя».

Вы можете видеть, что функция print была задумана как виртуальная функция в базовом классе «Игра». ». Таким образом, мы можем переопределить его во всех производных классах.

Позже мы получим доступ к функции print через указатель на Game, и полиморфизм убедится, что будет вызвана правильная функция.

ОК, это была иерархия класса "Game".

Далее "GameList".

Содержит вектор указателей на "Game". Пожалуйста, обратите внимание. В настоящее время мы больше не используем сырые указатели для собственной памяти, а умные указатели, такие как std::unique_pointer. Это предотвратит утечки памяти и другие катастрофы. Мы также больше не будем использовать new, но std::make_unique.

Но, все это перед вашим базовым c вопросом: Как читать данные? Для этого мы переопределяем оператор экстрактора для класса «GameList». При этом мы можем читать из любого потока, например std::ifstream или std::istringstream или любого другого потока, и использовать функциональность iostream.

Итак, что мы делаем в этой функции?

Сначала мы используем для l oop, чтобы прочитать все строки файла. В части объявления для l oop мы объявляем 2 std::string переменные: "тип" и "имя". Затем следует немного хитрая часть.

Сначала мы хотим прочитать «type» с помощью is >> type. Затем мы хотим прочитать остальную часть строки, пока не нажмем '\ n'. Это можно сделать с помощью std::getline(is, name).

Возможно, вы слышали, что операции извлечения или вставки могут быть связаны, например, std::cin >> a >> b >> c. Это работает, потому что эти операции возвращают ссылку на исходный поток. Итак, std::cin >> a возвращает std::cin, а затем это будет использоваться для ">> b" и т. Д.

С этим ноу-хау (is >> type вернется), мы можем использовать это и поместите его в первый параметр функции std::getline, которая ожидает "is" в качестве первого параметра. Итак, мы можем написать

std::getline((is >> type), name)

Затем сначала будет прочитан тип, а затем имя.

Хорошо, понятно. Но почему это в условной части for l oop. Для этого вам нужно знать, что std::getline также возвращает ссылку на данный поток, поэтому снова «есть». И поток имеет перезаписанный bool operator, и он будет возвращать состояние потока, например, «конец файла». И по этой причине результат std::getline будет преобразован в логическое значение и может использоваться как условие.

В for l oop после того, как мы прочитали «тип», мы используем вид фабричного шаблона и создайте новый подходящий производный класс и сохраните указатель на базовый класс в нашем внутреннем std::vector.

В части вставки класса "GameList" мы извлекаем все указатели на базу Класс через диапазон, основанный на l oop и вызвать их виртуальную функцию "печати". И, к чуду полиморфизма, будет вызвана правильная функция.

В основном для этого примера я не открываю файл, потому что у меня нет файлов в StackOverflow. Вместо этого я использую std::istringstreasm, но вы, конечно, можете использовать любой другой поток.

Main тогда довольно прост. Мы определяем класс "GameList" и затем используем оператор экстрактора, чтобы прочитать все данные и создать все классы.

То же самое с оператором насекомого. Мы вставляем полный «GameList» в std::cout и с этим вызываем виртуальные print функции.

Некоторые тяжелые вещи. Но, пожалуйста, попытайтесь усвоить этот принцип, а затем сделайте свое собственное извинение.

Как жаль, что никто не прочитает это. , .

...