108

我正在编写一些代码,我需要它来捕获参数并传递它们fmt.Println
(我想要它的默认行为,编写由空格分隔并后跟换行符的参数)。但是它需要[]interface {}flag.Args()返回一个[]string.
这是代码示例:

package main

import (
    "fmt"
    "flag"
)

func main() {
    flag.Parse()
    fmt.Println(flag.Args()...)
}

这将返回以下错误:

./example.go:10: cannot use args (type []string) as type []interface {} in function argument

这是一个错误吗?不应该fmt.Println采取任何阵列?顺便说一句,我也尝试过这样做:

var args = []interface{}(flag.Args())

但我收到以下错误:

cannot convert flag.Args() (type []string) to type []interface {}

有没有“去”的方法来解决这个问题?

4

7 回答 7

130

这不是错误。fmt.Println()需要一个[]interface{}类型。这意味着,它必须是一个interface{}值切片,而不是“任何切片”。为了转换切片,您需要循环并复制每个元素。

old := flag.Args()
new := make([]interface{}, len(old))
for i, v := range old {
    new[i] = v
}
fmt.Println(new...)

您不能使用任何切片的原因是 a[]string和 a之间的转换[]interface{}需要更改内存布局并在 O(n) 时间内发生。将类型转换为interface{}需要 O(1) 时间。如果他们使这个 for 循环变得不必要,编译器仍然需要插入它。

于 2012-10-20T16:42:51.620 回答
13

在这种情况下,不需要类型转换。只需将flag.Args()值传递给fmt.Println.


问题:

无法将 [] 字符串转换为 [] 接口 {}

我正在编写一些代码,我需要它来捕获参数并通过 fmt.Println 传递它们(我想要它的默认行为,编写由空格分隔并后跟换行符的参数)。

这是代码示例:

package main

import (
    "fmt"
    "flag"
)

func main() {
    flag.Parse()
    fmt.Println(flag.Args()...)
}

包标志

import "flag"

函数参数

func Args() []string

Args返回非标志命令行参数。


打包文件

import "fmt"

函数打印

func Println(a ...interface{}) (n int, err error)

Println对其操作数使用默认格式并写入标准输出。操作数之间总是添加空格并附加换行符。它返回写入的字节数和遇到的任何写入错误。


在这种情况下,不需要类型转换。只需将flag.Args()值传递给fmt.Println,它使用反射将值解释为 type []string。包reflect实现运行时反射,允许程序操作任意类型的对象。例如,

args.go

package main

import (
    "flag"
    "fmt"
)

func main() {
    flag.Parse()
    fmt.Println(flag.Args())
}

输出:

$ go build args.go
$ ./args arg0 arg1
[arg0 arg1]
$ 
于 2018-01-31T05:27:32.140 回答
11

如果它只是您要打印的一段字符串,则可以通过加入来避免转换并获得完全相同的输出:

package main

import (
    "fmt"
    "flag"
    "strings"
)

func main() {
    flag.Parse()
    s := strings.Join(flag.Args(), " ")
    fmt.Println(s)
}
于 2015-12-19T16:06:35.007 回答
0

在 Go 中,函数只能接受函数定义中参数列表中指定类型的参数。可变参数语言功能使这有点复杂,但它遵循明确定义的规则。

的函数签名fmt.Println是:

func Println(a ...interface{}) (n int, err error)

根据语言规范

函数签名中的最终传入参数可能具有以 ... 为前缀的类型。具有这种参数的函数称为可变参数,并且可以使用该参数的零个或多个参数来调用。

这意味着您可以传递类型Println参数的列表interface{}。由于所有类型都实现了空接口,因此您可以传递任何类型的参数列表,这就是您能够调用Println(1, "one", true)的方式,例如,没有错误。请参阅语言规范的“将参数传递给...参数”部分:

传递的值是一个 []T 类型的新切片,带有一个新的底层数组,其连续元素是实际参数,所有这些都必须可分配给 T。

给您带来麻烦的部分就在规范之后:

如果最后一个参数可分配给切片类型 []T,则它可以作为 ...T 参数的值不变地传递,如果参数后跟 ...。在这种情况下,不会创建新切片。

flag.Args()是类型[]string。由于TinPrintlninterface{}[]T[]interface{}。所以问题归结为字符串切片值是否可以分配给接口切片类型的变量。您可以通过尝试分配轻松地在您的 go 代码中测试它,例如:

s := []string{}
var i []interface{}
i = s

如果您尝试这样的分配,编译器将输出以下错误消息:

cannot use s (type []string) as type []interface {} in assignment

这就是为什么你不能在字符串切片之后使用省略号作为fmt.Println. 这不是错误,它按预期工作。

您仍然可以使用多种方式进行打印flag.Args()Println例如

fmt.Println(flag.Args())

[elem0 elem1 ...](根据fmt 包文档,它将输出为)

或者

fmt.Println(strings.Join(flag.Args(), ` `)

例如,使用带有字符串分隔符的字符串包中的 Join 函数(将输出字符串切片元素,每个元素由一个空格分隔)。

于 2018-02-04T09:26:25.700 回答
0

另一种选择是只迭代切片:

package main
import "flag"

func main() {
   flag.Parse()
   for _, each := range flag.Args() {
      println(each)
   }
}
于 2021-04-07T15:27:02.757 回答
-1

我认为可以使用反射,但我不知道这是否是一个好的解决方案

package main

import (
    "fmt"
    "reflect"
    "strings"
)

type User struct {
    Name string
    Age  byte
}

func main() {
    flag.Parse()
    fmt.Println(String(flag.Args()))
    fmt.Println(String([]string{"hello", "world"}))
    fmt.Println(String([]int{1, 2, 3, 4, 5, 6}))
    u1, u2 := User{Name: "John", Age: 30},
        User{Name: "Not John", Age: 20}
    fmt.Println(String([]User{u1, u2}))
}

func String(v interface{}) string {
    val := reflect.ValueOf(v)
    if val.Kind() == reflect.Array || val.Kind() == reflect.Slice {
        l := val.Len()
        if l == 0 {
            return ""
        }
        if l == 1 {
            return fmt.Sprint(val.Index(0))
        }
        sb := strings.Builder{}
        sb.Grow(l * 4)
        sb.WriteString(fmt.Sprint(val.Index(0)))
        for i := 1; i < l; i++ {
            sb.WriteString(",")
            sb.WriteString(fmt.Sprint(val.Index(i)))
        }
        return sb.String()
    }

    return fmt.Sprintln(v)
}

输出:

$ go run .\main.go arg1 arg2
arg1,arg2
hello,world
1,2,3,4,5,6
{John 30},{Not John 20}
于 2019-03-05T19:06:08.997 回答
-2

fmt.Println接受可变参数

func Println(a ...interface{}) (n int, err error)

flag.Args()无需转换即可打印[]interface{}

func main() {
    flag.Parse()
    fmt.Println(flag.Args())
}
于 2018-01-30T18:02:01.453 回答