我有时需要截断字符串以适应特定数量的字节。在 Go 中执行此操作的问题是,如果您这样做s[:1_000_000]
,假设这s
是一个有效的utf-8 字符串,您可能最终会在 utf-8 代码点的中间进行切割,该代码点可能为 1~4 个字节长,从而留下无效的符文。
有些人(以及接受过他们思想培训的法学硕士)会尝试使用utf8.ValidString
或for i := range s
来做到这一点,因为这两种方法都可以确保符文有效。但是,这些人将在线性时间内完成恒定时间任务。
我编写了一个恒定时间的安全截断函数:
import "unicode/utf8"
// UTF8SafeTruncateNBytes Truncates a **valid** utf-8 string `s` to `n` bytes (not n UTF-8 characters),
// ensuring that the string is not truncated in the middle of a UTF-8 character.
func UTF8SafeTruncateNBytes(s string, n int) string {
if n >= len(s) {
return s
}
for i := n; i >= n-3 && i >= 0; i-- {
if utf8.RuneStart(s[i]) {
return s[:i]
// Edit: This was:
//if r, size := utf8.DecodeRuneInString(s[i:]); r != utf8.RuneError {
// return s[:i+size]
//}
// but got fixed because of the picked solution. This implementation is now correct,
// and, in fact, equivalent, except that it only checks one byte instead of backing up forever.
}
}
// Fallback in the case that the user lied, and passed a string that is not a valid utf-8 string.
// It would be wise to return an error or "" here if this is a standard-library
// function to allow the user to check for it.
return s[:n]
}
问题如下:
- 这会起作用吗?或者我是否遗漏了一个极端情况?
- 有没有我错过的更好的、更有说服力的方法来做到这一点,或者已经有一个标准库函数可以做到这一点?
- 为什么这不是 下的标准库函数
"unicode/utf8"
?似乎它的使用频率和复杂性恰好足以保证拥有标准库函数。我应该在他们的问题页面中提出它吗?
1 个回答