如何使用带有区分大小写的unicode字符串的hash_map作为密钥?

时间:2009-12-23 00:06:23

标签: c++ stl hash dictionary

我是STL的新手,对C ++来说还是个新手。我试图在C ++中获得相当于.NET Dictionary<string, value>(StringComparer.OrdinalIgnoreCase)的东西。这大致是我正在尝试的:

stdext::hash_map<LPCWSTR, SomeStruct> someMap;
someMap.insert(stdext::pair<LPCWSTR, SomeStruct>(L"a string", struct));
someMap.find(L"a string")
someMap.find(L"A STRING")

麻烦的是,查找操作通常都不起作用(返回someMap.end())。它似乎有时可行,但大部分时间它都没有。我猜测hash_map使用的散列函数是散列字符串的内存地址而不是字符串本身的内容,而且几乎可以肯定它不区分大小写。

如何获得使用不区分大小写的键并且可以存储我的自定义结构的类字典结构?

感谢。

4 个答案:

答案 0 :(得分:3)

链接到的hash_map文档表明您可以提供自己的traits类作为第三个模板参数。这必须满足与hash_compare相同的界面。

扫描文档,我认为您必须做的就是这个,这基本上取代了您在词典中使用的StringComparer.OrdinalIgnoreCase

struct my_hash_compare {
    const size_t bucket_size = 4;
    const size_t min_buckets = 8;
    size_t operator()(const LPCWSTR &Key) const {
        // implement a case-insensitive hash function here,
        // or find something in the Windows libraries.
    }
    bool operator()(const LPCWSTR &Key1, const LPCWSTR &Key2) const {
        // implement a case-insensitive comparison function here
        return _wcsicmp(Key1, Key2) < 0;
        // or something like that. There's warnings about
        // locale plastered all over this function's docs.
    }
};

我很担心,虽然文档说比较函数必须是一个完整的顺序,而不是C ++标准库中排序容器通常的严格弱顺序。如果MS确实意味着总订单,那么hash_map可能依赖于它与operator==一致。也就是说,如果my_hash_compare()(a,b)为false,my_hash_compare()(b,a)为false,则可能需要a == b。显然,我写的不是这样,在这种情况下,你运气不好。

作为替代方案,在任何情况下都可能更高效,您可以在将所有键推送到常见案例之前将其用于地图中。不区分大小写的比较比常规字符串比较更昂贵。但是有一些Unicode问题与我无法记住的事情有关。也许你必须转换 - &gt;小写 - &gt;大写,而不仅仅是 - &gt;大写,或类似的东西,以避免某些语言或标题字符的一些讨厌的情况。任何人吗?

正如其他人所说,你可能并不真正想要LPCWSTR作为你的钥匙。这将在地图中存储指针,这意味着任何插入字符串的人都必须确保它指向的数据保持有效,只要它在hash_map中即可。从长远来看,hash_map通常会更好地保存传递给insert的密钥字符串的副本,在这种情况下,您应该使用wstring作为密钥。

答案 1 :(得分:2)

这里有一些很好的信息。我从答案中收集了点点滴滴,然后把它们放在一起:

#include "stdafx.h"
#include "atlbase.h"
#include <map>
#include <wchar.h>

typedef std::pair<std::wstring, int> MyPair;

struct key_comparer
{
    bool operator()(std::wstring a, std::wstring b) const
    {
        return _wcsicmp(a.c_str(), b.c_str()) < 0;
    }
};

int _tmain(int argc, _TCHAR* argv[])
{
    std::map<std::wstring, int, key_comparer> mymap;
    mymap.insert(MyPair(L"GHI",3));
    mymap.insert(MyPair(L"DEF",2));
    mymap.insert(MyPair(L"ABC",1));

    std::map<std::wstring, int, key_comparer>::iterator iter;
    iter = mymap.find(L"def");
    if (iter == mymap.end()) {
        printf("No match.\n");
    } else {
        printf("match: %i\n", iter->second);
    }
    return 0;
}

答案 2 :(得分:1)

如果您使用std::map代替非标准hash_map,则可以设置在执行二进制搜索时使用的比较函数:

// Function object for case insensitive comparison
struct case_insensitive_compare
{
    case_insensitive_compare() {}

    // Function objects overloader operator()
    // When used as a comparer, it should function as operator<(a,b)
    bool operator()(const std::string& a, const std::string& b) const
    {
        return to_lower(a) < to_lower(b);
    }

    std::string to_lower(const std::string& a) const
    {
        std::string s(a);
        std::for_each(s.begin(), s.end(), char_to_lower);
        return s;
    }

    void char_to_lower(char& c) const
    {
        if (c >= 'A' && c <= 'Z')
            c += ('a' - 'A');
    }
};

// ...

std::map<std::string, std::string, case_insensitive_compare> someMap;
someMap["foo"] = "Hello, world!";
std::cout << someMap["FOO"] << endl; // Hello, world!

答案 3 :(得分:0)

LPCWSTR是一个指向以null结尾的unicode字符数组的指针,在这种情况下可能不是你想要的。请改用wstring的{​​{1}}专精。

对于不区分大小写的情况,在插入和搜索之前,您需要将键转换为全大写或全小写。至少我认为你不能以任何其他方式做到这一点。