Lets ignore the fact that this is Go for a second. The first thing you need is a serialization format to marshal the []string into.
There are many option here. You could build your own or use a library. I am going to assume you don't want to build your own and jump to serialization formats go supports.
In all examples, data is the []string and fp is the file you are reading/writing to. Errors are being ignored, check the returns of functions to handle errors.
Gob
Gob is a go only binary format. It should be relatively space efficient as the number of strings increases.
enc := gob.NewEncoder(fp)
enc.Encode(data)
Reading is also simple
var data []string
dec := gob.NewDecoder(fp)
dec.Decode(&data)
Gob is simple and to the point. However, the format is only readable with other Go code.
Json
Next is json. Json is a format used just about everywhere. This format is just as easy to use.
enc := json.NewEncoder(fp)
enc.Encode(data)
And for reading:
var data []string
dec := json.NewDecoder(fp)
dec.Decode(&data)
XML
XML is another common format. However, it has pretty high overhead and not as easy to use. While you could just do the same you did for gob and json, proper xml requires a root tag. In this case, we are using the root tag "Strings" and each string is wrapped in an "S" tag.
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 is different from the others. You have two options, use one record with n rows or n records with 1 row. The following example uses n records. It would be boring if I used one record. It would look too much like the others. CSV can ONLY hold strings.
enc := csv.NewWriter(fp)
for _, v := range data {
enc.Write([]string{v})
}
enc.Flush()
To read:
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])
}
}
Which format you use is a matter of preference. There are many other possible encodings that I have not mentioned. For example, there is an external library called bencode. I don't personally like bencode, but it works. It is the same encoding used by bittorrent metadata files.
If you want to make your own encoding, encoding/binary is a good place to start. That would allow you to make the most compact file possible, but I hardly thing it is worth the effort.
Answer from Stephen Weinberg on Stack OverflowLets ignore the fact that this is Go for a second. The first thing you need is a serialization format to marshal the []string into.
There are many option here. You could build your own or use a library. I am going to assume you don't want to build your own and jump to serialization formats go supports.
In all examples, data is the []string and fp is the file you are reading/writing to. Errors are being ignored, check the returns of functions to handle errors.
Gob
Gob is a go only binary format. It should be relatively space efficient as the number of strings increases.
enc := gob.NewEncoder(fp)
enc.Encode(data)
Reading is also simple
var data []string
dec := gob.NewDecoder(fp)
dec.Decode(&data)
Gob is simple and to the point. However, the format is only readable with other Go code.
Json
Next is json. Json is a format used just about everywhere. This format is just as easy to use.
enc := json.NewEncoder(fp)
enc.Encode(data)
And for reading:
var data []string
dec := json.NewDecoder(fp)
dec.Decode(&data)
XML
XML is another common format. However, it has pretty high overhead and not as easy to use. While you could just do the same you did for gob and json, proper xml requires a root tag. In this case, we are using the root tag "Strings" and each string is wrapped in an "S" tag.
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 is different from the others. You have two options, use one record with n rows or n records with 1 row. The following example uses n records. It would be boring if I used one record. It would look too much like the others. CSV can ONLY hold strings.
enc := csv.NewWriter(fp)
for _, v := range data {
enc.Write([]string{v})
}
enc.Flush()
To read:
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])
}
}
Which format you use is a matter of preference. There are many other possible encodings that I have not mentioned. For example, there is an external library called bencode. I don't personally like bencode, but it works. It is the same encoding used by bittorrent metadata files.
If you want to make your own encoding, encoding/binary is a good place to start. That would allow you to make the most compact file possible, but I hardly thing it is worth the effort.
The gob package will do this for you http://godoc.org/encoding/gob
Example to play with http://play.golang.org/p/e0FEZm-qiS
same source code is below.
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)
}
Most programs should probably just cast between the two types:
s1 := "this is a string" b := ([]byte)(s1) s2 := (string)(b)
However, note that casting involves copying the data. This is because Go strings are immutable, so copying is necessary to ensure correctness. (BTW, Rust does this differently because it ensures immutability via ownership.)
That said, in some specific cases programmers might prefer to avoid copying data in order to save memory and improve performance for large amounts of data.
I think this is the best way to achieve it in Go 1.20+:
// This conversion *does not* copy data. Note that casting via "(string)([]byte)" *does* copy data.
// Also note that you *should not* change the byte slice after conversion, because Go strings
// are treated as immutable. This would cause a segmentation violation panic.
func BytesToString(bytes []byte) string {
return unsafe.String(unsafe.SliceData(bytes), len(bytes))
}
// This conversion *does not* copy data. Note that casting via "([]byte)(string)" *does* copy data.
// Also note that you *should not* change the byte slice after conversion, because Go strings
// are treated as immutable. This would cause a segmentation violation panic.
func StringToBytes(string_ string) (bytes []byte) {
return unsafe.Slice(unsafe.StringData(string_), len(string_))
}This is indeed the approach you see in the strings.Builder source code.
Note the very important warning that you should never change the byte arrays after using these functions. It would result in a SIGSEGV panic.
For the first function, I've used this in the past:
func BytesToString(bytes []byte) string {
return *(*string)(unsafe.Pointer(&bytes))
}This works, but I wonder what the trade-offs are versus the approach above. Length does not have to be extracted here, but are there edge cases that would cause problems? Or are the two approaches entirely equivalent?
For the second function, I've used this in the past, however it should not be used anymore because StringHeader and SliceHeader are to be deprecated in Go 1.21. Including it here for reference:
func StringToBytes(string_ string) (bytes []byte) {
stringHeader := (*reflect.StringHeader)(unsafe.Pointer(&string_))
sliceHeader := (*reflect.SliceHeader)(unsafe.Pointer(&bytes))
sliceHeader.Data = stringHeader.Data
sliceHeader.Cap = stringHeader.Len
sliceHeader.Len = stringHeader.Len
return
}Other relevant reference material:
-
https://github.com/golang/go/issues/25484
-
https://github.com/golang/go/issues/19367
-
https://github.com/golang/go/issues/53003#issuecomment-1140276077
Convert string array to byte array in go - Stack Overflow
go - How to assign string to bytes array - Stack Overflow
types - How to convert byte array to string in Go - Stack Overflow
proposal: Go 2: permit converting a string constant to a byte array type
Use a conversion to convert each string to a []byte.
ex := [...]string{"a", "o", ".", ".", "2", ".", ".", "9"}
var ey [len(ex)][]byte
for i := range ex {
ey[i] = []byte(ex[i])
}
Use this code if your intent is to get a byte array of the joined strings. This code only works when the strings are single ASCII characters.
ex := [...]string{"a", "o", ".", ".", "2", ".", ".", "9"}
var ey [len(ex)]byte
for i := range ex {
ey[i] = ex[i][0]
}
Use this expression of you want to get a slice of bytes of the joined strings: []byte(strings.Join(ex[:], ""))
I don't know the your context for doing this, but my guess is that it's more appropriate to use a slice than an array:
ex := []string{"a", "o", ".", ".", "2", ".", ".", "9"}
ey := make([][]byte, len(ex))
for i := range ex {
ey[i] = []byte(ex[i])
}
..
s := []byte(strings.Join(ex, ""))
Depending on if this is part of a code-generation pipeline, you can do this a couple of ways.
Directly:
bs := [...]byte{'a', 'o', '.', '.', '2', '.', '.', '9'}
or indirectly:
ex := [...]string{"a", "o", ".", ".", "2", ".", ".", "9"}
bs := [...]byte{
ex[0][0],
ex[1][0],
ex[2][0],
ex[3][0],
ex[4][0],
ex[5][0],
ex[6][0],
ex[7][0],
} // type [8]int8 i.e. [8]byte
https://play.golang.org/p/iMEjFpCKAaW
Depending on your use case, these ways may be too rigid. For dynamic initialization methods see the other answers here.
Safe and simple:
[]byte("Here is a string....")
For converting from a string to a byte slice, string -> []byte:
[]byte(str)
For converting an array to a slice, [20]byte -> []byte:
arr[:]
For copying a string to an array, string -> [20]byte:
copy(arr[:], str)
Same as above, but explicitly converting the string to a slice first:
copy(arr[:], []byte(str))
- The built-in
copyfunction only copies to a slice, from a slice. - Arrays are "the underlying data", while slices are "a viewport into underlying data".
- Using
[:]makes an array qualify as a slice. - A string does not qualify as a slice that can be copied to, but it qualifies as a slice that can be copied from (strings are immutable).
- If the string is too long,
copywill only copy the part of the string that fits (and multi-byte runes may then be copied only partly, which will corrupt the last rune of the resulting string).
This code:
var arr [20]byte
copy(arr[:], "abc")
fmt.Printf("array: %v (%T)\n", arr, arr)
...gives the following output:
array: [97 98 99 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0] ([20]uint8)
I also made it available at the Go Playground
The easiest method I use to convert byte to string is:
myString := string(myBytes[:])
The easiest way to convert []byte to string in Go:
myString := string(myBytes)
Note: to convert a "sha1 value to string" like you're asking, it needs to be encoded first, since a hash is binary. The traditional encoding for SHA hashes is hex (import "encoding/hex"):
myString := hex.EncodeToString(sha1bytes)