登录
推荐 文章 Go 技术 课程 下载 专题 AI
首页 >  Golang >  Go教程

Go unicode/utf8.ValidString 怎么判断输入是否为合法 UTF-8

来源:17golang原创

时间:2026-09-10 03:25:58 209浏览 收藏

接口收到一段来自文件、消息队列或外部表单的文本时,最容易犯的错是把 string 当成“已经是合法 UTF-8”。Go 的字符串可以保存任意字节,真正需要判断的是这些字节能否完整解释成合法的 UTF-8 编码。最直接的入口是 utf8.ValidString(s):返回 true 才接受为合法 UTF-8,返回 false 就拒绝、记录或转入清洗流程。

官方文档:https://pkg.go.dev/unicode/utf8

要点速览
  • ValidString 检查整个字符串,不负责告诉你坏字节的位置。
  • RuneError 既可能是合法字符,也可能是解码失败后的替代结果,不能单独作为判定条件。
  • 需要定位问题时,按字节偏移调用 DecodeRuneInString,不要用字符下标切字符串。

先用 ValidString 把输入边界守住

下面这个入口适合放在 HTTP 请求、文件导入或消息消费的边界。示例故意构造了一段包含 0xff 的字符串:它仍然是合法的 Go string,但不是合法 UTF-8。

package main

import (
    "fmt"
    "unicode/utf8"
)

func acceptText(s string) error {
    // string 可以保存任意字节,所以先做整段 UTF-8 校验。
    if !utf8.ValidString(s) {
        return fmt.Errorf("输入不是合法 UTF-8")
    }
    // 校验通过后,后续的按 rune 处理才有稳定前提。
    return nil
}

func main() {
    valid := "订单-α"
    invalid := string([]byte{'o', 'k', 0xff})

    fmt.Println(acceptText(valid))
    fmt.Println(acceptText(invalid))
}
Go utf8.ValidString 在输入边界区分合法 UTF-8 字符串与非法字节字符串的静态关系图
图1:把 string 输入送入 utf8.ValidString,合法 UTF-8 才进入后续的 rune 处理边界。

这里的校验是“全量判断”:空字符串会返回 true,ASCII 也属于合法 UTF-8;只要任意一个字节序列违反 UTF-8 编码规则,整体结果就是 false。如果输入来自不可信边界,尽早拒绝通常比让非法字节流进入模板、日志或下游序列化器更容易维护。

不要把 RuneError 和非法 UTF-8 混为一谈

utf8.RuneError 是 Unicode 替换字符,它本身可以合法地编码为 UTF-8。另一方面,解码非法字节时,DecodeRuneInString 也会返回它,并把宽度报告为 1。因此“遍历结果里出现了 RuneError”只能说明需要进一步判断,不能直接等价于原始输入非法。

还有一个经常混淆的边界:len(s) 统计字节数,utf8.RuneCountInString(s) 统计 rune 数。中文、表情和组合字符都会让两者不同;校验 UTF-8 与计算展示长度是两个独立问题。

目标函数返回值含义
整段是否合法utf8.ValidString全部合法为 true
统计原始占用len(s)字节数
统计解码单元utf8.RuneCountInStringrune 数,非法编码按错误 rune 处理

需要报错位置时按字节偏移定位

ValidString 只给出布尔值。文件导入或消息审计需要知道坏数据从哪里开始时,可以逐段解码,并维护字节偏移。重点是每次用返回的 size 前进,而不是按 rune 下标截取。

package main

import (
    "fmt"
    "unicode/utf8"
)

func firstInvalidByte(s string) (int, bool) {
    // offset 始终是原字符串的字节偏移,便于日志和原始文件定位。
    for offset := 0; offset 
Go DecodeRuneInString 按字节偏移定位非法 UTF-8 的函数、RuneError 与原始字符串关系图
图2:DecodeRuneInString 连接原始字符串、字节偏移和 RuneError,帮助定位第一处异常。

上面的判断只适合“找到第一处非法编码”。如果业务要自动修复,应先明确策略:直接拒绝、丢弃坏片段,还是用替换字符重新编码。不要在没有记录原始偏移的情况下静默清洗,否则后续很难追查是哪一条输入污染了数据。

把校验放在哪里更稳妥

建议在系统边界做一次统一验收:HTTP body 解码后、文件读取后、消息反序列化后都可以成为校验点。内部函数如果已经约定接收合法 UTF-8,就不必每一层重复扫描;但跨边界转成 []byte、拼接外部字段或读取二进制协议时,要重新审视这个不变量。

  • 只想判断能否接受:调用 utf8.ValidString
  • 想得到坏位置:按字节偏移组合 DecodeRuneInString
  • 想限制显示长度:先校验,再单独计算 rune 或用户感知字符数。

相关问题

空字符串算合法 UTF-8 吗?

算。它不包含任何非法编码,因此 utf8.ValidString("") 返回 true;是否允许空值是业务校验,不是 UTF-8 校验。

把字符串转成 []rune 能修复非法字节吗?

不能把它当作无损修复。转换过程可能把非法序列替换成 RuneError,原始字节边界就丢失了。需要保留证据时先记录或拒绝原始输入。

为什么 len 和 RuneCountInString 不一样?

len 统计字节,rune 按 UTF-8 编码单元统计。多字节字符天然会让字节数大于 rune 数,二者服务于不同的限制条件。

声明:本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>