如何逐行搜索某些字符串的关键字

How to search for certain strings line by line for keywords

我在文本文件中有一个游戏列表,其类别如下

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(); 

}

我先给你看一些示例代码,然后再给你解释。请注意:这是许多可能的解决方案之一。而且我添加了很多调试输出,这样你就可以看到,幕后发生了什么。

#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;
}

解释:

首先我们构建一个 class 层次结构。

我们从 class "Game" 开始,然后从中导出 4 个 class:"MMO"、"Strategy"、"Arcade" 和 "AR".

所有派生都是public,这样我们就可以继承protected和public的成员和函数了

基数 class 包含变量 "name"。这被所有派生的 classes 继承。所以,不需要再在那里定义它。

基class有一个纯虚函数,在函数定义后由= 0指定。这意味着您根本无法创建/实例化 class "Game"。但是您可以定义函数,例如构造函数和析构函数。每个 class 中的所有析构函数只会生成一些调试输出。

Base class 构造函数将设置游戏的 "name"。派生的 classes 的构造函数将调用基础 class 构造函数并用它设置 "name"。

可以看到print函数在基class"Game"中被设计成了虚函数。因此,我们可以在所有派生的 classes.

中覆盖它

稍后,我们通过指向 Game 的指针访问 print 函数,多态性将确保调用正确的函数。

好的,那是 "Game" class 层次结构。

下一个 "GameList".

这包含指向 "Game" 的指针向量。请注意。现在我们不再为拥有的内存使用原始指针,而是像 std::unique_pointer 这样的智能指针。这将防止内存泄漏和其他灾难。我们也不会再使用 new,而是 std::make_unique.

但是,所有这一切都在您的基本问题之前:如何读取数据?为此,我们覆盖了 "GameList" class 的提取器运算符。这样,我们就可以从 std::ifstreamstd::istringstream 或任何其他流中读取任何流,并使用 iostream 功能。

那么,我们在这个函数中做了什么?

首先,我们使用for循环读取文件的所有行。在for循环的声明部分,我们声明了2个std::string变量:"type"和"name"。然后是有点棘手的部分。

我们想先阅读 "type" 和 is >> type。然后我们要读取该行的其余部分,直到我们点击'\n'。这可以通过 std::getline(is, name).

来完成

您可能听说过可以链接提取或插入操作,例如 std::cin >> a >> b >> c。这是可行的,因为这些操作 return 是对原始流的引用。所以,std::cin >> a returns std::cin 然后这将用于 ">> b" 等等。

知道如何(is >> type 将 return 是),我们可以使用它并将它放在 std::getline 函数的第一个参数中,该函数需要一个 "is" 作为第一个参数。所以,我们可以写

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

然后,首先读取类型,然后读取名称。

好的,明白了。但是为什么在 for 循环的条件部分。为此,您需要知道 std::getline 也 return 是对给定流的引用,因此再次 "is"。流有一个被覆盖的 bool operator,这将 return 流的状态,例如 "end of file"。因此,结果 std::getline 将被转换为布尔值并可用作条件。

for循环中,在我们读取了"type"之后,我们使用一种工厂模式并创建一个新的拟合派生class,并存储一个指向基的指针class 在我们内部 std::vector.

在 "GameList" class 的插入器部分,我们通过基于范围的 for 循环检索所有指向基 class 的指针,并调用它们的虚拟 "print" 函数.而且,由于多态性的神奇,将调用正确的函数。

主要是,对于这个例子,我没有打开文件,因为我在 Whosebug 上没有文件。我改用 std::istringstreasm,但您当然可以使用任何其他流。

Main 就相当简单了。我们定义一个 class "GameList" 然后使用提取器运算符,读取所有数据并创建所有 classes.

插入运算符也是如此。我们将完整的 "GameList" 插入 std::cout 并用它调用虚拟 print 函数。

一些沉重的东西。但是请尝试消化原理,然后用它做你自己的应用程序。

可惜没人会看。 . .