基于字符串生成唯一哈希码

24

我有以下两个字符串:

var string1 = "MHH2016-05-20MASTECH HOLDINGS, INC. Financialshttp://finance.yahoo.com/q/is?s=mhhEDGAR Online FinancialsHeadlines";

var string2 = "CVEO2016-06-22Civeo upgraded by Scotia Howard Weilhttp://finance.yahoo.com/q/ud?s=CVEOBriefing.comHeadlines";

乍一看,这两个字符串不同,但使用GetHashCode方法它们的哈希码相同。

var hash = 0;
var total = 0;
foreach (var x in string1) //string2
{
    //hash = x * 7;
    hash = x.GetHashCode();
    Console.WriteLine("Char: " +  x + " hash: " + hash + " hashed: " + (int) x);
    total += hash;
}

两个字符串的总和最终为620438779。是否有另一种方法可以返回更独特的哈希码?我需要基于字符串中的字符使哈希码唯一。尽管这两个字符串是不同的且代码正常工作,但它们的总和相同。如何改进此代码以使它们更加唯一?


6
你应该知道,无法保证所有可能的字符串都具有唯一的哈希码。一个哈希码是32位的,意味着有40多亿个可能的值。你的两个字符串都超过了120个字符。使用96个可打印ASCII字符的所有可能的120个字符的字符串数量要大得多。碰撞是不可避免的。在普通情况下,不存在唯一的哈希码。增加哈希码的大小将减少碰撞的机会,但不能完全消除它们。 - Jim Mischel
2
您的问题暗示着您正在尝试使用哈希码作为唯一标识符。这是一个极其糟糕的想法,注定会失败。@AlexD的答案解释了原因。 - Jim Mischel
@JimMischel 是的,我现在已经意识到了,但还是谢谢。 - some random dude
可能是重复问题:在JavaScript中基于字符串输入生成唯一数字 - Codebeat
老问题了,我知道,看看我3年前的问题和答案: https://dev59.com/FG_Xa4cB1Zd3GeqP2pRc - Codebeat
2个回答

41

string.GetHashCode方法不适合用于真正的哈希:

警告

哈希码旨在用于基于哈希表的集合中的高效插入和查找。 哈希码不是永久值。 因此:

  • 不要序列化哈希码值或将其存储在数据库中。
  • 不要使用哈希码作为从键控集合检索对象的键。
  • 不要使用哈希码代替由加密哈希函数返回的值。 对于加密哈希,请使用从System.Security.Cryptography.HashAlgorithmSystem.Security.Cryptography.KeyedHashAlgorithm类派生的类。
  • 不要测试哈希码的等同性以确定两个对象是否相等。(不相等的对象可以具有相同的哈希码。)要测试相等性,请调用ReferenceEqualsEquals方法。

而且,该方法存在高度可能的重复问题

考虑使用HashAlgorithm.ComputeHash方法。如@zaph所建议的,示例稍微更改以使用SHA256而不是MD5。

static string GetSha256Hash(SHA256 shaHash, string input)
{
    // Convert the input string to a byte array and compute the hash.
    byte[] data = shaHash.ComputeHash(Encoding.UTF8.GetBytes(input));

    // Create a new Stringbuilder to collect the bytes
    // and create a string.
    StringBuilder sBuilder = new StringBuilder();

    // Loop through each byte of the hashed data 
    // and format each one as a hexadecimal string.
    for (int i = 0; i < data.Length; i++)
    {
        sBuilder.Append(data[i].ToString("x2"));
    }

    // Return the hexadecimal string.
    return sBuilder.ToString();
}

请参见 https://msdn.microsoft.com/zh-cn/library/system.security.cryptography.md5(v=vs.110).aspx,获取完整示例。 - lexx9999
@lexx9999,我认为帖子中的链接已经指向了相同的算法。 - AlexD
当我阅读它时,它没有包括GetMd5Hash/VerifyMd5Hash。 - lexx9999
@lexx9999 啊,是的,也许当时正在进行帖子编辑。 - AlexD
2
@SirajMansour 密码哈希确实是为了避免碰撞而设计的。在我的 iPhone 上,我可以在 0.950 毫秒内计算出一个 1MB 文件的 SHA-256 哈希值,速度够快吗?顺便说一下,在我的手机上,SHA-256 比 MD5 稍微快一些。 - zaph
显示剩余2条评论

8
using System.Security.Cryptography;
string data="test";
byte[] hash;
using (MD5 md5 = MD5.Create())
{
    md5.Initialize();
    md5.ComputeHash(Encoding.UTF8.GetBytes(data));
    hash = md5.Hash;
}

哈希是一个16字节的数组,你可以将其转换为一些十六进制字符串或Base64编码字符串进行存储。

编辑:

那个哈希代码有什么用途?

hash(x)!= hash(y)可以得出x!= y,但是从hash(x) == hash(y)通常无法推导出x==y


2
这将导致性能缓慢,安全哈希不是用于避免冲突的。 - sm_
1
@somerandomdude,就像每个哈希函数一样,您必须比较原始数据,以防您想要绝对确定。您可以尝试其他哈希算法,但必须始终预期发生冲突。这就是“从hash(x)== hash(y)无法推导出x == y”的含义。 - lexx9999
2
@zaph 它们是为了安全目的而设计的。虽然它可以达到避免碰撞的目的,但并不意味着它是正确的工具。 - sm_
1
@zaph,你认为基于哈希对象的请求路由到分片数据存储是否需要使用加密哈希?你需要读一些相关资料了伙计。我猜设计非加密哈希算法如Murmur、FNV、SuperFastHash的人都错了,他们应该听从你的建议。 - sm_
1
我并不是在抨击加密哈希函数或者它们在应该工作的地方被证明有效。我只是想说,如果唯一目的是随机性,那么根据性能需求,加密哈希算法并不总是最佳选择。现在你可以反驳并且尽情表达自己的观点,但请尝试接受其他人的意见。这并不会伤害到你。 - sm_
显示剩余7条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接