首页 > 解决方案 > 以特定格式快速解析原始数据

问题描述

我正在寻找快速、非常快地阅读文本文件的方法。我想了很多解决方案,但无法找到最佳解决方案。让我描述一下我的问题,然后我将写下我已经尝试过的东西。

问题陈述:

假设我有一个 10G 的文本文件,该文件的格式是:

___PART_1___
*1 abc
*2 def
...
<5 million lines of this format>
*5000001 blah

___PART_2___
1 *1:1 *2:2 <value1>
2 *3:1 *4:3 <value2>
3 *4:2 *4:4 <value3>
<another 10 million lines of this format>

_PART_1_中,有两列,ID 和 NAME。在_PART_2_中,有 4 列,序列号、data1、data2、some_value

我们想要从这个巨大的文件中获取 data1 和 data2 列中冒号之前的数据。在这种情况下,我们希望

_PART_2_的第一行,提取 *1 和 *2,从_PART_1_中获取对应的名称,在这种情况下为 abc & def。从_PART_2_的第 2 行,提取 *3 和 *4,从_PART_1_中获取相应的名称,无论它们是什么。

这就是我们想要的所有信息。

在我们得出结论之前需要考虑的事项:

_PART_1_中,ID 可能不是唯一的或连续的,并且可能有任意数量的行,500 万只是一个数字。

_PART_2_中,可以肯定的是,_PART_1_ 中会有一个条目,用于PART_2_的 data1 和 data2 列的冒号前的数据

到目前为止尝试过:第 1 号:我尝试将_PART_1_保留在地图中,但由于条目数量很大,因此平衡本身需要很多时间。所以,我在 unordered_map 上确认了自己。也会为它写一个好的散列函数。然后每当我到达_PART_2_时,标记该行,获取第二个/第三个标记,再次标记它们并获取数据。最后,在 unordered_map 中寻找它们。使用 boost::tokenizer 到 tokenizer。

第 2 号:不是 boost::tokenizer,而是与 regex_searches 一起使用,但它们似乎也很慢。

数字 2:使用 mmap 将文件映射到内存,但由于文件很大,我的程序有时会耗尽内存。

代码快照,不是完整代码:

typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
typedef std::unordered_map<std::string, std::string> m_unordered;
typedef std::unordered_map<std::string, std::string>::iterator m_unordered_itr;

int main() {
  m_unordered un_name_map;
  m_unordered_itr un_name_map_itr;
  boost::char_separator<char> space_sep{" "};
  std::ifstream myfile("file.txt");
  if (myfile.is_open()) {
    std::string line;
    bool part1_starts = 0;
    bool part2_starts = 0;
    while ( std::getline (myfile,line) ) {
      if (line.find("___PART_1___") != std::string::npos) {
        part1_starts = 1;
        continue;
      }
      if (mapping_starts) {
        tokenizer tok{line, space_sep};
        tokenizer::iterator it = tok.begin();
        std::string index = *it++;
        std::string value = *it;
        un_name_map.insert(un_name_map.end(), {index, value});
      }
      if (line.find("___PART_2___") != std::string::npos) {
        part2_starts = 1;
        part1_starts = 0;
        continue;
      }
      if (part2_starts) {
        tokenizer tok{line, space_sep};
        tokenizer::iterator it_start = tok.begin();
        // Ignore first token and advance
        std::advance(it_start, 1);

        // Split the second token which is my second column of ___PART_2___             vector<std::string> strs;
        strs.reserve(2);
        boost::split(strs, *it_start, boost::is_any_of(":"));
        un_name_map_itr = un_name_map.find(strs[0]);
        if (un_name_map_itr != un_name_map.end()) {
         std::cout << "1. Name from the map is " << un_name_map_itr->second << std::endl;
        }

        // Split the third token which is my third column of ___PART_2___
        // Similar code as above.
      }
    }
  }
}

我确信有更好的方法可以达到上述解决方案。我期待着他们所有人。我唯一关心的是“速度”。如果需要,我很乐意写更多关于它的细节。

标签: c++dictionaryboostunordered-map

解决方案


推荐阅读