Golang中如何获取Unicode字符串的"可见"长度
Golang中如何获取Unicode字符串的"可见"长度 有没有一种简单的方法可以获取字符串的可见长度?我的程序返回以下字符串:
e[0;97;44;1m per e[0;34;104me[0me[0me[0;97;104;1m 2021-11-15 13:06 e[0;94;107me[0me[0me[0;94;107;1m ~/code/goprompt e[0;97;104me[0me[0me[0;97;104;1m go 1.17 e[0;94;44me[0me[0me[0;97;44;1m master|~3 e[0;34me[0me[0m
这个字符串超过200字节,但其中大部分字符是不可打印的,比如颜色代码等。终端中的实际输出是这样的:

我感兴趣的是这个提示符在终端中的长度,如果我数得没错的话,大约是67个字符。我尝试了几种不同的方法:
fmt.Println(len(result)) // 返回 229
fmt.Println(len([]rune(result))) // 返回 219
fmt.Println(utf8.RuneCountInString(result)) // 返回 219
我基本上想要一个 utf8.**Visible**RuneCountInString(result) 这样的函数。有人知道这样的函数吗?
更多关于Golang中如何获取Unicode字符串的"可见"长度的实战教程也可以访问 https://www.itying.com/category-94-b0.html
啊,我明白你的意思了。那我就手动解决它,通过在应用着色之前计算长度。听起来可行……谢谢!
更多关于Golang中如何获取Unicode字符串的"可见"长度的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
这些终端转义序列是 Unix shell 特有的(而且很可能只针对特定的 shell,如 bash 或 zsh)。它们存在于 Unicode 时代之前,因此像 RuneCountInString 这样的 Unicode 函数在这里并不适用。
你需要一个自定义的长度函数,该函数能够识别这些 shell 转义序列以及它们在终端中的视觉表示大小。不确定是否存在这样的现成函数。
或许确实存在一些可用的长度函数。在 pkg.go.dev 上搜索 escape sequence terminal 会得到一些结果,其中包括
text package - github.com/MichaelMure/go-term-text - pkg.go.dev
该包有一个 Len() 函数,在计算字符数时会忽略转义序列。
在Golang中,处理包含ANSI转义码的字符串时,确实需要特殊处理来获取可见字符长度。你可以使用正则表达式来过滤掉这些控制序列:
package main
import (
"fmt"
"regexp"
"unicode/utf8"
)
func visibleRuneCount(s string) int {
// 匹配ANSI转义序列的正则表达式
ansiEscapeRegex := regexp.MustCompile(`\x1b\[[0-9;]*[a-zA-Z]`)
// 移除所有ANSI转义序列
cleanString := ansiEscapeRegex.ReplaceAllString(s, "")
// 计算清理后字符串的rune数量
return utf8.RuneCountInString(cleanString)
}
func main() {
str := `e[0;97;44;1m per e[0;34;104me[0me[0me[0;97;104;1m 2021-11-15 13:06 e[0;94;107me[0me[0me[0;94;107;1m ~/code/goprompt e[0;97;104me[0me[0me[0;97;104;1m go 1.17 e[0;94;44me[0me[0me[0;97;44;1m master|~3 e[0;34me[0me[0m`
// 修复字符串中的转义字符(实际应该是\x1b而不是"e")
fixedStr := ""
for i := 0; i < len(str); i++ {
if str[i] == 'e' && i+1 < len(str) && str[i+1] == '[' {
fixedStr += "\x1b["
i++ // 跳过'['
} else {
fixedStr += string(str[i])
}
}
visibleLen := visibleRuneCount(fixedStr)
fmt.Printf("可见字符长度: %d\n", visibleLen) // 应该返回约67
}
如果你需要更精确的处理,特别是对于包含Unicode组合字符的字符串,可以使用golang.org/x/text/width包:
package main
import (
"fmt"
"regexp"
"unicode/utf8"
"golang.org/x/text/width"
)
func visibleDisplayWidth(s string) int {
// 移除ANSI转义序列
ansiEscapeRegex := regexp.MustCompile(`\x1b\[[0-9;]*[a-zA-Z]`)
cleanString := ansiEscapeRegex.ReplaceAllString(s, "")
// 计算显示宽度(考虑全角/半角字符)
displayWidth := 0
for _, r := range cleanString {
switch width.LookupRune(r).Kind() {
case width.EastAsianWide, width.EastAsianFullwidth:
displayWidth += 2
default:
displayWidth += 1
}
}
return displayWidth
}
func main() {
str := `e[0;97;44;1m per e[0;34;104me[0me[0me[0;97;104;1m 2021-11-15 13:06 e[0;94;107me[0me[0me[0;94;107;1m ~/code/goprompt e[0;97;104me[0me[0me[0;97;104;1m go 1.17 e[0;94;44me[0me[0me[0;97;44;1m master|~3 e[0;34me[0me[0m`
// 修复转义字符
fixedStr := ""
for i := 0; i < len(str); i++ {
if str[i] == 'e' && i+1 < len(str) && str[i+1] == '[' {
fixedStr += "\x1b["
i++
} else {
fixedStr += string(str[i])
}
}
visibleCount := utf8.RuneCountInString(ansiEscapeRegex.ReplaceAllString(fixedStr, ""))
displayWidth := visibleDisplayWidth(fixedStr)
fmt.Printf("可见rune数量: %d\n", visibleCount)
fmt.Printf("终端显示宽度: %d\n", displayWidth)
}
第一个示例提供了基本的可见字符计数,第二个示例考虑了字符在终端中的实际显示宽度(全角字符占2个位置)。根据你的字符串内容,visibleRuneCount函数应该返回大约67个可见字符。

