Go语言中的ToString()函数

154

strings.Join 函数仅接受字符串切片:

s := []string{"foo", "bar", "baz"}
fmt.Println(strings.Join(s, ", "))

但是如果能够传递实现了ToString()函数的任意对象就更好了。

type ToStringConverter interface {
    ToString() string
}

在Go中是否有类似的东西,或者我必须像 int 一样装饰现有类型并编写包装器来使用 strings.Join

func Join(a []ToStringConverter, sep string) string

10
请注意,这样的接口已经存在:http://golang.org/pkg/fmt/#Stringer。 - Denys Séguret
1
请参阅有关接受“Stringer”对象的“Join”函数的相关问题(https://dev59.com/r2rWa4cB1Zd3GeqP_4IF)。 - deamon
@daemon 我不认为需要这个重复。在我看来,现在的问题已经足够清楚了,事实上没有真正的(或完整的)答案并不意味着你必须再次提问。 - Denys Séguret
7个回答

259

为任何命名类型添加String() string方法,享受自定义的“ToString”功能:

package main

import "fmt"

type bin int

func (b bin) String() string {
        return fmt.Sprintf("%b", b)
}

func main() {
        fmt.Println(bin(42))
}

Playground: http://play.golang.org/p/Azql7_pDAA


输出结果

101010

1
你说得对,尽管答案并不意味着转换是唯一的选择。关键在于附加到类型的String()方法。无论fmt.*在哪里找到该方法,它都会使用它来获取这种类型的字符串表示形式。 - zzzz
4
bin(42).String()作为另一个例子添加将更好地回答问题。 - Thellimist
注意:函数Error() string的优先级高于String() string - Geln Yang
3
换句话说,实现Stringer接口:https://golang.org/pkg/fmt/#Stringer - tothemario
如果你使用泛型,这将无法工作。 - honestduane

31

当你拥有自己的struct时,你可以拥有自己的转换为字符串函数。

package main

import (
    "fmt"
)

type Color struct {
    Red   int `json:"red"`
    Green int `json:"green"`
    Blue  int `json:"blue"`
}

func (c Color) String() string {
    return fmt.Sprintf("[%d, %d, %d]", c.Red, c.Green, c.Blue)
}

func main() {
    c := Color{Red: 123, Green: 11, Blue: 34}
    fmt.Println(c) //[123, 11, 34]
}

7

另一个使用结构体的示例:

package types

import "fmt"

type MyType struct {
    Id   int    
    Name string
}

func (t MyType) String() string {
    return fmt.Sprintf(
    "[%d : %s]",
    t.Id, 
    t.Name)
}

在使用时要小心,
使用“+”进行连接无法编译:

t := types.MyType{ 12, "Blabla" }

fmt.Println(t) // OK
fmt.Printf("t : %s \n", t) // OK
//fmt.Println("t : " + t) // Compiler error !!!
fmt.Println("t : " + t.String()) // OK if calling the function explicitly

1
  • 这个很好用
   package main
    
    import "fmt"
    
    
    type Person struct {
        fname, sname string 
        address string 
    }
    
    
    func (p *Person) String() string {
        s:=  fmt.Sprintf("\n %s %s  lives at %s \n", p.fname, p.sname, p.address)
        return s
    }
    
    
    func main(){
        alex := &Person{"Alex", "Smith", "33 McArthur Bvd"}
        fmt.Println(alex)
    
    }


输出:

Alex Smith住在33 McArthur Bvd


1
如果您有一组固定的元素类型可以进行转换,则可以为每个类型定义转换函数,并使用反射来测试元素的实际类型并调用相应的函数的通用转换函数,例如:
func ToStringint(x int) string { 
  return strconv.Itoa(x)
}

func ToStringlong(x int64) string { 
  return strconv.FormatInt(x,10)
} 

func ToStringdouble(x float64) string { 
  return fmt.Sprintf("%f", x)
} 

func ToStringboolean(x bool) string {
  if x { 
    return "true"
  } 
  return "false"
} 

func ToStringOclAny(x interface{}) string { 
  if reflect.TypeOf(x) == TYPEint { 
    return strconv.Itoa(x.(int)) 
  } 

  if reflect.TypeOf(x) == TYPEdouble { 
      return fmt.Sprintf("%f", x.(float64))
  } 

  if reflect.TypeOf(x) == TYPElong { 
    return strconv.FormatInt(x.(int64),10)
  } 

  if reflect.TypeOf(x) == TYPEString { 
     return x.(string)
  } 

  if reflect.TypeOf(x) == TYPEboolean { 
     return ToStringboolean(x.(bool))
  } 

  if reflect.TypeOf(x) == TYPESequence { 
     return ToStringSequence(x.(*list.List))
  } 

  if reflect.TypeOf(x) == TYPEMap { 
     return ToStringMap(x.(map[interface{}]interface{}))
  }

  return ""
}

func ToStringSequence(col *list.List) string { 
  res := "Sequence{"
  for e := col.Front(); e != nil; e = e.Next() { 
     res = res + ToStringOclAny(e.Value)
     if e.Next() != nil { 
       res = res + ", "
    } 
  } 
  return res + "}" 
}

func ToStringSet(col *list.List) string { 
   res := "Set{"
   for e := col.Front(); e != nil; e = e.Next() { 
      res = res + ToStringOclAny(e.Value)
      if e.Next() != nil { 
         res = res + ", "
      }
   } 
   return res + "}" 
 }

func ToStringMap(m map[interface{}]interface{}) string { 
  res := "Map{"
  for i, v := range m { 
    res = res + ToStringOclAny(i) + " |-> " + ToStringOclAny(v) + " "
  }
  return res + "}"
} 

-2

这里有一个简单的处理方法:

package main

import (
    "fat"
    "strconv"
)

type Person struct {
    firstName, lastName string
    age int
}

func (p Person) GetFullName() string {
    return p.firstName + " " + p.lastName
}

func (p Person) GetAge() int {
    return p.age
}

func (p Person) GetAgeAsString() string {
    return strconv.Itoa(p.age)
}

func main() {
    p := Person {"John", "Doe", 21}
    fmt.Println(p.GetFullName())
    fmt.Println(p.GetAgeAsString())
}

输出:

"John Doe"
"21"

这些只是随意的函数,不能满足任何特定接口。 - deamon

-8
我更喜欢类似以下的东西:
type StringRef []byte

func (s StringRef) String() string {
        return string(s[:])
}

…

// rather silly example, but ...
fmt.Printf("foo=%s\n",StringRef("bar"))

6
不需要无用的 :,只需使用 string(s)。另外,如果 b[]byte,那么更简单的方法是使用 string(b),然后使用 StringRef(b).String()。最后,你的示例是无意义的,因为 %s(不像 %v)已经将 []byte 参数作为字符串打印出来,而不需要像string(b)通常做的潜在复制。 - Dave C

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接