【问题标题】:How do I create a HashCode in .net (c#) for a string that is safe to store in a database?如何在 .net (c#) 中为可安全存储在数据库中的字符串创建 HashCode?
【发布时间】:2011-07-06 12:19:15
【问题描述】:

引用 Eric Lippert 的 Guidelines and rules for GetHashCode 的话:

规则:GetHashCode 的消费者不能依赖它随着时间的推移或跨应用程序域而保持稳定

假设您有一个 Customer 对象 有一堆字段,例如名称, 地址等。如果你做两个 这样的对象完全一样 数据在两个不同的过程中,他们 不必返回相同的哈希 代码。如果你在 周二在一个进程中,关闭它, 并再次运行程序 星期三,哈希码可以是 不同。

这在过去曾咬人。 的文档 System.String.GetHashCode 注释 特别是两个相同的 字符串可以有不同的哈希码 在不同版本的 CLR 中,以及 事实上他们确实如此。 不要在数据库中存储字符串哈希并期望它们永远相同,因为它们不会。

那么创建可以存储在数据库中的字符串的 HashCode 的正确方法是什么?

(请告诉我,我不是第一个在我编写的软件中留下这个错误的人!)

【问题讨论】:

  • 好吧,我从不依赖GetHashCode,因为我知道,我实现这个方法是多么的草率。我相信其他人并没有做得更好...... ;-)
  • 您不是第一个在您编写的软件中留下此错误的人。
  • Dbase 引擎已经非常擅长散列字符串。只需为该列创建一个索引。
  • @Hans,见stackoverflow.com/questions/2730865/…,不要假设字符串存储在单个表中。
  • 为什么这很重要?无论如何,您都会索引连接中使用的列以使查询快速。在我看来,您正在尝试完成 dbase 引擎的工作。

标签: c# .net database hashcode gethashcode


【解决方案1】:

这取决于您希望该哈希具有哪些属性。例如,您可以只写如下内容:

public int HashString(string text)
{
    // TODO: Determine nullity policy.

    unchecked
    {
        int hash = 23;
        foreach (char c in text)
        {
            hash = hash * 31 + c;
        }
        return hash;
    }
}

只要您记录这是计算哈希的方式,那是有效的。它绝不是加密安全或类似的东西,但你可以毫无问题地坚持下去。在序数意义上绝对相等的两个字符串(即没有应用文化平等等,完全逐个字符相同)将使用此代码产生相同的哈希。

当您依赖 undocumented 散列时,问题就来了 - 即遵循 GetHashCode() 但不能保证在不同版本之间保持相同的东西......比如string.GetHashCode()

像这样编写和记录您自己的散列有点像说“这些敏感信息使用 MD5(或其他)散列”。只要是定义明确的哈希,就可以了。

编辑:其他答案建议使用加密哈希,例如 SHA-1 或 MD5。我要说的是,在我们知道需要加密安全性而不仅仅是稳定性之前,将字符串转换为字节数组并对其进行散列处理是没有意义的。当然,如果散列 旨在用于任何与安全相关的事情,那么行业标准的散列 正是 您应该达到的目标。但这在问题的任何地方都没有提到。

【讨论】:

  • 23 和* 31 有什么神奇之处吗?相反,有什么理由选择这些而不是任何其他值? ...超过任何其他[记录]散列方法?我猜不是,尽管 31 比 ASCII 可打印文件少 1 让我产生了不必要的怀疑。
  • @ruffin:它们是 Josh Bloch 推荐的价值观。乘以 31 是有效的,因为它可以作为移位和减法来完成。还有很多其他问题在讨论这个问题——老实说,这有点像黑暗艺术。
  • 整洁!来自Effective Java (2008), page 48选择了值 31,因为它是一个奇数素数。如果它是偶数并且乘法溢出,则信息将丢失,因为乘法相当于移位。使用素数的优势不太明显,但它是传统的。 31 的一个很好的属性是乘法可以用移位和减法代替以获得更好的性能:31 * i == (i << 5) - i。现代 VM 会自动进行这种优化。 看起来很有趣;再次感谢。
  • “现代虚拟机自动进行这种优化”这是否也适用于 .NET?
【解决方案2】:

这是the current way .NET calculates it's string hash code for 64 bit systems 的重新实现。这不像真正的GetHashCode()那样使用指针,所以它会稍微慢一些,但它确实使它对string的内部更改更具弹性,这将提供比Jon Skeet's version更均匀分布的哈希码,这可能会导致在字典中更好的查找时间。

public static class StringExtensionMethods
{
    public static int GetStableHashCode(this string str)
    {
        unchecked
        {
            int hash1 = 5381;
            int hash2 = hash1;

            for(int i = 0; i < str.Length && str[i] != '\0'; i += 2)
            {
                hash1 = ((hash1 << 5) + hash1) ^ str[i];
                if (i == str.Length - 1 || str[i+1] == '\0')
                    break;
                hash2 = ((hash2 << 5) + hash2) ^ str[i+1];
            }

            return hash1 + (hash2*1566083941);
        }
    }
}

【讨论】:

    【解决方案3】:

    答案是编写您自己的散列函数。您可以通过 cmets 中指向您发布的文章的链接找到一些来源。或者,您可以使用最初用于加密(MD5、SHA1 等)的内置哈希函数,而不是使用所有位。

    【讨论】:

    • 很好的答案!我喜欢“自己做,我没有头绪”的精神
    猜你喜欢
    • 2016-08-21
    • 1970-01-01
    • 2023-03-23
    • 1970-01-01
    • 2022-11-11
    • 1970-01-01
    • 2016-04-19
    • 1970-01-01
    • 2012-02-17
    相关资源
    最近更新 更多