【问题标题】:How to search for certain strings line by line for keywords如何逐行搜索某些字符串的关键字
【发布时间】:2020-04-19 15:23:26
【问题描述】:

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

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

}

【问题讨论】:

  • fin 和递归方式打开文件do {fin &gt;&gt; type; getline(fin, name);} while(fin);。这里typename 属于std::string 类型。注意:这仅在保证您的类型始终为单个单词时才有效,即类型不包含空格。
  • @DivyanshSingh 类型是一个单词,我将如何实现您在我的主要功能中所说的内容。我输入了你给我的代码行,然后我输入了 if 语句或者它将如何工作?

标签: c++ text iostream


【解决方案1】:

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

#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”。

所有派生都是公开的,因此我们可以继承受保护和公开的成员和函数。

基类包含变量“name”。这是由所有派生类继承的。所以,这里不需要再定义了。

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

基类构造函数将设置游戏的“名称”。并且派生类的构造函数会调用基类构造函数并用它设置“名称”。

您可以看到print 函数已被设计为基类“Game”中的虚函数。因此,我们可以在所有派生类中重写它。

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

好的,这就是“游戏”类层次结构。

接下来是“游戏列表”。

这包含一个指向“游戏”的指针向量。请注意。如今,我们不再对拥有的内存使用原始指针,而是使用像std::unique_pointer 这样的智能指针。这将防止内存泄漏和其他灾难。我们也不会再使用new,而是std::make_unique

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

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

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

我们想先用is &gt;&gt; type 读取“类型”。然后我们要读取该行的其余部分,直到我们点击'\n'。这可以通过std::getline(is, name) 完成。

您可能听说过提取或插入操作可以链接起来,例如std::cin &gt;&gt; a &gt;&gt; b &gt;&gt; c。这是可行的,因为这些操作返回对原始流的引用。所以,std::cin &gt;&gt; a 返回std::cin 然后这将用于“>> b”等等。

知道如何(is &gt;&gt; type 将返回 is),我们可以使用它并将其放在 std::getline 函数的第一个参数中,该函数需要一个“is”作为第一个参数。所以,我们可以写

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

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

好的,明白了。但为什么会出现在for 循环的条件部分。为此,您需要知道std::getline 还返回对给定流的引用,所以再次“是”。并且流有一个被覆盖的bool operator,它将返回流的状态,例如“文件结束”。出于这个原因,结果std::getline 将被转换为布尔值并可以用作条件。

for循环中,当我们读取“类型”后,我们使用一种工厂模式并创建一个新的适合派生类,并将指向基类的指针存储在我们内部的std::vector中。

在“GameList”类的插入器部分,我们通过基于范围的 for 循环检索所有指向基类的指针,并调用它们的虚拟“打印”函数。而且,通过多态的奇迹,正确的函数会被调用。

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

Main 就相当简单了。我们定义了一个“GameList”类,然后使用提取操作符,读取所有数据并创建所有类。

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

一些沉重的东西。但请尽量消化原理,然后自己道歉。

很遗憾没有人会读到这个。 . .

【讨论】:

  • 嘿!非常感谢您冗长而详细的解释!我从中学到了一些新东西。我的问题是,我已经完成了所有的课程和一切。我已经到了可以打印文件中名称的地步。我的问题是如何使用我拥有的文本创建新对象。我想逐行搜索,当它看到像MMO这样的词时,用游戏名称创建一个新的mmo对象。
  • 查看 for 循环。它发生在那里。 for 循环逐行读取,然后根据类型创建所需的类。您应该以类似的方式执行此操作。
猜你喜欢
  • 1970-01-01
  • 2020-05-01
  • 2014-12-08
  • 1970-01-01
  • 2021-06-22
  • 2014-06-11
  • 2019-03-13
  • 1970-01-01
  • 2011-10-04
相关资源
最近更新 更多