VBA哈希字符串

VBA哈希字符串,vba,hash,excel-2003,Vba,Hash,Excel 2003,如何使用Excel VBA获取长字符串的短哈希 给出了什么 输入字符串长度不超过80个字符 有效输入字符为:[0..9][A_Z]。\uz/ 有效的输出字符为[0..9][A_Z][A_Z](可以使用小写和大写) 输出哈希不应超过12个字符(越短越好) 根本不需要是唯一的,因为这将导致太长的散列 我到目前为止所做的事情 我认为这是一个好的开始,因为它生成了一个4位十六进制代码(CRC16) 但4位数字太小了。在我对400个字符串的测试中,20%在其他地方得到了一个副本。 产生碰撞的几率太高

如何使用Excel VBA获取长字符串的短哈希

给出了什么

  • 输入字符串长度不超过80个字符
  • 有效输入字符为:[0..9][A_Z]。\uz/
  • 有效的输出字符为[0..9][A_Z][A_Z](可以使用小写和大写)
  • 输出哈希不应超过12个字符(越短越好)
  • 根本不需要是唯一的,因为这将导致太长的散列
我到目前为止所做的事情

我认为这是一个好的开始,因为它生成了一个4位十六进制代码(CRC16)

但4位数字太小了。在我对400个字符串的测试中,20%在其他地方得到了一个副本。
产生碰撞的几率太高

子测试仪()
对于i=2到433
单元(i,2)=CRC16(单元(i,1))
接下来我
端接头
函数CRC16(txt作为字符串)
暗x等长
Dim mask、i、j、nC、Crc作为整数
作为字符串的Dim c
Crc=&HFFFF
对于nC=1到Len(txt)
j=Val(“&H”+Mid(txt,nC,2))
Crc=Crc异或j
对于j=1到8
掩码=0
如果Crc/2 Int(Crc/2),则掩码=&HA001
Crc=Int(Crc/2)和&H7FFF:Crc=Crc异或掩码
下一个j
下一个nC
CRC16=十六进制$(Crc)
端函数
如何复制

您可以复制这些400。
将它们粘贴到新Excel工作簿中的A列,并执行上面的代码


Q:如何获取足够短(12个字符)且足够长的字符串哈希,以获得少量重复。

将字符串拆分为三个较短的字符串(如果不能被三整除,则最后一个字符串将比其他两个长)。对每个对象运行“short”算法,并连接结果

我可以写代码,但基于问题的质量,我认为你可以从这里开始

编辑:事实证明,这些建议是不够的。您的原始CRC16代码中有一个严重缺陷,即以下代码行:

j = Val("&H" + Mid(txt, nC, 2))
这只处理可以解释为十六进制值的文本:小写和大写字母是相同的,字母表中F之后的任何内容都被忽略(据我所知)。任何好的东西都会出现,这是一个奇迹。如果您将线路替换为

j = asc(mid(txt, nC, 1))
事情进展得更好——每一个ASCII码至少都是以其自身的价值开始生命的

将此更改与我之前提出的建议相结合,您将获得以下代码:

Function hash12(s As String)
' create a 12 character hash from string s

Dim l As Integer, l3 As Integer
Dim s1 As String, s2 As String, s3 As String

l = Len(s)
l3 = Int(l / 3)
s1 = Mid(s, 1, l3)      ' first part
s2 = Mid(s, l3 + 1, l3) ' middle part
s3 = Mid(s, 2 * l3 + 1) ' the rest of the string...

hash12 = hash4(s1) + hash4(s2) + hash4(s3)

End Function

Function hash4(txt)
' copied from the example
Dim x As Long
Dim mask, i, j, nC, crc As Integer
Dim c As String

crc = &HFFFF

For nC = 1 To Len(txt)
    j = Asc(Mid(txt, nC)) ' <<<<<<< new line of code - makes all the difference
    ' instead of j = Val("&H" + Mid(txt, nC, 2))
    crc = crc Xor j
    For j = 1 To 8
        mask = 0
        If crc / 2 <> Int(crc / 2) Then mask = &HA001
        crc = Int(crc / 2) And &H7FFF: crc = crc Xor mask
    Next j
Next nC

c = Hex$(crc)

' <<<<< new section: make sure returned string is always 4 characters long >>>>>
' pad to always have length 4:
While Len(c) < 4
  c = "0" & c
Wend

hash4 = c

