Às vezes, preciso me encontrar truncando uma string para caber em um número específico de bytes. O problema de fazer isso em Go é que se você fizer isso s[:1_000_000]
, dado que s
é uma string utf-8 válida , você pode acabar cortando bem no meio de um ponto de código utf-8 que pode ter de 1 a 4 bytes de comprimento, deixando você com uma runa inválida.
Algumas pessoas (e os LLMs treinados em suas ideias) tentariam usar utf8.ValidString
, ou for i := range s
fazer isso, pois ambos garantiriam uma runa válida. No entanto, essas pessoas estariam fazendo uma tarefa de tempo constante em tempo linear.
Eu escrevi uma função de truncamento seguro de tempo constante:
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]
}
As perguntas são as seguintes:
- Isso vai funcionar ou há algum caso especial que deixei passar?
- Existe uma maneira melhor e mais eloquente de fazer isso que eu esqueci, ou uma função de biblioteca padrão que já faz isso?
- Por que isso não é uma função de biblioteca padrão em
"unicode/utf8"
? Parece que é o nível certo de frequência de uso e complexidade para garantir ter uma função de biblioteca padrão. Devo propor isso na página de problemas deles ?
1 respostas