【问题标题】:Elegant way of parsing Data files for Simulation为模拟解析数据文件的优雅方式
【发布时间】:2010-03-25 15:48:31
【问题描述】:

我正在从事这个项目,我需要从 .dat 文件中读取大量数据并使用这些数据来执行模拟。我的 .dat 文件中的数据如下所示:

DeviceID  InteractingDeviceID InteractionStartTime InteractionEndTime
  1            2                  1101                1105

1,2 1101 和 1105 是制表符分隔的,表示设备 1 在 1101 毫秒与设备 2 交互,并在 1105 毫秒结束交互。

我有一个跟踪数据集,其中编译了数千个此类交互,我的工作是分析这些交互。

第一步是解析文件。选择的语言是 C++。我正在考虑采用的方法是读取文件,为读取的每一行创建一个设备对象。 此 Device 对象将包含属性 DeviceId 和结构数组/向量,其中将包含在模拟过程中与给定 DeviceId 交互的所有设备的列表。该结构将包含交互设备 ID、交互开始时间和互动结束时间。

我在这里有两个问题:

  1. 我的方法正确吗?

  2. 如果我走在正确的轨道上,如何使用 C++ 快速解析这些制表符分隔的数据文件并创建设备对象而不会产生过多的内存开销?

我们将不胜感激。

谢谢

【问题讨论】:

    标签: c++ parsing data-structures simulation


    【解决方案1】:

    鉴于您提供的信息,您的方法似乎是正确的。

    我假设你会创建一个类似的类:

    class device {
      public:
        int id;
        vector<interaction> interactions;
        void add_interaction(interaction add_me); // uses vector::insert
    };
    

    typedef struct interaction_t {
        int other_device_id;
        int start_time;
        int end_time;
    } interaction;
    

    此时,您应该能够一次读取一行文件并提取数据。

    device* pDev = NULL;
    interaction new_interaction;
    ifstream ifs( "data.dat" );
    char temp[MAX_LINE_LENGTH+1];
    int id, other_id, start, end;
    
    while(ifs.getline(temp, MAX_LINE_LENGTH)) {
        sscanf(temp, "%i\t%i\t%i\t%i",
            &id,
            &new_interaction.other_device_id,
            &new_interaction.start_time,
            &new_interaction.end_time);
        pDev = find_device_by_id(id);
        pDev->add_interaction(new_interaction);
    }
    

    代码未经测试,仅用于说明目的,但您可以理解。诀窍是编写find_device_by_id 函数(将返回指向device 对象的指针,并带有匹配的id 字段)。这不应该要求每个输入行的内存开销太大;如果您的输入文件很大,您可能无法将数据存储在内存中,而可能不得不存储在数据库中。

    【讨论】:

    • 这样做你最终会得到每个交互的两个(或更多用于 n 面交互)副本。如果您维护一个单独的交互列表并使用vector&lt;interaction *&gt;(或使用更智能的指针),您将获得相同的行为而无需重复数据。否则,很好。
    • 如果设备索引可能出现在“DeviceID”列或“InteractingDeviceID”列中,那么您是对的。删除interaction.other_device_id,具有interaction 的全局向量,并让每个device 仅存储指向interactions 的指针会更好。当我写这篇文章时,我假设设备列表和交互设备列表是不同的(好像这些数据正在跟踪“发起者”设备和他们发送命令的“目标”设备列表)。
    • 非常感谢..这就解释了。
    【解决方案2】:

    解决设计内存存储和链接的问题:

    你告诉我们的还不够多。数据的必要结构取决于您需要如何使用数据。

    • 如果您要按 starttime 顺序遍历(全部或部分)数据,难道您不能按 starttime 顺序访问事件吗?如果您打算在某个时间跳到流的中间,您不应该能够在开始时间之前有效地搜索。
    • 如果要检查某个时间间隔内活动的事件,还需要能够按结束时间高效搜索。
    • 如果您想检查单个设备的所有交互,您需要能够按设备选择事件(您提出的结构很好)
    • ...您还有哪些其他用例...

    如果您不需要可能的最佳性能(即良好的性能就可以),则可能需要使用关系数据库。或者您可以构建具有您需要的所有特征的内存结构,但它们可能会比较复杂......

    【讨论】:

    • 谢谢。对我来说幸运的是,跟踪数据已按时间顺序排列。一旦我开始解析数据,模拟就会触发。一读到第一行,我就检查 HashMap 是否存在 DeviceId 和交互的 DeviceId。如果不存在,我创建两个新的 DeviceId 对象,填充它们的属性并将它们存储在 HashMap 中。模拟运行一段时间后,我会根据交互频率和设备与另一个设备花费的时间跨度来分析设备交互的任何趋势。在我的模拟过程中,我是“设备不可知论者”。
    【解决方案3】:

    我对互动的人做了类似的事情。为了将来的可扩展性,我将执行以下操作: 拥有一个包含 id 和指针向量的 Device 类交互对象。设备可以保存在地图(或哈希图)中以便于查找。交互类将包含文件中的其余信息。这将允许您创建多态设备和交互,以防您每个人都有多种设备或交互。您可能还希望为设备和交互建立工厂,以促进这一点。

    【讨论】:

    • 对。 无论您使用什么存储安排,您都应该将每条数据保存在一个位置,并使用大量引用(或指针)来管理您的组织需求。
    • 谢谢基思。你能解释一下什么是指针交互对象的向量吗?将设备保存在以 DeviceId 作为键的 HashMap 中是一个好主意。在模拟过程中,我必须查找 deviceIds 及其交互历史来推断有用的趋势。另外,您是否建议我为交互部分创建一个单独的类?我假设这个 Interaction 对象将由 InteractingDeviceId、InteractionStartTime 和 InteractionEndTime 组成。
    【解决方案4】:

    看看Boost.Spirit。这是一个不错的解析器框架。

    编辑,固定链接

    【讨论】:

    • 虽然我真的很喜欢 Spirit,但除非您已经熟悉它,否则它可能为此目的而矫枉过正。
    • 谢谢格伦。很高兴了解 Boost.Spirit。由于我在某种程度上是 C++ 世界的新手,我想知道这是否可以在不依赖诸如 Spirit 之类的框架的情况下完成。此外,我可能需要示例代码来概述读取文件、解析文件并将解析的数据移动到其他数据结构中所需的必要管道,并且开销最小,因为计算时间会缩短模拟时间。跨度>
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-02-18
    • 1970-01-01
    • 2010-09-18
    • 2015-10-29
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多