英文:
Backspace character does not work in the Go playground
问题
我是你的中文翻译助手,以下是翻译好的内容:
我刚开始学习Go语言,刚刚学会了fmt.Println()
的各种用法。我在官方的代码运行环境中尝试了以下代码,但得到了一个非常出乎意料的输出结果。请解释一下我对代码的理解哪里出错了。
输入:fmt.Println("hi\b", "there!")
输出:hi� there!
预期:h there!
输入:fmt.Println("hi", '\b', "there!")
输出:hi 8 there!
预期:hithere!
... 假设符文不会附加空格
输入:fmt.Println("hi", "\bthere!")
输出:hi �there!
预期:hithere!
(注意:上面的占位符字符已被替换为U+FFFD,因为原始字符在不同环境中的呈现方式不一致。)
英文:
I am new to Go. Just learnt the various uses of fmt.Println()
. I tried the following stuff in the official playground but got a pretty unexpected output. Please explain where I have gone wrong in my understanding.
input: fmt.Println("hi\b", "there!")
output: hi� there!
expected: h there!
input: fmt.Println("hi", '\b', "there!")
output: hi 8 there!
expected: hithere!
... assuming runes are not appended with spaces
input: fmt.Println("hi", "\bthere!")
output: hi �there!
expected: hithere!
(Note: above, the placeholder character has been substituted by U+FFFD, as the original character does not render consistently between environments.)
答案1
得分: 4
您的程序输出的正是您告诉它要输出的内容。问题主要出在输出查看器上。
控制字符和序列只有在发送到兼容的虚拟控制台(或物理终端、打印机或电传打字机,但这些设备如今相当罕见)时才会产生预期的效果。Go Playground 的做法是将程序的输出原样捕获,并将其未经修改地发送到浏览器进行显示。浏览器不会解释终端控制代码(除了换行符,即使是换行符有时也只是部分解释);相反,它期望通过 HTML 标记传递格式化信息。由于退格字符没有分配的字形,浏览器通常会显示一个占位符字形,或者有时根本不显示任何内容。
如果在本地计算机上运行 Go 程序时,将其输出重定向到文本文件,然后在文本编辑器中打开该文件,您将获得类似的效果:编辑器不会解释文本文件中包含的任何转义序列;有时,它甚至会主动阻止终端显示编辑器的控制字符(如果它恰好是基于控制台的编辑器),而是用符号化的、常规的字符表示(如 ^H
)。
在中间的示例中,'\b'
字面量被评估为具有字符的 Unicode 代码点数值的整数(Go 中称为“rune”)。这在规范中有解释:
> 一个 rune 字面量表示一个 rune 常量,即标识 Unicode 代码点的整数值。一个 rune 字面量表示为一个或多个字符,用单引号括起来,如 'x'
或 '\n'
。在引号内,除了换行符和未转义的单引号之外,任何字符都可以出现。单引号字符表示字符本身的 Unicode 值,而以反斜杠开头的多字符序列以各种格式编码值。
由于 '\b'
表示的是 U+0008,传递给 fmt.Println
的是整数值 8。该函数然后将整数作为十进制表示打印出来,而不是将其解释为字符代码。
英文:
Your program outputs exactly what you told it to. The problem is mostly with your output viewer.
Control characters and sequences only have their expected effect when sent to a compatible virtual console (or a physical terminal, or a printer or teletypewriter; but the latter are pretty rare these days). What the Go playground does is capture the output of your program as-is and send it unmodified to the browser to display. The browser does not interpret terminal control codes (other than the newline character, and even that only sometimes); instead, it expects formatting to be conveyed via HTML markup. Since the backspace character does not have an assigned glyph, browsers will usually display a placeholder glyph instead, or sometimes nothing at all.
You would get a similar effect if, when running your Go program on your local machine, you redirected its output into a text file and then opened the file in a text editor: the editor will not interpret any escape sequence contained in the text file; sometimes it will even actively prevent control characters from being interpreted by the terminal displaying the editor (if it happens to be console-based editor), by substituting a symbolic, conventional representation of the character like ^H
.
In the middle example, the '\b'
literal evaluates to an integer with the value of the character’s Unicode code point number (what Go terms a ‘rune’). This is explained in the specification:
> A rune literal represents a rune constant, an integer value identifying a Unicode code point. A rune literal is expressed as one or more characters enclosed in single quotes, as in 'x'
or '\n'
. Within the quotes, any character may appear except newline and unescaped single quote. A single quoted character represents the Unicode value of the character itself, while multi-character sequences beginning with a backslash encode values in various formats.
Since '\b'
represents U+0008, what is passed to fmt.Println
is the integer value 8. The function then prints the integer as its decimal representation, instead of interpreting it as a character code.
答案2
得分: 1
首先要检查的是你的终端,'\b' 是终端相关的,检查一下运行你的程序的终端是否将其处理为“将光标向后移动一个字符”(大多数类Unix系统会这样做,我不清楚Windows),在我的终端(st)上,你给出的第一个和第三个示例的输出与你的期望完全一致。
这里你的假设与 fmt 包的行为不符:
对于每个类似 Printf 的函数,还有一个不带格式的 Print 函数,它等同于对每个操作数使用 %v。另一个变体 Println 在操作数之间插入空格并追加一个换行符。
Fmt 将 rune 的 %v
格式化为 %d
,而不是 %c
,所以 '\b' 被格式化为 "8"(ASCII 值为 56),而不是 '\b'(ASCII 值为 8)。此外,如果 rune 处于两个参数之间,它们之间会有一个空格。
对于这个输入,Println 的处理过程如下:
打印字符串 "hi"
打印空格
格式化数字 8,然后打印字符串 "8"
打印空格
打印字符串 "there!"
要调试类似渲染不可见字符的问题,我建议你使用 encoding/hex
包,例如:
package main
import (
"encoding/hex"
"fmt"
"os"
)
func main() {
d := hex.Dumper(os.Stdout)
defer d.Close()
fmt.Fprintln(d, "hi", '\b', "there!")
}
输出结果:00000000 68 69 20 38 20 74 68 65 72 65 21 0a |hi 8 there!.|
Playground: https://go.dev/play/p/F-I2mdh43K7
英文:
First thing to check out is your terminal, '\b' is terminal dependent, check if the terminal running your program handles that as "move cursor one character back" (most unixes-like will, i don't know about Windows), your first and third given example works exactly how your expectation is on my terminal (st).
> input: fmt.Println("hi", '\b', "there!")
>
> output: hi 8 there!
>
> expected: hithere!... assuming runes are not appended with spaces
>
Here your assumption is not what package fmt does:
> For each Printf-like function, there is also a Print function that takes no format and is equivalent to saying %v for every operand. Another variant Println inserts blanks between operands and appends a newline.
Fmt handles %v
for rune as %d
, not %c
, so '\b' is formatted as "8" (ascii value 56), not the '\b' (ascii value 8). Also runes will have a space if they are between two arguments.
What Println does for this input is:
print string "hi"
print space
format number 8 then print string "8"
print space
print string "there!"
To debug problems like rendering invisible characters, I suggest you to use encoding/hex
package, For example:
package main
import (
"encoding/hex"
"fmt"
"os"
)
func main() {
d := hex.Dumper(os.Stdout)
defer d.Close()
fmt.Fprintln(d, "hi", '\b', "there!")
}
Output: 00000000 68 69 20 38 20 74 68 65 72 65 21 0a |hi 8 there!.|
Playground: https://go.dev/play/p/F-I2mdh43K7
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论