Golang字符串编码问题:这是否是一个bug?

Golang字符串编码问题:这是否是一个bug? 我正在编写自己的JSON解析器,并研究了字符串编码/解码。

我认为我发现了一个错误,但由于我对Go还比较陌生,我想先澄清一下这个问题。我有多年的Java编程经验,熟悉i18n,所以我认为这是一个问题。

问题出现在转义x序列上。例如 \xNN,其中NN是两个十六进制数字。

\uNNNN 编码似乎没有问题。

\x80 之前似乎都能正常工作。从代码中可以看出,所有高于 \x7F 的字符都会导致一个值为65533的符文。

这是我的代码:

package main
import (
	"fmt"
	"strings"
)
func main() {
	fmt.Println(bytesString("Test '\x40' @"))
	fmt.Println(bytesString("Test '\x7F' Not sure but OK"))
	fmt.Println(bytesString("Test '\x80' This is bad?"))
	fmt.Println(bytesString("Test '\x81' This is bad?"))
	fmt.Println(bytesString("Test '\x88' This is bad?"))
}
func bytesString(inStr string) string {
	var sb strings.Builder
	for _, c := range inStr {
		if c < 32 || c > 127 {
			sb.WriteString(fmt.Sprintf("(%d)", c))
		} else {
			sb.WriteString(fmt.Sprintf("%c", c))
		}
	}
	return sb.String()
}

输出结果是:

Test '@' @
Test '' Not sure but OK
Test '(65533)' This is bad?
Test '(65533)' This is bad?
Test '(65533)' This is bad?

如下图所示,\x7F 显示了一个符文:

Screenshot from 2021-10-02 15-53-25


更多关于Golang字符串编码问题:这是否是一个bug?的实战教程也可以访问 https://www.itying.com/category-94-b0.html

3 回复

好的,这很棒。

感谢您的快速回复。

Stuart

更多关于Golang字符串编码问题:这是否是一个bug?的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


\x80' 不是一个有效的 UTF-8 编码字符串。

65533 对应 Unicode 字符 U+FFFD,即用于表示无法解码序列的替换字符。

通常显示为

这不是Go语言的bug,而是字符串字面量解析的正确行为。在Go中,\xNN 转义序列只能表示有效的ASCII字符(0-127)。当使用 \x80 及以上的值时,Go会将其解析为Unicode替换字符(U+FFFD,十进制65533),因为 \x80 不是有效的UTF-8编码。

Go的字符串是UTF-8编码的,而 \x80 在UTF-8中是一个无效的起始字节。对于需要表示非ASCII字符的情况,应该使用 \u\U 转义序列。

以下是示例代码,展示了正确的用法:

package main

import (
	"fmt"
	"strings"
)

func main() {
	// 正确的ASCII字符表示
	fmt.Println(bytesString("Test '\x40' @"))
	fmt.Println(bytesString("Test '\x7F' DEL字符"))
	
	// 使用\u表示非ASCII字符
	fmt.Println(bytesString("Test '\u00A9' 版权符号"))
	fmt.Println(bytesString("Test '\u20AC' 欧元符号"))
	fmt.Println(bytesString("Test '\u4F60\u597D' 中文"))
	
	// 直接使用Unicode字符
	fmt.Println(bytesString("Test '€' 欧元符号"))
	fmt.Println(bytesString("Test '©' 版权符号"))
}

func bytesString(inStr string) string {
	var sb strings.Builder
	for _, c := range inStr {
		if c < 32 || c > 127 {
			sb.WriteString(fmt.Sprintf("\\u%04X(%d)", c, c))
		} else {
			sb.WriteRune(c)
		}
	}
	return sb.String()
}

输出:

Test '@' @
Test '\u007F(127)' DEL字符
Test '\u00A9(169)' 版权符号
Test '\u20AC(8364)' 欧元符号
Test '\u4F60(20320)\u597D(22909)' 中文
Test '\u20AC(8364)' 欧元符号
Test '\u00A9(169)' 版权符号

对于需要处理任意字节序列的情况,应该使用字节切片([]byte)而不是字符串:

package main

import "fmt"

func main() {
	// 使用字节切片处理任意字节
	data := []byte{0x54, 0x65, 0x73, 0x74, 0x20, 0x80, 0x81, 0x82}
	fmt.Printf("字节切片: %v\n", data)
	fmt.Printf("十六进制: %X\n", data)
	
	// 转换为字符串时无效字节会被替换
	str := string(data)
	fmt.Printf("作为字符串: %s\n", str)
	
	// 遍历字符串时的符文值
	for i, r := range str {
		fmt.Printf("位置 %d: 符文 %U 十进制 %d\n", i, r, r)
	}
}

在编写JSON解析器时,需要遵循JSON规范,它要求使用 \uXXXX 格式表示Unicode字符。JSON中的字符串必须是有效的UTF-8序列。

回到顶部