What's the difference between the `%q` and the `%#q` string formatters?

huangapple go评论87阅读模式
英文:

What's the difference between the `%q` and the `%#q` string formatters?

问题

我不明白在使用q#的含义。

fmt.Printf("%q", "\"")
fmt.Println()
fmt.Printf("%#q", "\"")

输出结果为

"\""
`"`

但是

fmt.Printf("%q", "\n")
fmt.Println()
fmt.Printf("%#q", "\n")

输出结果为

"\n"
"\n"

更多示例可以在https://cs.opensource.google/go/go/+/master:src/fmt/fmt_test.go中找到。

{"%q", "", `""`},
{"%#q", "", "``"},
{"%q", "\"", `\"`},
{"%#q", "\"", "`\"`"},
{"%q", "`", "`" + "`" + "`"},
{"%#q", "`", "`" + "`" + "`"},
{"%q", "\n", `"\n"`},
{"%#q", "\n", `"\n"`},
{"%q", `\n`, `"\\n"`},
{"%#q", `\n`, "`\\n`"},
{"%q", "abc", `"abc"`},
{"%#q", "abc", "`abc`"},
英文:

I don't get the meaning of # when used with q.

fmt.Printf("%q", "\"")
fmt.Println()
fmt.Printf("%#q", "\"")

prints

"\""
`"`

but

fmt.Printf("%q", "\n")
fmt.Println()
fmt.Printf("%#q", "\n")

prints

"\n"
"\n"

There are more examples at https://cs.opensource.google/go/go/+/master:src/fmt/fmt_test.go.

{"%q", "", `""`},
{"%#q", "", "``"},
{"%q", "\"", `"\""`},
{"%#q", "\"", "`\"`"},
{"%q", "`", `"` + "`" + `"`},
{"%#q", "`", `"` + "`" + `"`},
{"%q", "\n", `"\n"`},
{"%#q", "\n", `"\n"`},
{"%q", `\n`, `"\\n"`},
{"%#q", `\n`, "`\\n`"},
{"%q", "abc", `"abc"`},
{"%#q", "abc", "`abc`"},

答案1

得分: 5

动词%q将字符串的内容打印出来,就像在Go源代码中声明为解释字符串字面值一样,即带有转义序列并用双引号"括起来。

动词%#q也是一样的,但是作为原始字符串字面值,没有转义序列,并用反引号 括起来。

fmt包的文档中报告了以下示例:

使用%v%s格式化字符串,使用%q格式化为带引号的字符串,使用%#q格式化为带反引号的字符串。

placeholders := `foo "bar"` 	
fmt.Printf("%v %s %q %#q\n", placeholders, placeholders, placeholders, placeholders) 	
// 结果: foo "bar" foo "bar" "foo \"bar\"" `foo "bar"`

假设你想声明一个字符串变量,其内容是字符"。你该如何在Go源代码中编写它?使用解释字符串字面值,你需要对其进行转义;而使用原始字符串字面值,你只需将其括在反引号中。因此:

  • %q的输出是"\"",你可以将其复制粘贴到Go代码中作为解释字符串字面值。

  • %#q的输出是``"",你可以将其复制粘贴到Go代码中作为原始字符串字面值。

这在字符串包含应该转义的字符时非常重要。

换句话说:

func main() {
	v := "\""
    w := `"\"`
	fmt.Printf("%q\n", v)  // 打印"\",与我刚刚声明的v相同
	fmt.Printf("%#q\n", w) // 打印"`",与我刚刚声明的w相同
}

作为复制粘贴用例的副作用,任何一个格式动词的实际应用是检查字符串变量的运行时值是否包含不可见字符,如果你在Go源代码中使用它,你必须以某种方式输入这些字符。

还有一个值得一提的特殊情况,也可以在fmt包的文档中找到:

#备用格式:[...] 对于%q,如果strconv.CanBackquote返回true,则打印原始(带反引号)字符串;[...]

并跳转到strconv.CanBackquote的文档:

CanBackquote报告字符串s是否可以不变地表示为单行反引号字符串,且除了制表符之外没有控制字符。

