我希望在 GO 中将字符串数组转换为字节数组,以便将其写入磁盘。将字符串数组 ( ) 编码和解码为[]string
字节数组 ( []byte
) 的最佳解决方案是什么?
我正在考虑对字符串数组进行两次迭代,第一次是获取字节数组所需的实际大小,第二次是[]byte(str)
为每个元素写入长度和实际字符串 ()。
解决方案必须能够以其他方式转换它;从一个[]byte
到一个[]string
。
我希望在 GO 中将字符串数组转换为字节数组,以便将其写入磁盘。将字符串数组 ( ) 编码和解码为[]string
字节数组 ( []byte
) 的最佳解决方案是什么?
我正在考虑对字符串数组进行两次迭代,第一次是获取字节数组所需的实际大小,第二次是[]byte(str)
为每个元素写入长度和实际字符串 ()。
解决方案必须能够以其他方式转换它;从一个[]byte
到一个[]string
。
让我们暂时忽略这是 Go 的事实。您需要的第一件事是编组[]string
到的序列化格式。
这里有很多选择。您可以自己构建或使用库。我将假设您不想构建自己的并跳转到支持的序列化格式。
在所有示例中,data 是[]string
您正在读取/写入的文件,而 fp 是您正在读取/写入的文件。错误被忽略,检查函数的返回来处理错误。
Gob 是一种仅限二进制格式。随着字符串数量的增加,它应该相对节省空间。
enc := gob.NewEncoder(fp)
enc.Encode(data)
阅读也很简单
var data []string
dec := gob.NewDecoder(fp)
dec.Decode(&data)
Gob 简单明了。但是,该格式仅适用于其他 Go 代码。
接下来是json。Json 是一种几乎无处不在的格式。这种格式同样易于使用。
enc := json.NewEncoder(fp)
enc.Encode(data)
对于阅读:
var data []string
dec := json.NewDecoder(fp)
dec.Decode(&data)
XML 是另一种常见的格式。但是,它具有相当高的开销并且不那么容易使用。虽然您可以对 gob 和 json 执行相同的操作,但正确的 xml 需要一个根标记。在这种情况下,我们使用根标签“Strings”,每个字符串都包裹在一个“S”标签中。
type Strings struct {
S []string
}
enc := xml.NewEncoder(fp)
enc.Encode(Strings{data})
var x Strings
dec := xml.NewDecoder(fp)
dec.Decode(&x)
data := x.S
CSV 不同于其他的。您有两种选择,使用一条记录 n 行或 n 记录 1 行。以下示例使用 n 条记录。如果我用一张唱片会很无聊。它看起来太像其他人了。CSV 只能保存字符串。
enc := csv.NewWriter(fp)
for _, v := range data {
enc.Write([]string{v})
}
enc.Flush()
读书:
var err error
var data string
dec := csv.NewReader(fp)
for err == nil { // reading ends when an error is reached (perhaps io.EOF)
var s []string
s, err = dec.Read()
if len(s) > 0 {
data = append(data, s[0])
}
}
您使用哪种格式是一个偏好问题。还有许多其他可能的编码我没有提到。例如,有一个名为 bencode 的外部库。我个人不喜欢bencode,但它确实有效。它与 bittorrent 元数据文件使用的编码相同。
如果您想制作自己的编码,encoding/binary 是一个不错的起点。这将允许您制作最紧凑的文件,但我认为这不值得付出努力。
gob 包将为您执行此操作http://godoc.org/encoding/gob
使用http://play.golang.org/p/e0FEZm-qiS玩的示例
相同的源代码如下。
package main
import (
"bytes"
"encoding/gob"
"fmt"
)
func main() {
// store to byte array
strs := []string{"foo", "bar"}
buf := &bytes.Buffer{}
gob.NewEncoder(buf).Encode(strs)
bs := buf.Bytes()
fmt.Printf("%q", bs)
// Decode it back
strs2 := []string{}
gob.NewDecoder(buf).Decode(&strs2)
fmt.Printf("%v", strs2)
}
为了说明问题,先转换[]string
为[]byte
再转换[]byte
回[]string
,这是一个简单的解决方案:
package main
import (
"encoding/binary"
"fmt"
)
const maxInt32 = 1<<(32-1) - 1
func writeLen(b []byte, l int) []byte {
if 0 > l || l > maxInt32 {
panic("writeLen: invalid length")
}
var lb [4]byte
binary.BigEndian.PutUint32(lb[:], uint32(l))
return append(b, lb[:]...)
}
func readLen(b []byte) ([]byte, int) {
if len(b) < 4 {
panic("readLen: invalid length")
}
l := binary.BigEndian.Uint32(b)
if l > maxInt32 {
panic("readLen: invalid length")
}
return b[4:], int(l)
}
func Decode(b []byte) []string {
b, ls := readLen(b)
s := make([]string, ls)
for i := range s {
b, ls = readLen(b)
s[i] = string(b[:ls])
b = b[ls:]
}
return s
}
func Encode(s []string) []byte {
var b []byte
b = writeLen(b, len(s))
for _, ss := range s {
b = writeLen(b, len(ss))
b = append(b, ss...)
}
return b
}
func codecEqual(s []string) bool {
return fmt.Sprint(s) == fmt.Sprint(Decode(Encode(s)))
}
func main() {
var s []string
fmt.Println("equal", codecEqual(s))
s = []string{"", "a", "bc"}
e := Encode(s)
d := Decode(e)
fmt.Println("s", len(s), s)
fmt.Println("e", len(e), e)
fmt.Println("d", len(d), d)
fmt.Println("equal", codecEqual(s))
}
输出:
equal true
s 3 [ a bc]
e 19 [0 0 0 3 0 0 0 0 0 0 0 1 97 0 0 0 2 98 99]
d 3 [ a bc]
equal true
我建议使用PutUvarint和 Uvarint来存储/检索len(s)
并使用[]byte(str)
传递str
给一些io.Writer
. 使用已知的字符串长度Uvarint
,可以buf := make([]byte, n)
将 传递buf
给 some io.Reader
。
在整个内容前面加上字符串数组的长度,并对其所有项目重复上述操作。读回整个内容是再次首先阅读外部长度并重复 n 次阅读的项目。
转换[]string
为[]byte
var str = []string{"str1","str2"}
var x = []byte{}
for i:=0; i<len(str); i++{
b := []byte(str[i])
for j:=0; j<len(b); j++{
x = append(x,b[j])
}
}
转换[]byte
为string
str := ""
var x = []byte{'c','a','t'}
for i := 0; i < len(x); i++ {
str += string(x[i])
}
使用字符串包可以轻松完成。首先,您需要将字符串切片转换为字符串。
func Join(elems []string, sep string) string
您需要传递字符串切片和分隔字符串中的元素所需的分隔符。(例如:空格或逗号)
然后您可以通过类型转换轻松地将字符串转换为字节切片。
package main
import (
"fmt"
"strings"
)
func main() {
//Slice of Strings
sliceStr := []string{"a","b","c","d"}
fmt.Println(sliceStr) //prints [a b c d]
//Converting slice of String to String
str := strings.Join(sliceStr,"")
fmt.Println(str) // prints abcd
//Converting String to slice of Bytes
sliceByte := []byte(str) //prints [97 98 99 100]
fmt.Println(sliceByte)
//Converting slice of bytes a String
str2 := string(sliceByte)
fmt.Println(str2) // prints abcd
//Converting string to a slice of Strings
sliceStr2 := strings.Split(str2,"")
fmt.Println(sliceStr2) //prints [a b c d]
}
你可以这样做:
var lines = []string
var ctx = []byte{}
for _, s := range lines {
ctx = append(ctx, []byte(s)...)
}