【问题标题】:Convert a bitstring into a byte array将位串转换为字节数组
【发布时间】:2018-07-16 17:26:06
【问题描述】:

我似乎无法将 1 和 0 的位串转换为字节数组。

这是我目前的代码:

package main

import (
    "strconv"
    "encoding/binary"
    "fmt"
)

func main() {
    /* goal: convert a bit string (ex "10110110") to a byte array */
    bitString := "00000000000000000000000100111000100001100000000000000000000000000000000000000000000000000000000000000000000000000000000000000011"

    bitNum, err := strconv.ParseUint(bitString, 2, 128) // turn my 128-bit bitstring into an int

    if err != nil {
        panic(err) // currently panics with "value out of range"
    }

    // convert my integer to a byte array
    // code from https://stackoverflow.com/questions/16888357/convert-an-integer-to-a-byte-array
    bs := make([]byte, 128)                   // allocate memory for my byte array
    binary.LittleEndian.PutUint64(bs, bitNum) // convert my bitnum to a byte array
    fmt.Println(bs)

}

我显然遗漏了一些东西,但我似乎无法将这种大小的位字符串转换为字节数组。

edit我通过了第一个错误:

package main

import (
    "fmt"
    "strconv"
)

func main() {
    /* goal: convert a bit string (ex "10110110") to a byte array */
    bitString := "00000000000000000000000100111000100001100000000000000000000000000000000000000000000000000000000000000000000000000000000000000011"

    myBytes := make([]byte, 16)
    for len(bitString) > 7 {
        currentByteStr := bitString[:8]
        bitString = bitString[8:]
        currentByteInt, _ := strconv.ParseUint(currentByteStr, 2, 8)
        currentByte := byte(currentByteInt)
        myBytes = append(myBytes, currentByte)
    }

    fmt.Println(myBytes)

}

但它没有输出我期望的字节数组的样子:

[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 56 134 0 0 0 0 0 0 0 0 0 0 3]

我希望它是十六进制的?这不是字节数组在 golang 中的样子吗?

【问题讨论】:

  • 您可能需要使用大整数:golang.org/pkg/math/big
  • 将字符串 8bits 乘以 8bits 组成一个字节(uint8)。
  • 我刚刚点击了那个链接。我会看看那些的大小。是的,我想我应该一个字节一个字节地做。
  • 您希望将每个 8 位序列存储为一个字节,将整数存储为可以用作数字的东西,还是每个位存储一个字节?您的示例代码听起来像是一个很大的数字,需要math/big
  • 对不起@Marc,这还不清楚。我想要一个字节数组。

标签: arrays go


【解决方案1】:

我认为在这种非常指定的任务的情况下,最好手动构造数组。

func main() {
    /* goal: convert a bit string (ex "10110110") to a byte array */
    bitString := "00000000000000000000000100111000100001100000000000000000000000000000000000000000000000000000000000000000000000000000000000000011"

    lenB := len(bitString) / 8 + 1
    bs:=make([]byte,lenB)

    count,i := 0,0
    var now byte
    for _,v:=range bitString {
        if count == 8 {
            bs[i]=now
            i++
            now,count = 0,0
        }
        now = now << 1 + byte(v-'0')
        count++
    }
    if count!=0 {
        bs[i]=now << (8-byte(count))
        i++
    }

    bs=bs[:i:i]
    fmt.Println(bs)

}

代码使用count 来计算自上次“flush”和“flush”以来消耗的位数,当有 8 个时。变量i 保留字节区域的信息,并且在循环之后可能会有一个 final冲洗。

byte(v-'0')是从符文到比特的转换。

游乐场:https://play.golang.org/p/eB1mc_FjiQc

【讨论】:

  • 你的回答和我一样(除了我只是想让我的字节数组更大一点)。
  • @Jeff 你说的更大是什么意思?
【解决方案2】:

我会选择创建一个新类型 bitString,它具有转换为 []byte 和 []string 的方法(如果您愿意,可以转换为十六进制)。这也应该防止输入字符串不能被 8 整除。

package main

import (
    "encoding/hex"
    "fmt"
    "strconv"
)

type bitString string

func (b bitString) AsByteSlice() []byte {
    var out []byte
    var str string

    for i := len(b); i > 0; i -= 8 {
        if i-8 < 0 {
            str = string(b[0:i])
        } else {
            str = string(b[i-8 : i])
        }
        v, err := strconv.ParseUint(str, 2, 8)
        if err != nil {
            panic(err)
        }
        out = append([]byte{byte(v)}, out...)
    }
    return out
}

func (b bitString) AsHexSlice() []string {
    var out []string
    byteSlice := b.AsByteSlice()
    for _, b := range byteSlice {
        out = append(out, "0x" + hex.EncodeToString([]byte{b}))
    }
    return out
}

func main() {
    x := bitString("00000000000000000000000100111000100001100000000000000000000000000000000000000000000000000000000000000000000000000000000000000011")
    fmt.Println(x.AsByteSlice())
    fmt.Println(x.AsHexSlice())
}

输出

[64 0 1 56 134 0 0 0 0 0 0 0 0 0 0 3]
[0x00 0x00 0x01 0x38 0x86 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x03]

Playgroud

如果速度是一个问题,那么可以通过分配具有适当容量的切片并自己跟踪索引来实现优化。

【讨论】:

  • 开销最大的是strconv.ParseUint。不是内存分配。
  • @leafbebop 很公平......但这就是 pprof 的用途:)。
  • 这正是 pprof 之前告诉我的(我找不到确切的代码,但基本相同)。
【解决方案3】:

这是一个简单的解决方案。

package main

import (
    "errors"
    "fmt"
)

var ErrRange = errors.New("value out of range")

func bitStringToBytes(s string) ([]byte, error) {
    b := make([]byte, (len(s)+(8-1))/8)
    for i := 0; i < len(s); i++ {
        c := s[i]
        if c < '0' || c > '1' {
            return nil, ErrRange
        }
        b[i>>3] |= (c - '0') << uint(7-i&7)
    }
    return b, nil
}

func main() {
    s := "00000000000000000000000100111000100001100000000000000000000000000000000000000000000000000000000000000000000000000000000000000011"
    b, err := bitStringToBytes(s)
    if err != nil {
        fmt.Println(s, err)
    }
    fmt.Println(b)
    fmt.Printf("%b\n", b)
    fmt.Printf("%x\n", b)
}

游乐场:https://play.golang.org/p/Ils1gzkY4Dg

输出:

[0 0 1 56 134 0 0 0 0 0 0 0 0 0 0 3]
[0 0 1 111000 10000110 0 0 0 0 0 0 0 0 0 0 11]
00000138860000000000000000000003

注意:更新为@leaf bebop的优化。

【讨论】:

  • b[i &gt;&gt; 3] |= byte((r-'0') &lt;&lt; uint(7-i&amp;7) 呢?
  • 有道理。我的印象是% 很慢。这似乎不适用于 Go。
  • 感谢代码,我想稍后在我不使用移动设备时进行更多讨论,但我认为这太离题了。
猜你喜欢
  • 1970-01-01
  • 2019-12-13
  • 2011-02-28
  • 2011-02-02
  • 2019-12-10
  • 2010-09-26
  • 2010-10-17
相关资源
最近更新 更多