值得注意的是,对于包含反引号字符本身或换行符\n的字符串,CanBackquote返回false,因为在后一种情况下,该字符串无法表示为单行字符串。(另请参阅https://stackoverflow.com/questions/7933460/how-do-you-write-multiline-strings-in-go)

因此,在这些情况下,标志#将没有效果,%#q将打印与%q相同的结果。

func main() {
    v := "\n"
    w := `
`
    fmt.Printf("%q\n", v)  // 打印"\n",与我刚刚声明的v相同
    fmt.Printf("%#q\n", w) // 也打印"\n",因为CanBackquote返回false
}
英文:

The verb %q prints the contents of a string as it appears if you declared it as an interpreted string literal in your Go source code, i.e. with escape sequences and enclosed in double quotes ".

The verb %#q does the same, but as a raw string literal, with no escape sequences and enclosed in backquotes <code>`</code>.

The fmt package documentation reports this example:

> Strings are formatted with %v and %s as-is, with %q as quoted strings, and %#q as backquoted strings.
>
> go
&gt; placeholders := `foo &quot;bar&quot;`
&gt; fmt.Printf(&quot;%v %s %q %#q\n&quot;, placeholders, placeholders, placeholders, placeholders)
&gt; // Result: foo &quot;bar&quot; foo &quot;bar&quot; &quot;foo \&quot;bar\&quot;&quot; `foo &quot;bar&quot;`
&gt;

Let's say you want to declare a string variable whose content is the character &quot;. How would you write that in your Go source? With an interpreted string literal, you would have to escape it, and with a raw string literal you would just enclose it in backquotes. Therefore:

  • The output of %q is &quot;\&quot;&quot; that you can copy-paste in Go code as an interpreted string literal.

  • The output of %#q is `&quot;` that you can copy-paste in Go code as a raw string literal.

This is mostly relevant when your string contains characters that should be escaped.

In other words:

func main() {
	v := &quot;\&quot;&quot;
    w := `&quot;`
	fmt.Printf(&quot;%q\n&quot;, v)  // prints &quot;\&quot;&quot;, same as I just declared v
	fmt.Printf(&quot;%#q\n&quot;, w) // prints `&quot;`, same as I just declared w
}

As a side effect of this copy-paste use case, a practical application of either format verb is to inspect the run-time value of string variables for invisible characters, which you would have to type out somehow if you used it in Go source code.

There's one special case worth mentioning, which is also found in the fmt package docs:

> # alternate format: [...] for %q, print a raw (backquoted) string if strconv.CanBackquote returns true; [...]

And jump to the docs of strconv.CanBackquote:

> CanBackquote reports whether the string s can be represented unchanged as a single-line backquoted string without control characters other than tab.

Notably, CanBackquote returns false for strings that contain the backquote character itself or newline character \n, as in the latter case the string can not be represented as a single-line. (See also https://stackoverflow.com/questions/7933460/how-do-you-write-multiline-strings-in-go)

So in these cases the flag # would have no effect and %#q would print the same as %q.

func main() {
    v := &quot;\n&quot;
    w := `
`
    fmt.Printf(&quot;%q\n&quot;, v)  // prints &quot;\n&quot;, same as I just declared v
    fmt.Printf(&quot;%#q\n&quot;, w) // prints &quot;\n&quot; too, because CanBackquote returns false
}

答案2

得分: 0

%q 将值作为带引号的字符串打印出来。
%#q 将值作为带引号的字符串以 Go 语法打印出来。

一般来说,在 % 和类型之间添加 # 会导致输出为 Go 代码,语法上是正确的,可以直接复制粘贴到你的程序中。

以下示例演示了 # 的效果:

s := []string{"foo", "bar", "baz"}
fmt.Printf("%v\n", s)
fmt.Printf("%#v\n", s)
fmt.Printf("%q\n", s)
fmt.Printf("%#q\n", s)

输出结果为:

[foo bar baz]
[]string{"foo", "bar", "baz"}
["foo" "bar" "baz"]
[`foo` `bar` `baz`]

可以参考在线演示

英文:

%q prints the value as a quoted string.
%#q prints the value as a quoted string as Go syntax.

In general, adding # between % and the type causes output to be in Go code - syntactically correct that you could copy-paste into your program.

This example illustrates the effect of the #:

s := []string{&quot;foo&quot;, &quot;bar&quot;, &quot;baz&quot;}
fmt.Printf(&quot;%v\n&quot;, s)
fmt.Printf(&quot;%#v\n&quot;, s)
fmt.Printf(&quot;%q\n&quot;, s)
fmt.Printf(&quot;%#q\n&quot;, s)

Produces:

[foo bar baz]
[]string{&quot;foo&quot;, &quot;bar&quot;, &quot;baz&quot;}
[&quot;foo&quot; &quot;bar&quot; &quot;baz&quot;]
[`foo` `bar` `baz`]

See live demo.

huangapple
  • 本文由 发表于 2022年7月7日 20:08:30
  • 转载请务必保留本文链接:https://go.coder-hub.com/72897561.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定