前言
regexpUTF-8UTF-8regexpgo version go1.14.2 darwin/amd64regexp匹配字节序列
我们将匹配网络流量所遇到的问题,进行抽象和最小化复现,如下:
\xffUTF-8编码
regexp1.ASCII
在计算机的世界,字符最终都由二进制来存储,标准 ASCII 编码使用一个字节(低7位),所以只能表示 127 个字符,而不同国家有不同的字符,所以建立了自己的编码规范,当不同国家相互通信的时候,由于编码规范不同,就会造成乱码问题。
2.Unicode
为了解决乱码问题,提出了 Unicode 字符集,为所有字符分配一个独一无二的编码,随着 Unicode 的发展,不断添加新的字符,目前最新的 Unicode 采用 UCS-4(Unicode-32) 标准,也就是使用 4 字节(32位) 来进行编码,理论上可以涵盖所有字符。
但是 Unicode 只是字符集,没有考虑计算机中的使用和存储问题,比如:
ASCII(A)=65 / UCS-2(A)=00652.由于 Unicode 编码高字节可能为 0,C 语言字符串串函数将出现 00 截断问题
3.从全世界来看原来 ASCII 的字符串使用得最多,而换成 Unicode 过后,这些 ASCII 字符的存储都将额外占用字节(存储0x00)
3.UTF-8
后来提出了 UTF-8 编码方案,UTF-8 是在互联网上使用最广的一种 Unicode 的实现方式;UTF-8 是一种变长的编码方式,编码规则如下:
1. 对于单字节的符号,字节的第一位设为 0,后面 7 位为这个符号的 Unicode 的码点, 兼容 ASCII
2. 对于需要 n 字节来表示的符号(n > 1),第一个字节的前 n 位都设为 1,第 n+1 位设置为 0;后面字节的前两位一律设为 10,剩下的的二进制位则用于存储这个符号的 Unicode 码点(从低位开始)。
编码规则如下:
你1.根据 UTF-8 编码规则,当需要编码的符号超过 1 个字节时,其第一个字节前面的 1 的个数表示该字符占用了几个字节。 2.UTF-8 是自同步码(Self-synchronizing_code),在 UTF-8 编码规则中,任意字符的第一个字节必然以 0 / 110 / 1110 / 11110 开头,UTF-8 选择 10 作为后续字节的前缀码,以此进行区分。自同步码可以便于程序寻找字符边界,快速跳过字符,当遇到错误字符时,可以跳过该字符完成后续字符的解析,这样不会造成乱码扩散的问题(GB2312存在该问题)
byte/rune/string
byte/rune/stringbyteuint8stringstring[]byteruneint32rune类型转换
byte(uint8)rune(int32)stringstringbytebytebytestringintstring()encoderune()stringruneUTF-8编码 <=> Unicode编码string => runestringtoslicerune()for-rangedecoderune()RuneError = \uFFFDrune => stringbyteintstring()测试如下:
regexp处理表达式
regexp\t \a 或者 16进制regexpregexpregexp1.编译
rune\xff0x00ff (rune)regexpstrings.BuilderWriteRune()utf8.EncodeRune()\xff => \xc3\xbf2.匹配
当匹配时,首先使用前缀字符串匹配,这里使用常规的字符串匹配。UTF-8 可以正常进行匹配,但当我们的字符串中包含非 UTF-8 字符就会出现问题,原因正则表达式中的前缀字符串已经被强制 UTF-8 编码了,示例如下:
tryBacktrace()step()string/byte/runestring/byteutf8.DecodeRune*()runeruneruneutf8.DecodeRune*()RuneError=0xfffdregexpregexp解决方法
在了解以上知识点过后,就很容易解决问题了:表达式可以使用任意字符,待匹配字符串在匹配前手动转换为合法的 UTF-8 字符串。
regexpregexprune实现测试如下:
总结
regexpstring/byteregexpregexpregexpReferences:
https://www.ruanyifeng.com/blog/2007/10/ascii_unicode_and_utf-8.html https://blog.golang.org/strings https://zh.wikipedia.org/wiki/UTF-8 https://stackoverflow.com/questions/53009692/utf-8-encoding-why-prefix-10 https://en.wikipedia.org/wiki/Self-synchronizing_code https://www.zhihu.com/question/19817672 https://pkg.go.dev/regexp/syntax https://github.com/golang/go/issues/38006 https://github.com/golang/go/tree/master/src/regexp https://golang.org/src/runtime/string.go https://github.com/golang/go/blob/master/src/builtin/builtin.go https://github.com/golang/gofrontend/blob/master/go/statements.cc#L6841 https://github.com/golang/go/blob/master/src/cmd/compile/internal/walk/range.go#L220 https://github.com/golang/go/blob/master/src/runtime/string.go#L244 https://github.com/golang/go/blob/master/src/runtime/string.go#L178