我希望将字符串数组转换为GO中的字节数组,以便将其写入磁盘。将字符串数组([]string
)编码和解码为字节数组([]byte
)的最佳解决方案是什么?
我在考虑迭代字符串数组两次,第一次是获取字节数组所需的实际大小,然后是第二次为每个元素写入长度和实际字符串([]byte(str)
)。
解决方案必须能够以其他方式转换它;从[]byte
到[]string
。
答案 0 :(得分:23)
让我们忽略这是Go的事实。您需要的第一件事就是将[]string
编组为序列化格式。
这里有很多选择。您可以构建自己的或使用库。我将假设您不想构建自己的,并跳转到序列化格式支持。
在所有示例中,数据是[]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需要一个root标签。在这种情况下,我们使用根标签“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元数据文件使用的编码相同。
如果你想自己编码,编码/二进制是一个很好的起点。这样可以让你创建最紧凑的文件,但我觉得这不值得付出努力。
答案 1 :(得分:7)
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)
}
答案 2 :(得分:2)
要说明问题,请将[]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
答案 3 :(得分:1)
我建议使用PutUvarint和Uvarint来存储/检索len(s)
并使用[]byte(str)
将str
传递给某些io.Writer
。如果字符串长度为Uvarint
,则可以buf := make([]byte, n)
将buf
传递给某些io.Reader
。
在字符串数组的长度前面加上整个内容,并对其所有项重复上述内容。读完整个东西再次首先读取外部长度并重复读取项目的n次。
答案 4 :(得分:1)
将[]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])
}
答案 5 :(得分:0)
您可以执行以下操作:
var lines = []string
var ctx = []byte{}
for _, s := range lines {
ctx = append(ctx, []byte(s)...)
}
答案 6 :(得分:0)
使用 strings 包可以轻松完成。首先,您需要将字符串切片转换为字符串。
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]
}