获取中文字符串的正确宽度

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

Get the width of Chinese strings correctly

问题

我想在文本“这是一个测试”周围加上边框,但我无法获得它的实际宽度。对于英文文本,它可以完美地工作。

以下是我的分析:

len 告诉我这个:

这是一个测试 18
aaaaaaaaa 10
つのだ☆HIRO 16
aaaaaaaaaa 10

runewidth.StringWidth 告诉我这个:

这是一个测试 12
aaaaaaaaa 10
つのだ☆HIRO 11
aaaaaaaaaa 10
func main() {
	fmt.Println("这是一个测试 |")
	fmt.Println("aaaaaaaaaa | 10*a")
	fmt.Println()
	fmt.Println("这是一个测试 |")
	fmt.Println("aaaaaaaaa | 9*a")
	fmt.Println()
	fmt.Println("两者都不等于中文文本。")
	fmt.Println("(竖线)行不在同一行上。")
}

问题:

如何使我的框(第一个截图)正确显示?

英文:

I want to make a border around the text 这是一个测试, but I cannot get the actual width of it. With English text, it does work perfectly.

获取中文字符串的正确宽度

Here is my analysis:

len tells me this:

这是一个测试 18
aaaaaaaaa 10
つのだ☆HIRO 16
aaaaaaaaaa 10

runewidth.StringWidth tells me this:

这是一个测试 12
aaaaaaaaa 10
つのだ☆HIRO 11
aaaaaaaaaa 10
func main() {
	fmt.Println("这是一个测试 |")
	fmt.Println("aaaaaaaaaa | 10*a")
	fmt.Println()
	fmt.Println("这是一个测试 |")
	fmt.Println("aaaaaaaaa | 9*a")
	fmt.Println()
	fmt.Println("Both are not equal to the Chinese text.")
	fmt.Println("The (pipe) lines are not under each other.")
}

获取中文字符串的正确宽度

Question:

How can I get my box (first screenshot) to appear correctly?

答案1

得分: 4

Unicode字符(如中文字符)在Golang中占用3个字节,而ASCII只占用1个字节。这是设计上的规定。

如果你想要检查Unicode字符的实际字符串大小,可以使用unicode/utf8内置包。

fmt.Printf("String: %s\nLength: %d\nRune Length: %d\n", c, len(c), utf8.RuneCountInString(c))
// String: 这是一个测试
// Length: 18
// Rune Length: 6

更基本的计数方法是使用for循环。

count := 0
for range "这是一个测试" {
	count++
}
fmt.Printf("Count=%d\n", count)
// Count=6

关于以表格形式漂亮打印中文和英文字符串,似乎没有直接的方法。即使是tabwriter在这种情况下也不起作用。一个小的解决方法是使用csv writer,如下所示:

data := [][]string{
	{"这是一个测试", "|"},
	{"aaaaaaaaaa", "|"},
	{"つのだ☆HIRO", "|"},
	{"aaaaaaaaaa", "|"},
}

w := csv.NewWriter(os.Stdout)
defer w.Flush()
w.Comma = '\t'

for _, row := range data {
	w.Write(row)
}

这样应该按预期打印数据。不幸的是,StackOverflow没有打印出与我在终端中看到的相同格式。但是Playground可以帮助我们。点击这里

注意:这适用于字符的rune大小彼此接近的字符串。对于较长的字符串,你需要更多的解决方法。

英文:

Unicode characters (like Chinese characters) in Golang take 3 bytes, while ASCII only takes 1 byte. That's by design.

If you wish to check the actual string size of unicode character, use unicode/utf8 built-in package.

fmt.Printf("String: %s\nLength: %d\nRune Length: %d\n", c, len(c), utf8.RuneCountInString(c))
// String: 这是一个测试
// Length: 18
// Rune Length: 6

More basic way to count is by using for loop.

