首页 > 解决方案 > Go中的问题是附加到[]字节,写入文件并读取它

问题描述

我正在尝试解析大量 IP(~20mb 或 400 万个 IP),将它们作为字节存储在文件中,然后再读取它们。

我遇到的问题是我希望它们按排序顺序存储,但我看到随机字节片在读回它们时看起来像损坏的 IP。

// 让它被称为 generator.go

var buf []byte


// So this is where we build up `buf`, which we later write to a file.
func writeOut(record RecordStruct) {
    // This line is never hit. All slices have a length of 4, as expected
    if len(record.IPEnd.Bytes()) != 4 {
        fmt.Println(len(record.IPEnd.Bytes()), record.IPEnd.Bytes())
    }

    // Let's append the IP to the byte slice with a seperater of 10 null bytes which we will later call bytes.Split on.
    buf = append(buf, append(record.IPEnd.Bytes(), bytes.Repeat([]byte{0}, 10)...)...)
}

func main () {
    // Called many times. For brevity I won't include all of that logic. 
    // There are no Goroutines in the code and running with -race says all is fine.

    writeOut(...)

    err := ioutil.WriteFile("bin/test", buf, 0644)
}

阅读器.go

func main() {
    bytez, err := ioutil.ReadFile("bin/test")

    if err != nil {
        fmt.Println("Asset was not found.")
    }

    haystack := bytes.Split(bytez, bytes.Repeat([]byte{0}, 10))

    for _, needle := range haystack {
        // Get's hit maybe 10% of the time. The logs are below.
        if len(needle) != 4 {
            fmt.Println(fmt.Println(needle))
        }
    }
}
[188 114 235]
14 <nil>
[120 188 114 235 121]
22 <nil>
[188 148 98]
13 <nil>
[120 188 148 98 121]
21 <nil>

正如您所看到的,IP 位太少或太多。

如果我更改日志以更好地说明问题,看起来最后一个八位字节溢出了?

Fine: [46 36 202 235]
Fine: [46 36 202 239]
Fine: [46 36 202 255]
Weird: [46 36 203]
Weird: [0 46 36 203 1]
Fine: [46 36 203 3]
Fine: [46 36 203 5]
Fine: [46 36 203 7]
Fine: [46 36 203 9]

标签: gobyteslice

解决方案


当 IP 地址以零字节结尾时,代码不会正确拆分字节。通过将地址转换为 16 字节表示并存储不带分隔符的 16 字节记录来修复。

您可以使用以下命令有效地将 v4 和 v6 地址的混合附加到缓冲区:

switch len(p) {
case net.IPv6len: 
    buf = append(buf, p...)
case net.IPv4len:
    buf = append(buf, v4InV6Prefix...)
    buf = append(buf, p...)
default:
    // handle error
}

其中v4InV6Prefix是值为 的包级变量 []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff}

将文件读取为 v6 地址:

 buf, err := ioutil.ReadFile(xxx)
 if err != nil {
     // handle error
 }
 for i := 0; i < len(buf); i += 16 {
    addr := net.IP(buf[i:i+16])
    // do something with addr
 }

请注意,也可以使用 io.Reader 和 io.Writer 以增量方式读取和写入文件。此答案中的代码与应用程序一次性读取和写入文件的问题中的代码相匹配。


推荐阅读