End Function
函数hash12(作为字符串)
'从字符串s创建一个12个字符的哈希
Dim l为整数,l3为整数
尺寸s1为字符串,s2为字符串,s3为字符串
l=Len(s)
l3=整数(l/3)
s1=中部(s、1、l3)第一部分
s2=中部(s,l3+1,l3)'中部
s3=中间(s,2*l3+1)'字符串的其余部分。。。
hash12=hash4(s1)+hash4(s2)+hash4(s3)
端函数
函数hash4(txt)
"照搬例子,
暗x等长
Dim mask、i、j、nC、crc作为整数
作为字符串的Dim c
crc=&HFFFF
对于nC=1到Len(txt)
j=Asc(Mid(txt,nC))'20米左右(手工操作,在我的头脑中)

显然,您可以使hash12代码更紧凑一点,而且应该很容易看到如何将其扩展到任意长度


哦,还有最后一件事。如果启用了RC寻址,则使用
=CRC16(“字符串”)
作为电子表格公式会出现难以跟踪的
#REF
错误。。。这就是为什么我将其重命名为hash4

也许其他人会发现这很有用

我收集了一些不同的函数来在VBA中生成字符串的短哈希。
我不相信这些代码,所有的资料都被引用了

    • 函数:
      =CRC16HASH(A1)
      与此
    • 哈希是一个4个字符长的十六进制字符串
    • 19行代码
    • 4位长哈希=6895行中的624个冲突=9%冲突率
    • 函数:
      =CRC16NUMERIC(A1)
      与此
    • 哈希是一个5位数的数字
    • 92代码行
    • 5位长哈希=6895行中的616次冲突=8.9%冲突率
    • 功能:
      =CRC16TWICE(A1)
      与此
    • 哈希是一个8个字符长的十六进制字符串
    • 哈希可以扩展到12/16/20等字符,以进一步降低冲突率
    • 39行代码
    • 8位长散列=6895行中的18次冲突=0.23%冲突率
    • 函数:
      =SHA1TRUNC(A1)
      与此
    • 哈希是一个40个字符长的十六进制字符串
    • 142行代码
    • 可以被截断
    • 4位哈希=6895行中的726次冲突=10.5%冲突率
    • 5位哈希=6895行中的51次冲突=0.73%冲突率
    • 6位哈希=6895行中的0冲突=0%冲突率
    • 函数:
      =BASE64SHA1(A1)
      与此
    • 哈希是一个28个字符长的unicode字符串(区分大小写+特殊字符)
    • 41代码行
    • 需要.NET,因为它使用库“Microsoft MSXML”
    • 可以被截断
    • 4位哈希=6895行中的36次冲突=0.5%冲突率
    • 5位哈希=6895行中的0个冲突=0%冲突率
  • 是包含所有示例函数和大量测试字符串的测试工作簿


    您可以随意添加自己的函数。

    虽然下面的函数不是散列函数,但我已将其用作快速生成数字id的方法,该id在一个小列表上具有较低的冲突率(小到足以通过检查进行验证)

    它的工作原理:A列保存从第2行开始的字符串。在第1行中,A1和B1在字符串的中间位置保持任意的开始和结束位置。该公式使用字符串的第一个字母和从字符串中间提取的固定字母,并使用LEN()作为“扇形函数”,以减少碰撞的机会

     =CODE(A2)*LEN(A2) + CODE(MID(A2,$A$1,$B$1))*LEN(MID(A2,$A$1,$B$1))
    
    如果从具有固定宽度字段的数据库表中提取字符串,则可能需要修剪长度:

     =CODE(TRIM(C8))*LEN(TRIM(C8))
           +CODE(MID(TRIM(C8),$A$1,1))*LEN(MID(TRIM(C8),$A$1,$B$1))
    
    Public Function StrHash(text As String) As Long
        Dim i As Long
        StrHash = &H65D5BAAA
    
        For i = 1 To Len(text)
            StrHash = ((StrHash + AscW(Mid$(text, i, 1))) Mod 69208103) * 31&
        Next
    End Function
    
    Public Function StrHash64(text As String) As String
        Dim i&, h1&, h2&, c&
        h1 = &H65D5BAAA
        h2 = &H2454A5ED
    
        For i = 1 To Len(text)
            c = AscW(Mid$(text, i, 1))
            h1 = ((h1 + c) Mod 69208103) * 31&
            h2 = ((h2 + c) Mod 65009701) * 33&
        Next
    
        StrHash64 = Right("00000000" & Hex(h1), 8) & Right("00000000" & Hex(h2), 8)
    End Function