count := 0
for range "这是一个测试" {
	count++
}
fmt.Printf("Count=%d\n", count)
// Count=6

About the pretty print of Chinese and English strings in tabular format, there seems to be no direct way. Nor the tabwriter works in this case. A small hack-around this is to use csv writer as follows:

data := [][]string{
	{"这是一个测试", "|"},
	{"aaaaaaaaaa", "|"},
	{"つのだHIRO", "|"},
	{"aaaaaaaaaa", "|"},
}

w := csv.NewWriter(os.Stdout)
defer w.Flush()
w.Comma = '\t'

for _, row := range data {
	w.Write(row)
}

This should print data as expected. Unfortunately, StackOverflow isn't printing the same format as I see in terminal. But Playground to our rescue. Click Here

Note: This works for strings with rune size close enough to one another. For lengthier strings, you'd need more work-around.

答案2

得分: 0

你的问题(正如mkopriva在评论中指出的)是一个显示问题,无法通过任何计数技巧来解决。

当我们显示变宽字体或比例字体的英文文本时,与等宽字体文本相比,我们也会遇到同样的问题。比较一下:

mmmm, tasty
iiii, tasty?

与:

    mmmm, tasty<br>
    iiii, tasty?

(假设您使用浏览器阅读此答案!)。我们不需要打印中文字符,甚至不需要使用简单的ASCII字符就能遇到这个问题!

你需要的是一个等宽的显示字体来显示中文文本,或者可能需要一些软件以表格形式排版它,如何获得这些...又是另一个完全不同的问题。

英文:

Your problem is (as mkopriva points out in comments) a display issue, not amenable to being resolved by any sort of counting trick.

We have the same problem when we display variable-pitch, or proportional, text, vs monospace text, in English. That is, compare:

mmmm, tasty
iiii, tasty?

with:

&nbsp;&nbsp;&nbsp;&nbsp;mmmm, tasty<br>
&nbsp;&nbsp;&nbsp;&nbsp;iiii, tasty?

(assuming you use a browser to read this answer!). We don't have to print Chinese characters, or even leave simple ASCII to have the problem!

What you need is a monospaced display font for your Chinese text, or perhaps some software to typeset it in tabular form, and how you get that is ... another question entirely.

答案3

得分: 0

我认为这是你想要的翻译:

func TestChinese(t *testing.T) {
	tests := []string{
		"这是一个测试",
		"aaaaaaaaa",
		"つのだ☆HIRO",
		"aaaaaaaaaa",
		"这是aaaaa一个测试",
		"这是一个つの测试",
	}
	for _, tt := range tests {
		fmt.Printf("%s\t%d\t%d\n", tt, len([]rune(tt)), len([]byte(tt)))
	}
}

输出:

这是一个测试	6	18
aaaaaaaaa	9	9
つのだ☆HIRO	8	16
aaaaaaaaaa	10	10
这是aaaaa一个测试	11	23
这是一个つの测试	8	24
英文:

i think this is what you want

func TestChinese(t *testing.T) {
	tests := []string{
		&quot;这是一个测试&quot;,
		&quot;aaaaaaaaa&quot;,
		&quot;つのだHIRO&quot;,
		&quot;aaaaaaaaaa&quot;,
		&quot;这是aaaaa一个测试&quot;,
		&quot;这是一个つの测试&quot;,
	}
	for _, tt := range tests {
		fmt.Printf(&quot;%s\t%d\t%d\n&quot;, tt, len([]rune(tt)), len([]byte(tt)))
	}
}

output:

这是一个测试	6	18
aaaaaaaaa	9	9
つのだ☆HIRO	8	16
aaaaaaaaaa	10	10
这是aaaaa一个测试	11	23
这是一个つの测试	8	24

huangapple
  • 本文由 发表于 2021年10月14日 00:32:49
  • 转载请务必保留本文链接:https://go.coder-hub.com/69559133.html
匿名

发表评论

匿名网友

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

确定