package utils import ( "fmt" "strings" "unicode/utf8" "golang.org/x/text/encoding/simplifiedchinese" ) // utf8BOM 是 UTF-8 字节顺序标记,上传的 CSV 常带该前缀。 var utf8BOM = []byte{0xEF, 0xBB, 0xBF} // replacementChar 是解码器无法映射字节时产生的替换字符。 const replacementChar = "\uFFFD" // DecodeTextToUTF8 把上传文本解码为 UTF-8 字节。 // 先剥离 UTF-8 BOM;剥离后已是合法 UTF-8 时原样返回,否则按 GBK 回退解码。 // 仅在 UTF-8 校验失败时才尝试 GBK,避免对合法 UTF-8 内容做启发式改写; // GBK 解码失败或仍残留无法映射的字节时返回错误,由调用方按任务级失败处理。 func DecodeTextToUTF8(data []byte) ([]byte, error) { trimmed := data if len(trimmed) >= len(utf8BOM) && string(trimmed[:len(utf8BOM)]) == string(utf8BOM) { trimmed = trimmed[len(utf8BOM):] } if utf8.Valid(trimmed) { return trimmed, nil } decoded, err := simplifiedchinese.GBK.NewDecoder().Bytes(trimmed) if err != nil { return nil, fmt.Errorf("GBK 解码失败: %w", err) } if !utf8.Valid(decoded) { return nil, fmt.Errorf("GBK 解码结果不是合法 UTF-8") } if strings.Contains(string(decoded), replacementChar) { return nil, fmt.Errorf("GBK 解码存在无法映射的字节") } return decoded, nil }