clipperhouse/displaywidth
GitHub: clipperhouse/displaywidth
高性能 Go 库,用于精确测量字符串、UTF-8 字节和 rune 在等宽终端中的显示列宽,支持 emoji、东亚字符和控制序列。
Stars: 17 | Forks: 2
# displaywidth
一个高性能的 Go 包,用于测量字符串、UTF-8 字节和 rune 的等宽显示宽度。
[](https://pkg.go.dev/github.com/clipperhouse/displaywidth)
[](https://github.com/clipperhouse/displaywidth/actions/workflows/gotest.yml)
[](https://github.com/clipperhouse/displaywidth/actions/workflows/gofuzz.yml)
## 安装
```
go get github.com/clipperhouse/displaywidth
```
## 用法
```
package main
import (
"fmt"
"github.com/clipperhouse/displaywidth"
)
func main() {
width := displaywidth.String("Hello, 世界!")
fmt.Println(width)
width = displaywidth.Bytes([]byte("🌍"))
fmt.Println(width)
width = displaywidth.Rune('🌍')
fmt.Println(width)
}
```
在大多数情况下,你应该使用 `String` 或 `Bytes` 方法。它们会计算字符串或 byte 切片中字形簇宽度的总和。
### 遍历字形
如果你需要单个的字形:
```
import (
"fmt"
"github.com/clipperhouse/displaywidth"
)
func main() {
g := displaywidth.StringGraphemes("Hello, 世界!")
for g.Next() {
width := g.Width()
value := g.Value()
// do something with the width or value
}
}
```
### 选项
创建你需要的选项,然后使用 options 结构体上的方法。
```
var myOptions = displaywidth.Options{
EastAsianWidth: true,
ControlSequences: true,
}
width := myOptions.String("Hello, 世界!")
```
#### ControlSequences
`ControlSequences` 指定在计算显示宽度时是否忽略 ECMA-48 转义序列。当为 `false`(默认)时,ANSI 转义序列被视为一系列字符。当为 `true` 时,它们被视为一个零宽度的单个单元。
#### ControlSequences8Bit
`ControlSequences8Bit` 指定在计算显示宽度时是否忽略 8 位 ECMA-48 转义序列。当为 `false`(默认)时,它们被视为一系列字符。当为 `true` 时,它们被视为一个零宽度的单个单元。
注意:该选项会被 `Truncate` 方法忽略,因为字符串拼接可能会导致非预期的 UTF-8 语义。
#### EastAsianWidth
`EastAsianWidth` 定义了如何处理[东亚歧义字符](https://www.unicode.org/reports/tr11/#Ambiguous)。
当为 `false`(默认)时,东亚歧义字符的宽度被视为 1。
当为 `true` 时,它们的宽度被视为 2。
你可能希望根据环境变量或 locale 来配置此项。
例如,`go-runewidth` 就是在[包初始化期间](https://github.com/mattn/go-runewidth/blob/master/runewidth.go#L26C1-L45C2)进行此设置的。`displaywidth` 不会自动执行此操作,我们更倾向于将其留给你来决定。
## 技术标准与兼容性
该包实现了 Unicode 东亚宽度标准
([UAX #11](https://www.unicode.org/reports/tr11/tr11-43.html)),并处理了
[版本选择器](https://en.wikipedia.org/wiki/Variation_Selectors_(Unicode_block)),
以及[区域指示符对](https://en.wikipedia.org/wiki/Regional_indicator_symbol)
(旗帜)。我们实现了 [Unicode TR51](https://www.unicode.org/reports/tr51/tr51-27.html)
以支持 emojis。我们正在持续关注
[新兴标准](https://www.jeffquast.com/post/state-of-terminal-emulation-2025/)。
对于控制序列,我们针对 7 位和 8 位控制序列实现了 [ECMA-48](https://ecma-international.org/publications-and-standards/standards/ecma-48/) 标准。
`clipperhouse/displaywidth`、`mattn/go-runewidth` 和 `rivo/uniseg`
在处理大多数真实世界的文本时会给出相同的输出。更多详细信息请参阅
[兼容性分析](comparison/COMPATIBILITY_ANALYSIS.md)。
## 无效的 UTF-8
该包不验证 UTF-8。如果你传入无效的 UTF-8,结果将是
未定义的。我们对无效的 UTF-8 进行了模糊测试,以确保不会发生 panic 或
无限循环。
`ControlSequences8Bit` 选项意味着我们将分割有效的 8 位
控制序列,这些序列通常 _不是_ 有效的 UTF-8。8 位控制字节
恰好也是 UTF-8 的后续字节。请谨慎使用。
## 前期工作
[mattn/go-runewidth](https://github.com/mattn/go-runewidth)
[rivo/uniseg](https://github.com/rivo/uniseg)
[x/text/width](https://pkg.go.dev/golang.org/x/text/width)
[x/text/internal/triegen](https://pkg.go.dev/golang.org/x/text/internal/triegen)
## 基准测试
```
cd comparison
go test -bench=. -benchmem
```
```
goos: darwin
goarch: arm64
pkg: github.com/clipperhouse/displaywidth/comparison
cpu: Apple M2
BenchmarkString_Mixed/clipperhouse/displaywidth-8 6085 ns/op 277.23 MB/s 0 B/op 0 allocs/op
BenchmarkString_Mixed/mattn/go-runewidth-8 9970 ns/op 169.21 MB/s 0 B/op 0 allocs/op
BenchmarkString_Mixed/rivo/uniseg-8 19060 ns/op 88.51 MB/s 0 B/op 0 allocs/op
BenchmarkString_EastAsian/clipperhouse/displaywidth-8 6118 ns/op 275.76 MB/s 0 B/op 0 allocs/op
BenchmarkString_EastAsian/mattn/go-runewidth-8 13917 ns/op 121.22 MB/s 0 B/op 0 allocs/op
BenchmarkString_EastAsian/rivo/uniseg-8 19263 ns/op 87.58 MB/s 0 B/op 0 allocs/op
BenchmarkString_ASCII/clipperhouse/displaywidth-8 54.54 ns/op 2347.10 MB/s 0 B/op 0 allocs/op
BenchmarkString_ASCII/mattn/go-runewidth-8 125.5 ns/op 1020.32 MB/s 0 B/op 0 allocs/op
BenchmarkString_ASCII/rivo/uniseg-8 1478 ns/op 86.62 MB/s 0 B/op 0 allocs/op
BenchmarkString_Emoji/clipperhouse/displaywidth-8 3265 ns/op 221.74 MB/s 0 B/op 0 allocs/op
BenchmarkString_Emoji/mattn/go-runewidth-8 5110 ns/op 141.69 MB/s 0 B/op 0 allocs/op
BenchmarkString_Emoji/rivo/uniseg-8 7137 ns/op 101.44 MB/s 0 B/op 0 allocs/op
BenchmarkRune_Mixed/clipperhouse/displaywidth-8 3517 ns/op 479.72 MB/s 0 B/op 0 allocs/op
BenchmarkRune_Mixed/mattn/go-runewidth-8 4746 ns/op 355.48 MB/s 0 B/op 0 allocs/op
BenchmarkRune_EastAsian/clipperhouse/displaywidth-8 3454 ns/op 488.36 MB/s 0 B/op 0 allocs/op
BenchmarkRune_EastAsian/mattn/go-runewidth-8 11432 ns/op 147.56 MB/s 0 B/op 0 allocs/op
BenchmarkRune_ASCII/clipperhouse/displaywidth-8 255.5 ns/op 500.88 MB/s 0 B/op 0 allocs/op
BenchmarkRune_ASCII/mattn/go-runewidth-8 264.7 ns/op 483.48 MB/s 0 B/op 0 allocs/op
BenchmarkRune_Emoji/clipperhouse/displaywidth-8 1320 ns/op 548.44 MB/s 0 B/op 0 allocs/op
BenchmarkRune_Emoji/mattn/go-runewidth-8 2286 ns/op 316.72 MB/s 0 B/op 0 allocs/op
BenchmarkTruncateWithTail/clipperhouse/displaywidth-8 2495 ns/op 70.94 MB/s 192 B/op 14 allocs/op
BenchmarkTruncateWithTail/mattn/go-runewidth-8 4569 ns/op 38.74 MB/s 192 B/op 14 allocs/op
BenchmarkTruncateWithoutTail/clipperhouse/displaywidth-8 2456 ns/op 93.25 MB/s 0 B/op 0 allocs/op
BenchmarkTruncateWithoutTail/mattn/go-runewidth-8 5182 ns/op 44.19 MB/s 0 B/op 0 allocs/op
```
这里有一些关于[如何让 Unicode 操作变快](https://clipperhouse.com/go-unicode/)的说明。
标签:EVTX分析, 日志审计