I'm using to fmt.Sscan convert a string to any type, here is what I'm doing:
package main
import (
"fmt"
"reflect"
)
func test() interface{} {
return 0
}
func main() {
a := test() // this could be any type
v := "10" // this could be anything
fmt.Println(reflect.TypeOf(a), reflect.TypeOf(&a))
_, err := fmt.Sscan(v, &a)
fmt.Println(err)
}
This code is failing because Sscan doesn't accept interfaces as the second value: can't scan type: *interface {}. demo
What I find most weird is that the first print prints: int *interface {}, is it a int or an interface?
How can I assert a to the right type (it could be any primitive)? Is there a solution that doesn't include a giant switch statement?
Thank you.
Here's how to convert a string to a value of any type supported by the fmt package:
// convert converts s to the type of argument t and returns a value of that type.
func convert(s string, t interface{}) (interface{}, error) {
// Create pointer to value of the target type
v := reflect.New(reflect.TypeOf(t))
// Scan to the value by passing the pointer SScan
_, err := fmt.Sscan(s, v.Interface())
// Dereference the pointer and return the value.
return v.Elem().Interface(), err
}
Call it like this:
a := test()
a, err := convert("10", a)
fmt.Println(a, err)
Run it on the Playground
Related
Is it possible to update the value of a string when we execute a for loop?
package main
import (
"fmt"
"strings"
)
func Chop(r int, s string) string {
return s[r:]
}
func main() {
s:= "ThisIsAstring1ThisIsAstring2ThisIsAstring3"
for strings.Contains(s, "string") {
// Original value > ThisIsAstring1ThisIsAstring2ThisIsAstring3
fmt.Println(s)
// I delete a part of the string > ThisIsAstring1
remove := len(s)/3
// Now, I update the value of string > string := ThisIsAstring2ThisIsAstring3
s := Chop(remove, s)
fmt.Println(s)
break
}
}
I don't know how to do it.
I have no clue what the use case is, but here goes. Let's start with identifying the issues in your code:
// You cannot use a reserved keyword "string" as a variable name
string:= "ThisIsAstring1ThisIsAstring2ThisIsAstring3"
for strings.Contains(string, "string") {
// Remove is a float, but you need to pass an int into your chop function
remove := len(string)/3
// You're reassigning your string variable. You really just want =, not :=
string := Chop(remove, string)
fmt.Println(string)
}
Now, here's a solution that will work for your use case:
str := "ThisIsAstring1ThisIsAstring2ThisIsAstring3"
for strings.Contains(str, "string") {
fmt.Println(str)
remove := int(len(str) / 3)
str = Chop(remove, str)
}
fmt.Println(str)
GoPlay:
https://play.golang.org/p/NdROIFDS_5
Is there a generic helper method in Go to convert a string to the correct value based on reflect.Kind?
Or do I need to implement the switch over all kinds myself?
I have a value like "143" as a string and a reflect.Value with kind "UInt16" and like to convert that string value and set it into the UInt16 value of my struct.
My current code looks like:
func setValueFromString(v reflect.Value, strVal string) error {
switch v.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
val, err := strconv.ParseInt(strVal, 0, 64)
if err != nil {
return err
}
if v.OverflowInt(val) {
return errors.New("Int value too big: " + strVal)
}
v.SetInt(val)
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
val, err := strconv.ParseUint(strVal, 0, 64)
if err != nil {
return err
}
if v.OverflowUint(val) {
return errors.New("UInt value too big: " + strVal)
}
v.SetUint(val)
case reflect.Float32:
val, err := strconv.ParseFloat(strVal, 32)
if err != nil {
return err
}
v.SetFloat(val)
case reflect.Float64:
val, err := strconv.ParseFloat(strVal, 64)
if err != nil {
return err
}
v.SetFloat(val)
case reflect.String:
v.SetString(strVal)
case reflect.Bool:
val, err := strconv.ParseBool(strVal)
if err != nil {
return err
}
v.SetBool(val)
default:
return errors.New("Unsupported kind: " + v.Kind().String())
}
return nil
}
This works already, but I wonder if this is already implemented somewhere else.
Edit: Answer to the original question ("how to obtain a reflect.Kind from its string representation") is at the end. Answer to your edited question follows:
What you're doing is the fastest and "safest". If you don't want to hassle with that big switch, you may take advantage of e.g. the json package which already contains this switch to decode values from JSON string (in encoding/json/decode.go, unexported function literalStore()).
Your decoding function could look like this:
func Set(v interface{}, s string) error {
return json.Unmarshal([]byte(s), v)
}
A simple call to json.Unmarshal(). Using / testing it:
{
var v int
err := Set(&v, "1")
fmt.Println(v, err)
}
{
var v int
err := Set(&v, "d")
fmt.Println(v, err)
}
{
var v uint32
err := Set(&v, "3")
fmt.Println(v, err)
}
{
var v bool
err := Set(&v, "true")
fmt.Println(v, err)
}
{
var v float32
err := Set(&v, `5.1`)
fmt.Println(v, err)
}
{
var v string
err := Set(&v, strconv.Quote("abc"))
fmt.Println(v, err)
}
One thing to note: when you want to pass a string, that must be quoted, e.g. with strconv.Quote(). Output (try it on the Go Playground):
1 <nil>
0 invalid character 'd' looking for beginning of value
3 <nil>
true <nil>
5.1 <nil>
abc <nil>
If you don't want to require quoted strings (which just complicates things), you may build it into the Set() function:
func Set(v interface{}, s string) error {
if t := reflect.TypeOf(v); t.Kind() == reflect.Ptr &&
t.Elem().Kind() == reflect.String {
s = strconv.Quote(s)
}
return json.Unmarshal([]byte(s), v)
}
And then you may call it with the address of a string variable and a string value unquoted:
var v string
err := Set(&v, "abc")
fmt.Println(v, err)
Try this variant on the Go Playground.
Answer to the original question: how to obtain a reflect.Kind from its string representation:
Declaration of reflect.Kind:
type Kind uint
The different values of reflect.Kinds are constants:
const (
Invalid Kind = iota
Bool
Int
Int8
// ...
Struct
UnsafePointer
)
And the reflect package provides only a single method for the reflect.Kind() type:
func (k Kind) String() string
So as it stands, you cannot obtain a reflect.Kind from its string representation (only the reverse direction is possible by using the Kind.String() method). But it's not that hard to provide this functionality.
What we'll do is we build a map from all the kinds:
var strKindMap = map[string]reflect.Kind{}
We init it like this:
func init() {
for k := reflect.Invalid; k <= reflect.UnsafePointer; k++ {
strKindMap[k.String()] = k
}
}
This is possible and correct because constants are initialized using iota which evaluates to successive untyped integer constants, and the first value is reflect.Invalid and the last is reflect.UnsafePointer.
And now you can obtain reflect.Kind from its string representation by simply indexing this map. A helper function which does that:
func strToKind(s string) reflect.Kind {
k, ok := strKindMap[s]
if !ok {
return reflect.Invalid
}
return k
}
And we're done. Testing / using it:
fmt.Printf("All: %#v\n", strKindMap)
for _, v := range []string{"Hey", "uint8", "ptr", "func", "chan", "interface"} {
fmt.Printf("String: %q, Kind: %v (%#v)\n", v, strToKind(v), strToKind(v))
}
Output (try it on the Go Playground):
All: map[string]reflect.Kind{"int64":0x6, "uint8":0x8, "uint64":0xb, "slice":0x17, "uintptr":0xc, "int8":0x3, "array":0x11, "interface":0x14, "unsafe.Pointer":0x1a, "complex64":0xf, "complex128":0x10, "int":0x2, "uint":0x7, "int16":0x4, "uint16":0x9, "map":0x15, "bool":0x1, "int32":0x5, "ptr":0x16, "string":0x18, "func":0x13, "struct":0x19, "invalid":0x0, "uint32":0xa, "float32":0xd, "float64":0xe, "chan":0x12}
String: "Hey", Kind: invalid (0x0)
String: "uint8", Kind: uint8 (0x8)
String: "ptr", Kind: ptr (0x16)
String: "func", Kind: func (0x13)
String: "chan", Kind: chan (0x12)
String: "interface", Kind: interface (0x14)
I am having difficulty understanding the relationship between interfaces and structs in go. I have declared an interface called Datatype as follows:
package main
type Datatype interface {
Unmarshal(record []string) error
String() string
}
I have also created several structs that implement this interface. Here is one simple example:
package main
import (
"encoding/csv"
"fmt"
"gopkg.in/validator.v2"
"reflect"
"strconv"
"time"
)
type User struct {
Username string `validate:"nonzero"`
UserId string `validate:"nonzero"`
GivenName string `validate:"nonzero"`
FamilyName string `validate:"nonzero"`
Email string `validate:"regexp=^[0-9a-zA-Z]+#[0-9a-zA-Z]+(\\.[0-9a-zA-Z]+)+$"`
SMS string `validate:"nonzero"`
Phone string `validate:"min=10"`
DateOfBirth time.Time
}
type Users []User
func (u *User) Unmarshal(record []string) error {
s := reflect.ValueOf(u).Elem()
if s.NumField() != len(record) {
return &FieldMismatch{s.NumField(), len(record)}
}
for i := 0; i > s.NumField(); i++ {
f := s.Field(i)
switch f.Type().String() {
case "string":
f.SetString(record[i])
case "int", "int64":
ival, err := strconv.ParseInt(record[i], 10, 0)
if err != nil {
return err
}
f.SetInt(ival)
default:
return &UnsupportedType{f.Type().String()}
}
}
return nil
}
func (u *User) String() string {
return fmt.Sprintf("%#v", u)
}
func (u *User) populateFrom(reader *csv.Reader) (Users, error) {
var users Users
for {
record, err := reader.Read()
check(err)
err = u.Unmarshal(record)
check(err)
valid := validator.Validate(u)
if valid == nil {
user := *u
users = append(users, user)
} else {
fmt.Println("Validation error?: ", valid)
}
}
return users, nil
}
Problem:
As you can see, I also have a type called Users which is just []User. When I try to return this type from a function that has a return type of []Datatype, I get the following error message:
cannot use results (type Users) as type []Datatype in return argument
I'm sure I'm missing something obvious but it seems to me that this should work.
Question:
Could someone please explain why it does not work? Is there a better (more idiomatic) way to achieve this end result?
Slices are not covariant; even though User implements Datatype, []User does not implement []Datatype (because nothing implements []Datatype: it itself is not an interface type, it's just a slice type whose element type is an interface type).
Edited to add: As Dave C points out in a comment above, a closely-related question appears in the Go FAQ. [link] The Go FAQ is licensed in a way that's compatible with Stack Exchange content, so, here's the question in its entirety:
Can I convert a []T to an []interface{}?
Not directly, because they do not have the same representation in memory. It is necessary to copy the elements individually to the destination slice. This example converts a slice of int to a slice of interface{}:
t := []int{1, 2, 3, 4}
s := make([]interface{}, len(t))
for i, v := range t {
s[i] = v
}
Is there any way to range over a string in Go templates (that is, from the code in the template itself, not from native Go)? It doesn't seem to be supported directly (The value of the pipeline must be an array, slice, map, or channel.), but is there some hack like splitting the string into an array of single-character strings or something?
Note that I am unable to edit any go source: I'm working with a compiled binary here. I need to make this happen from the template code alone.
You can use FuncMap to split string into characters.
package main
import (
"text/template"
"log"
"os"
)
func main() {
tmpl, err := template.New("foo").Funcs(template.FuncMap{
"to_runes": func(s string) []string {
r := []string{}
for _, c := range []rune(s) {
r = append(r, string(c))
}
return r
},
}).Parse(`{{range . | to_runes }}[{{.}}]{{end}}`)
if err != nil {
log.Fatal(err)
}
err = tmpl.Execute(os.Stdout, "hello world")
if err != nil {
log.Fatal(err)
}
}
This should be:
[h][e][l][l][o][ ][w][o][r][l][d]
here is my code and I don't understand why the decode function doesn't work.
Little insight would be great please.
func EncodeB64(message string) (retour string) {
base64Text := make([]byte, base64.StdEncoding.EncodedLen(len(message)))
base64.StdEncoding.Encode(base64Text, []byte(message))
return string(base64Text)
}
func DecodeB64(message string) (retour string) {
base64Text := make([]byte, base64.StdEncoding.DecodedLen(len(message)))
base64.StdEncoding.Decode(base64Text, []byte(message))
fmt.Printf("base64: %s\n", base64Text)
return string(base64Text)
}
It gaves me :
[Decode error - output not utf-8][Decode error - output not utf-8]
The len prefix is superficial and causes the invalid utf-8 error:
package main
import (
"encoding/base64"
"fmt"
"log"
)
func main() {
str := base64.StdEncoding.EncodeToString([]byte("Hello, playground"))
fmt.Println(str)
data, err := base64.StdEncoding.DecodeString(str)
if err != nil {
log.Fatal("error:", err)
}
fmt.Printf("%q\n", data)
}
(Also here)
Output
SGVsbG8sIHBsYXlncm91bmQ=
"Hello, playground"
EDIT: I read too fast, the len was not used as a prefix. dystroy got it right.
DecodedLen returns the maximal length.
This length is useful for sizing your buffer but part of the buffer won't be written and thus won't be valid UTF-8.
You have to use only the real written length returned by the Decode function.
l, _ := base64.StdEncoding.Decode(base64Text, []byte(message))
log.Printf("base64: %s\n", base64Text[:l])
To sum up the other two posts, here are two simple functions to encode/decode Base64 strings with Go:
// Dont forget to import "encoding/base64"!
func base64Encode(str string) string {
return base64.StdEncoding.EncodeToString([]byte(str))
}
func base64Decode(str string) (string, bool) {
data, err := base64.StdEncoding.DecodeString(str)
if err != nil {
return "", true
}
return string(data), false
}
Try it!
#Denys Séguret's answer is almost 100% correct. As an improvement to avoid wasting memory with non used space in base64Text, you should use base64.DecodedLen. Take a look at how base64.DecodeString uses it.
It should look like this:
func main() {
message := base64.StdEncoding.EncodeToString([]byte("Hello, playground"))
base64Text := make([]byte, base64.StdEncoding.DecodedLen(len(message)))
n, _ := base64.StdEncoding.Decode(base64Text, []byte(message))
fmt.Println("base64Text:", string(base64Text[:n]))
}
Try it here.
More or less like above, but using []bytes and part of a bigger struct:
func (s secure) encodePayload(body []byte) string {
//Base64 Encode
return base64.StdEncoding.EncodeToString(body)
}
func (s secure) decodePayload(body []byte) ([]byte, error) {
//Base64 Decode
b64 := make([]byte, base64.StdEncoding.DecodedLen(len(body)))
n, err := base64.StdEncoding.Decode(b64, body)
if err != nil {
return nil, err
}
return b64[:n], nil
}