go `fmt: "%%" placeholder 接受不正确的索引值

sc4hvdpw  于 4个月前  发布在  Go
关注(0)|答案(5)|浏览(110)

你正在使用的Go版本是什么?

$go1.9.2$

这个问题在最新的版本中是否重现?

是的

你正在使用的操作系统和处理器架构是什么?

macOS Sierra 10.12.6, darwin/amd64

你做了什么?

运行以下代码:

$

package main

import (
	"fmt"
)

func main() {
	fmt.Printf("%[100]%")
	fmt.Printf("%[-100]%")
	fmt.Printf("%[not a mumber]%")
}

$

你想看到什么?

在所有情况下,都会打印出关于索引不正确的错误。

你看到了什么?

在所有情况下,都打印出了"%"。这种行为是意料之外的,至少在这里没有记录:

$https://golang.org/pkg/fmt/$

dldeef67

dldeef671#

由于文档中说明“%% 是一个字面百分号;不消耗任何值”,因此不会检查未消耗的值的索引。这可以明确地记录或通过更改fmt中的此switch语句的顺序来轻松更改:
go/src/fmt/print.go
第1084行 in 2e1f071
| | caseverb=='%': // Percent 不吸收操作数,忽略 f.wid 和 f.prec。 |
然而,更改这是向后不兼容的更改,可能会破坏之前运行良好的代码(即使是不太可能的情况)。

czq61nw1

czq61nw12#

关于“消耗无价值”的问题。

fmt.Printf("%% %d \n", 1) // consumes no value, '% 1' is printed

	fmt.Printf("%*% %d \n", 1, 2) // consumes '1' for width, '% 2' is printed
	fmt.Printf("%[100]*% %d \n", 1, 2) // still consumes '1' for width, '% 2' is printed
	fmt.Printf("%[a]*% %d \n", 1, 2) // still consumes '1' for width, '% 2' is printed

所以,如果我们使用索引来表示宽度或精度,那么值会被消耗掉,但索引仍然不会被检查。

k3bvogb1

k3bvogb13#

你的示例看起来都表现得很好。
有人可能会认为[100]应该报错,但既然它没有获取到任何参数,也可以说它是一个无操作。

u4vypkhs

u4vypkhs4#

关于宽度和精度的索引呢?值会被消耗,所以这不是一个空操作。
我发现了另一个与宽度和精度相关的有趣案例:

fmt.Printf("%[1]*% %d \n", 1, 2) // consumes values 1 and 2, prints '% 2'
fmt.Printf("%[2]*% %d \n", 1, 2) // reports error as for '%d' we have no value
fmt.Printf("%[3]*% %d \n", 1, 2) // consumes values 1 and 2, prints '% 2'
fmt.Printf("%[4]*% %d \n", 1, 2) // consumes values 1 and 2, prints '% 2'
fmt.Printf("%[5]*% %d \n", 1, 2) // consumes values 1 and 2, prints '% 2'
// ... and so on

因此,如果宽度/精度索引是正确的,%%的行为就像其他任何动词一样,并消耗相应的值。如果索引不正确(不是数字,小于1,或者大于参数数量),它会消耗第一个值。

nkkqxpd9

nkkqxpd95#

好的,这确实看起来像是一个bug,尽管很难对此感到兴奋。%[1]*% 是一个毫无意义的操作。

相关问题