将任意的Golang接口转换为字节数组

36

我正在尝试编写一个哈希函数,可以接受所有数据类型。一旦进入函数,我将数据处理为一个字节数组。我遇到了一个问题,无法将任意的interface{}转换为字节数组。

我尝试使用二进制包,但它似乎依赖于传递的数据类型。 Write()函数的其中一个参数(文档)需要知道参数的字节顺序。

所有数据类型的大小都是字节的某个倍数(甚至布尔型也是),所以理论上应该很简单。

以下是相关代码:

package bloom

import (
    "encoding/gob"
    "bytes"
)

// adapted from http://bretmulvey.com/hash/7.html
func ComputeHash(key interface{}) (uint, error) {
    var buf bytes.Buffer
    enc := gob.NewEncoder(&buf)
    err := enc.Encode(key)
    if err != nil {
        return 0, err
    }
    data := buf.Bytes()

    var a, b, c uint
    a, b = 0x9e3779b9, 0x9e3779b9
    c = 0;
    i := 0;

    for i = 0; i < len(data)-12; {
        a += uint(data[i+1] | data[i+2] << 8 | data[i+3] << 16 | data[i+4] << 24)
        i += 4
        b += uint(data[i+1] | data[i+2] << 8 | data[i+3] << 16 | data[i+4] << 24)
        i += 4
        c += uint(data[i+1] | data[i+2] << 8 | data[i+3] << 16 | data[i+4] << 24)

        a, b, c = mix(a, b, c);
    }

    c += uint(len(data))

    if i < len(data) {
        a += uint(data[i])
        i++
    }
    if i < len(data) {
        a += uint(data[i] << 8)
        i++
    }
    if i < len(data) {
        a += uint(data[i] << 16)
        i++
    }
    if i < len(data) {
        a += uint(data[i] << 24)
        i++
    }


    if i < len(data) {
        b += uint(data[i])
        i++
    }
    if i < len(data) {
        b += uint(data[i] << 8)
        i++
    }
    if i < len(data) {
        b += uint(data[i] << 16)
        i++
    }
    if i < len(data) {
        b += uint(data[i] << 24)
        i++
    }

    if i < len(data) {
        c += uint(data[i] << 8)
        i++
    }
    if i < len(data) {
        c += uint(data[i] << 16)
        i++
    }
    if i < len(data) {
        c += uint(data[i] << 24)
        i++
    }

    a, b, c = mix(a, b, c)
    return c, nil
}

func mix(a, b, c uint) (uint, uint, uint){
    a -= b; a -= c; a ^= (c>>13);
    b -= c; b -= a; b ^= (a<<8);
    c -= a; c -= b; c ^= (b>>13);
    a -= b; a -= c; a ^= (c>>12);
    b -= c; b -= a; b ^= (a<<16);
    c -= a; c -= b; c ^= (b>>5);
    a -= b; a -= c; a ^= (c>>3);
    b -= c; b -= a; b ^= (a<<10);
    c -= a; c -= b; c ^= (b>>15);

    return a, b, c
}

3
"pkg 'encoding/gob'怎么样?你可以使用它吗?" - nvcnvn
@nvcnvn,看起来工作正常。我之前尝试过,但现在我意识到哈希在小值(0-62)上存在弱点?我改变了我所使用的范围,现在似乎可以工作了。谢谢! - Nate Brennand
修复了哈希函数中的错误,更新的代码在此处找到:https://gist.github.com/natebrennand/10442587 - Nate Brennand
2个回答

77

我的代码中还有其他问题,这让我远离了gob包。后来发现这正是@nvcnvn建议的正确方法。下面是解决此问题的相关代码:

package bloom

import (
    "encoding/gob"
    "bytes"
)

func GetBytes(key interface{}) ([]byte, error) {
    var buf bytes.Buffer
    enc := gob.NewEncoder(&buf)
    err := enc.Encode(key)
    if err != nil {
        return nil, err
    }
    return buf.Bytes(), nil
}

3
随意接受您自己的回答作为问题的答案 :) - photoionized
3
能够展示这个方法的使用示例会很不错。对于像我这样初学 Go 的新手来说,这真的会帮助很多! - Rudi Strydom
1
@RudiStrydom 把一个地图转换成字节以节省空间?这就是我发现它有用的地方。 - Bryce Wayne
当时,我试图编写一个可以处理任意数据结构的布隆过滤器。因此,通过将任何结构体/映射/切片转换为[]byte,我可以将字节切片处理成哈希值。 https://gist.github.com/natebrennand/10442587 - Nate Brennand

5

interface{} 转换为 []bytes 的另一种方法是使用 fmt 包。

/*
* Convert variable `key` from interface{} to []byte
*/

byteKey := []byte(fmt.Sprintf("%v", key.(interface{})))

fmt.Sprintf 将接口值转换为字符串。
[]byte 将字符串值转换为字节。

※ 注意 ※ 如果 interface{} 值是指针,则此方法无法正常工作。请查看 @PassKit 的下面评论。


2
如果接口是指针,那么这可能会产生意想不到的结果,你将得到一个内存地址。https://play.golang.org/p/EgjvrqOyxEi - PassKit
1
值得一提的是,@PassKit 在他的评论中所说的。 - Kristianmitk

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