如何在stxxl :: map中使用std :: string作为键

时间:2014-04-17 13:21:47

标签: c++ string map stxxl

我正在尝试使用std :: string作为stxxl :: map中的键 对于少量的大约10-100个字符串,插入很好。 但是在尝试在其中插入大约100000个大量字符串时,我遇到了分段错误。

代码如下:

struct CompareGreaterString {
    bool operator () (const std::string& a, const std::string& b) const {
       return a > b;
    }
    static std::string max_value() {
       return "";
    } 
};

// template parameter <KeyType, DataType, CompareType, RawNodeSize, RawLeafSize, PDAllocStrategy (optional)>
typedef stxxl::map<std::string, unsigned int, CompareGreaterString, DATA_NODE_BLOCK_SIZE, DATA_LEAF_BLOCK_SIZE> name_map;
name_map strMap((name_map::node_block_type::raw_size)*3, (name_map::leaf_block_type::raw_size)*3);
for (unsigned int i = 0; i < 1000000; i++) { /// Inserting 1 million strings
    std::stringstream strStream;
    strStream << (i);
    Console::println("Inserting: " + strStream.str());
    strMap[strStream.str()]=i;
}

在这里,我无法确定为什么我无法插入更多数量的字符串。插入&#34; 1377&#34;时,我正在准确地获得分段错误。另外,我可以添加任意数量的整数作为关键。我觉得字符串的可变大小可能会导致这个麻烦。

此外,我无法理解为max_value字符串返回的内容。我只是回了一个空白字符串。

3 个答案:

答案 0 :(得分:3)

根据documentation

  

CompareType还必须提供静态max_value方法,该方法返回KeyType类型的值,该值大于map中存储的任何键

因为空字符串恰好比任何其他字符串都要小,所以它会破坏这个前提条件,从而可能导致未指定的行为。

这里应该有一个max_valueMAX_KEY_LEN只是一个整数,它大于或等于地图可以拥有的最长字符串键的长度。

struct CompareGreaterString {
    // ...
    static std::string max_value() {
        return std::string(MAX_KEY_LEN, std::numeric_limits<unsigned char>::max());
    }
};

答案 1 :(得分:1)

我终于在Timo bingmann,user2079303和Martin Ba的帮助下找到了我的问题的解决方案。谢谢。

我想与你分享。

首先,stxxl仅支持POD。这意味着它只存储固定大小的结构。因此std :: string不能成为关键。 stxxl :: map适用于大约100-1000个字符串,因为它们包含在物理内存中。当插入更多的字符串时,它必须在磁盘上写入,这在内部会导致一些问题。

因此我们需要使用char []的固定字符串,如下所示:

static const int MAX_KEY_LEN = 16;

class FixedString { 
public:
    char charStr[MAX_KEY_LEN];

    bool operator< (const FixedString& fixedString) const {
        return std::lexicographical_compare(charStr, charStr+MAX_KEY_LEN,
            fixedString.charStr, fixedString.charStr+MAX_KEY_LEN);
    }

    bool operator==(const FixedString& fixedString) const {
        return std::equal(charStr, charStr+MAX_KEY_LEN, fixedString.charStr);
    }

    bool operator!=(const FixedString& fixedString) const {
        return !std::equal(charStr, charStr+MAX_KEY_LEN, fixedString.charStr);
    } 
};

struct comp_type : public std::less<FixedString> {
    static FixedString max_value()
    {
        FixedString s;
        std::fill(s.charStr, s.charStr+MAX_KEY_LEN, 0x7f);
        return s;
    } 
};

请注意,所有运算符主要是((),==,!=)需要覆盖所有stxxl :: map函数才能工作 现在我们可以为map定义fixed_name_map,如下所示:

typedef stxxl::map<FixedString, unsigned int, comp_type, DATA_NODE_BLOCK_SIZE, DATA_LEAF_BLOCK_SIZE> fixed_name_map;
fixed_name_map myFixedMap((fixed_name_map::node_block_type::raw_size)*5, (fixed_name_map::leaf_block_type::raw_size)*5);

现在程序编译正常,并且正在接受大约10 ^ 8个字符串而没有任何问题。 我们也可以像std :: map本身一样使用myFixedMap。 {例如:myFixedMap [fixedString] = 10}

答案 2 :(得分:1)

如果您使用的是C ++ 11,那么作为FixedString类的替代方法,您可以使用std::array<char, MAX_KEY_LEN>。它是一个普通的固定大小C数组之上的STL层,实现了比较和迭代器,就像你习惯使用std :: string一样,但它是一个POD类型,所以STXXL应该支持它。

或者,您可以在TPIE中使用serialization_sort。它可以很好地对std::pair<std::string, unsigned int>类型的元素进行排序,所以如果您只需要批量插入所有内容然后批量访问它,这对您的情况就足够了(根据具体情况可能更快)。