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

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

Get the width of Chinese strings correctly

问题

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

以下是我的分析:

len 告诉我这个:

  1. 这是一个测试 18
  2. aaaaaaaaa 10
  3. つのだ☆HIRO 16
  4. aaaaaaaaaa 10

runewidth.StringWidth 告诉我这个:

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

问题:

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

英文:

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:

  1. 这是一个测试 18
  2. aaaaaaaaa 10
  3. つのだ☆HIRO 16
  4. aaaaaaaaaa 10

runewidth.StringWidth tells me this:

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

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

Question:

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

答案1

得分: 4

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

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

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

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

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

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

  1. data := [][]string{
  2. {"这是一个测试", "|"},
  3. {"aaaaaaaaaa", "|"},
  4. {"つのだ☆HIRO", "|"},
  5. {"aaaaaaaaaa", "|"},
  6. }
  7. w := csv.NewWriter(os.Stdout)
  8. defer w.Flush()
  9. w.Comma = '\t'
  10. for _, row := range data {
  11. w.Write(row)
  12. }

这样应该按预期打印数据。不幸的是,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.

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

More basic way to count is by using for loop.

  1. count := 0
  2. for range "这是一个测试" {
  3. count++
  4. }
  5. fmt.Printf("Count=%d\n", count)
  6. // 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:

  1. data := [][]string{
  2. {"这是一个测试", "|"},
  3. {"aaaaaaaaaa", "|"},
  4. {"つのだHIRO", "|"},
  5. {"aaaaaaaaaa", "|"},
  6. }
  7. w := csv.NewWriter(os.Stdout)
  8. defer w.Flush()
  9. w.Comma = '\t'
  10. for _, row := range data {
  11. w.Write(row)
  12. }

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在评论中指出的)是一个显示问题,无法通过任何计数技巧来解决。

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

  1. mmmm, tasty
  2. iiii, tasty?

与:

  1.     mmmm, tasty<br>
  2.     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:

  1. mmmm, tasty
  2. 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

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

  1. func TestChinese(t *testing.T) {
  2. tests := []string{
  3. "这是一个测试",
  4. "aaaaaaaaa",
  5. "つのだ☆HIRO",
  6. "aaaaaaaaaa",
  7. "这是aaaaa一个测试",
  8. "这是一个つの测试",
  9. }
  10. for _, tt := range tests {
  11. fmt.Printf("%s\t%d\t%d\n", tt, len([]rune(tt)), len([]byte(tt)))
  12. }
  13. }

输出:

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

i think this is what you want

  1. func TestChinese(t *testing.T) {
  2. tests := []string{
  3. &quot;这是一个测试&quot;,
  4. &quot;aaaaaaaaa&quot;,
  5. &quot;つのだHIRO&quot;,
  6. &quot;aaaaaaaaaa&quot;,
  7. &quot;这是aaaaa一个测试&quot;,
  8. &quot;这是一个つの测试&quot;,
  9. }
  10. for _, tt := range tests {
  11. fmt.Printf(&quot;%s\t%d\t%d\n&quot;, tt, len([]rune(tt)), len([]byte(tt)))
  12. }
  13. }

output:

  1. 这是一个测试 6 18
  2. aaaaaaaaa 9 9
  3. つのだ☆HIRO 8 16
  4. aaaaaaaaaa 10 10
  5. 这是aaaaa一个测试 11 23
  6. 这是一个つの测试 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:

确定