【问题标题】:Generating data structures by parsing plain text files通过解析纯文本文件生成数据结构
【发布时间】:2010-10-22 10:09:14
【问题描述】:

我为我正在编写的游戏编写了一个文件解析器,以便我自己轻松更改游戏的各个方面(例如角色/场景/碰撞数据)。例如,我可能有一个这样的字符类:

class Character
{
public:
    int x, y; // Character's location
    Character* teammate;
}

我设置我的解析器从文件中读取具有类似于 C++ 语法的数据结构

Character Sidekick
{
    X = 12
    Y = 0
}

Character AwesomeDude
{
    X = 10
    Y = 50
    Teammate = Sidekick
}

这将创建两个数据结构并将它们放在一个 map<std::string, Character*> 中,其中的键字符串是我给它的任何名称(在本例中为 Sidekick 和 AwesomeDude)。当我的解析器看到一个指向类的指针时,比如队友指针,它足够聪明,可以在地图中查找以获取指向该数据结构的指针。问题是我不能将 Sidekick 的队友声明为 AwesomeDude,因为它还没有被放置到 Character map 中。

我正在尝试找到解决此问题的最佳方法,以便我的数据结构可以引用尚未添加到地图中的对象。我能想到的两个最简单的解决方案是(a)添加转发声明数据结构的能力或(b)让解析器读取文件两次,一次是用指向空数据结构的指针填充映射,第二次是通过并填写它们。

(a) 的问题是我还可以决定在类上调用哪个构造函数,如果我转发声明某些内容,我必须让构造函数与其余数据分开,这可能会造成混淆. (b) 的问题是我可能想在他们自己的文件中声明 Sidekick 和 AwesomeDude。我必须让我的解析器能够读取一个文件列表,而不是一次读取一个(我猜这还不错,尽管有时我可能想从一个文件中读取一个文件列表文件)。 (b) 还有一个缺点,就是不能使用稍后在构造函数本身中声明的数据结构,但我认为这没什么大不了的。

哪种方式听起来更好?有没有我没有想到的第三种选择?似乎应该通过指针引用或绑定或其他东西来解决这个问题...... :-/ 我想这有点主观,基于我想给自己的功能,但欢迎任何输入。

【问题讨论】:

  • 你可能想阅读依赖注入;虽然具体情况会与您处理的问题有所不同,但问题应该足够相似,以便您获得一些见解。

标签: c++ parsing fileparsing


【解决方案1】:

当您第一次遇到引用时,只需将其存储为引用即可。然后,您可以将字符、引用或其他任何内容放在“需要稍后解析的引用”列表中。

文件完成后,遍历那些有引用并解决它们。

【讨论】:

  • 典型的两阶段解析,类似于编译器/链接器系统。好的,标准的方式。
【解决方案2】:

Will 准确地说出了我要写的内容。只需保留未解决的引用列表或其他内容即可。

并且一旦你读完文件,如果有未解决的引用不要忘记抛出一个错误=P

【讨论】:

    【解决方案3】:

    就个人而言,我会选择 b)。将您的代码拆分为 Parser 和 Validator 类,它们都在相同的数据结构上运行。 Parser 将读取并解析一个文件,填充数据结构并将任何对象引用存储为它们的文本名称,暂时将真实指针保留在您的结构中。

    当您完成加载文件时,使用 Validator 类来验证和解析任何引用,填充“真实”指针。您将需要考虑如何构建数据以使这些查找变得又快又好。

    【讨论】:

      【解决方案4】:

      嗯,你要求第三种选择。您不必使用 XML,但如果遵循以下结构,则使用 SAX 解析器构建数据结构将非常简单。

      无论如何,每个角色都不是引用队友,而是引用一个团队(在本例中为蓝队)。这将解耦循环引用问题。只需确保在角色之前列出团队即可。

      <team>Blue</team>
      
      <character>
          <name>Sidekick</name>
          <X>12</X>
          <Y>0</Y>
          <teamref>Blue</teamref>
      </character>
      
      <character>
          <name>Sidekick</name>
          <X>10</X>
          <Y>50</Y>
          <teamref>Blue</teamref>
      </character>
      

      【讨论】:

      • 这当然适用于我给出的示例,但该示例过于简化了我真正想做的事情,并且在某些情况下解耦两个对象并不可行。或者,至少,它会使制作文件的人更难被解析。
      【解决方案5】:

      不要在地图中存储 Character 对象,而是存储 Character 的代理。然后,代理将在加载对象时包含指向实际 Character 对象的指针。 Character::teammate 的类型将更改为此代理类型。当您阅读地图中尚未包含的参考资料时,您将创建一个代理并使用该代理。当您加载地图中已有空代理的角色时,使用新加载的角色填充它。您可能还想添加一个计数器来跟踪您在地图中有多少个空代理,这样您就知道何时加载了所有引用的字符。

      另一层间接......它总是使编程更容易和更慢。

      【讨论】:

        【解决方案6】:

        一种选择是撤销该义务。 Map负责填写参考

        template<T> class SymbolMap // I never could rememeber C++ template syntax
        {
           ...
        
           /// fill in target with thing name
           /// if no name yet, add it to the list of thing that will be name
           void Set(T& target, std::string name);
        
           /// define name as target
           /// go back and fill in anything that needs to be name
           void Define(T target, std::string name);
        
           /// make sure everything is resolved
           ~SymbolMap()
        }
        

        这不会与值/移动语义很好地交互,但我怀疑不会有太多。

        【讨论】:

          猜你喜欢
          • 2020-01-25
          • 1970-01-01
          • 2015-01-18
          • 2015-07-26
          • 2020-12-31
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2013-07-28
          相关资源
          最近更新 更多