Преобразовать [] строку в [] байт
Я хочу преобразовать строковый массив в массив байтов в GO, чтобы записать его на диск. Что является оптимальным решением для кодирования и декодирования массива строк ([]string
) в массив байтов ([]byte
)?
Я думал об итерации строкового массива дважды, сначала для получения фактического размера, необходимого для массива байтов, а затем второго для записи длины и фактической строки ([]byte(str)
) для каждого элемента.
Решение должно иметь возможность конвертировать его в другую сторону; от []byte
до a []string
.
Ответы
Ответ 1
Давайте проигнорируем тот факт, что это Go на секунду. Первое, что вам нужно, это формат сериализации для маршализации []string
в.
Здесь много вариантов. Вы можете создать свой собственный или использовать библиотеку. Я собираюсь предположить, что вы не хотите создавать свои собственные и перейти к форматам сериализации, поддерживаемым.
Во всех примерах данные являются []string
, а fp - это файл, который вы читаете/записываете. Ошибки игнорируются, проверяйте возвращаемые функции для обработки ошибок.
Капля
Gob - это только двоичный формат. Он должен быть относительно эффективным по площади, поскольку количество строк увеличивается.
enc := gob.NewEncoder(fp)
enc.Encode(data)
Чтение также прост
var data []string
dec := gob.NewDecoder(fp)
dec.Decode(&data)
Гоб прост и близок к точке. Тем не менее, формат доступен только для чтения с другим кодом Go.
Json
Далее json. Json - это формат, используемый практически везде. Этот формат так же прост в использовании.
enc := json.NewEncoder(fp)
enc.Encode(data)
И для чтения:
var data []string
dec := json.NewDecoder(fp)
dec.Decode(&data)
XML
XML - еще один распространенный формат. Тем не менее, он имеет довольно высокие накладные расходы и не так прост в использовании. Хотя вы могли бы просто сделать то же самое, что и для gob и json, для правильного xml требуется корневой тег. В этом случае мы используем корневой тег "Строки", и каждая строка обернута тегом "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
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.
Если вы хотите создать свою собственную кодировку, кодировка/бинар - это хорошее место для начала. Это позволит вам сделать максимально компактный файл, но вряд ли это стоит усилий.
Ответ 2
Пакет 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)
}
Ответ 3
Я бы предложил использовать PutUvarint и Uvarint для сохранения/извлечения len(s)
и с помощью []byte(str)
передать str
в некоторый io.Writer
. С длиной строки, известной из Uvarint
, можно buf := make([]byte, n)
и передать buf
в некоторый io.Reader
.
Подготовьте все это с длиной массива строк и повторите описанное выше для всех своих элементов. Чтение всего назад снова считывает первую внешнюю длину и повторяет n-раз, когда элемент читается.
Ответ 4
Чтобы проиллюстрировать проблему, преобразуйте []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
Ответ 5
для преобразования []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])
}