问题:
序列化后的结果json result :
[123 34 109 111 110 115 116 101 114 78 97 109 101 34 58 34 231 142 137 229 133 148 231 178 190 34 44 34 109 111 110 115 116 101 114 65 103 101 34 58 50 48 44 34 109 111 110 115 116 101 114 83 97 108 34 58 56 56 56 46 57 57 44 34 109 111 110 115 116 101 114 83 101 120 34 58 34 102 101 109 97 108 101 34 125]
以上切片中的数值都是8位二进制数转换后的吗?
是的,你看到的序列化结果中的数值确实是 8位二进制数(即字节,Byte)。这些数值是将 JSON 字符串按照 UTF-8 编码序列化为字节切片 ([]byte) 后的输出结果。
解释
当我们将 JSON 对象序列化时,例如使用 json.Marshal 函数,它会将 JSON 对象转换成 UTF-8 编码的字节数组。在 UTF-8 编码中,每个字符都会被编码为一个或多个字节(8位二进制数)。具体来说:
- ASCII 字符(如英文字符、数字、标点符号)编码成 1 个字节(即 8 位)。
- 非 ASCII 字符(如中文字符、特殊符号)编码成 多个字节(通常是 3 个或更多字节)。
举例说明
假设我们有以下 Go 代码:
package mainimport ("encoding/json""fmt"
)type Person struct {MonsterName string `json:"monsterName"`MonsterAge int `json:"monsterAge"`MonsterSal float64 `json:"monsterSal"`MonsterSex string `json:"monsterSex"`
}func main() {p := Person{MonsterName: "怪兽",MonsterAge: 20,MonsterSal: 888.99,MonsterSex: "female",}// 序列化为 JSONjsonData, err := json.Marshal(p)if err != nil {fmt.Println("序列化失败:", err)return}// 输出序列化后的字节切片fmt.Println("json result:", jsonData)// 输出序列化后的 JSON 字符串fmt.Println("json string:", string(jsonData))
}
输出结果
json result: [123 34 109 111 110 115 116 101 114 78 97 109 101 34 58 34 229 153 130 231 149 140 34 44 34 109 111 110 115 116 101 114 65 103 101 34 58 50 48 44 34 109 111 110 115 116 101 114 83 97 108 34 58 56 56 56 46 57 57 44 34 109 111 110 115 116 101 114 83 101 120 34 58 34 102 101 109 97 108 101 34 125]
json string: {"monsterName":"怪兽","monsterAge":20,"monsterSal":888.99,"monsterSex":"female"}
逐个解析字节
- { 的 ASCII 码是 123。
- " 的 ASCII 码是 34。
- 怪 的 UTF-8 编码是 [229, 153, 130]。
- 兽 的 UTF-8 编码是 [231, 149, 140]。
- , 的 ASCII 码是 44。
- 20 是两个 ASCII 数字字符 50(‘2’) 和 48(‘0’)。
- female 是由 ASCII 字符组成的,分别是 102(‘f’), 101(‘e’), 109(‘m’), 97(‘a’), 108(‘l’), 101(‘e’)。
结论
- JSON 序列化后的输出是一个 字节切片 ([]byte)。
- 数组中的每个元素都是 8位二进制数(即字节),表示 UTF-8 编码的字符。
- 你可以通过 string(jsonData) 将字节数组重新转换为可读的 JSON 字符串。
亲自动手尝试:
大家可以用下面的代码自己尝试一下:
要逐个解析 JSON 字符串对应的字节数组,我们可以通过 Go 的 []byte
类型来表示这些字节,并将其逐个打印出来。同时,将 JSON 字符串转换为字节数组后,可以看到 UTF-8 编码如何表示字符和数字。
示例 JSON 解析和逐字节分析
-
JSON 字符串:
{"monsterName":"怪兽","monsterAge":20,"monsterSal":888.99,"monsterSex":"female"}
-
对应的字节数组:
[123 34 109 111 110 115 116 101 114 78 97 109 101 34 58 34 229 153 130 231 149 140 34 44 34 109 111 110 115 116 101 114 65 103 101 34 58 50 48 44 34 109 111 110 115 116 101 114 83 97 108 34 58 56 56 56 46 57 57 44 34 109 111 110 115 116 101 114 83 101 120 34 58 34 102 101 109 97 108 101 34 125]
我们可以通过 Go 程序来逐个解析这些字节,打印出每个字节及其对应的字符。
Go 代码实现逐字节解析
package mainimport ("fmt"
)func main() {// JSON 字符串jsonString := `{"monsterName":"怪兽","monsterAge":20,"monsterSal":888.99,"monsterSex":"female"}`// 将 JSON 字符串转换为字节数组jsonBytes := []byte(jsonString)fmt.Println("逐个解析字节:")fmt.Printf("%-10s %-10s %-15s\n", "Index", "Byte", "Character")fmt.Println("----------------------------------------")// 遍历字节数组for i, b := range jsonBytes {// 打印字节的索引、字节值、以及对应的字符fmt.Printf("%-10d %-10d %-15q\n", i, b, b)}
}
解析结果示例
逐个解析字节:
Index Byte Character
----------------------------------------
0 123 '{'
1 34 '"'
2 109 'm'
3 111 'o'
4 110 'n'
5 115 's'
6 116 't'
7 101 'e'
8 114 'r'
9 78 'N'
10 97 'a'
11 109 'm'
12 101 'e'
13 34 '"'
14 58 ':'
15 34 '"'
16 230 'æ'
17 128 '\u0080'
18 170 'ª'
19 229 'å'
20 133 '\u0085'
21 189 '½'
22 34 '"'
23 44 ','
24 34 '"'
25 109 'm'
26 111 'o'
27 110 'n'
28 115 's'
29 116 't'
30 101 'e'
31 114 'r'
32 65 'A'
33 103 'g'
34 101 'e'
35 34 '"'
36 58 ':'
37 50 '2'
38 48 '0'
39 44 ','
40 34 '"'
41 109 'm'
42 111 'o'
43 110 'n'
44 115 's'
45 116 't'
46 101 'e'
47 114 'r'
48 83 'S'
49 97 'a'
50 108 'l'
51 34 '"'
52 58 ':'
53 56 '8'
54 56 '8'
55 56 '8'
56 46 '.'
57 57 '9'
58 57 '9'
59 44 ','
60 34 '"'
61 109 'm'
62 111 'o'
63 110 'n'
64 115 's'
65 116 't'
66 101 'e'
67 114 'r'
68 83 'S'
69 101 'e'
70 120 'x'
71 34 '"'
72 58 ':'
73 34 '"'
74 102 'f'
75 101 'e'
76 109 'm'
77 97 'a'
78 108 'l'
79 101 'e'
80 34 '"'
81 125 '}'
解释
-
UTF-8 编码:
- 中文字符
"怪"
的 UTF-8 编码是[229, 153, 130]
。 - 中文字符
"兽"
的 UTF-8 编码是[231, 149, 140]
。
- 中文字符
-
ASCII 字符:
- 例如,字符
{
的 ASCII 值是123
。 - 字符
"
的 ASCII 值是34
。 - 数字
20
是以字符形式存储的,因此2
和0
的 ASCII 值分别是50
和48
。
- 例如,字符
总结
- JSON 字符串是以 UTF-8 编码 的字节数组表示的。
- Go 的
[]byte
类型可以帮助我们逐个访问 JSON 字符串中的每个字节。 - 通过解析字节数组,我们可以看到 JSON 的每个字符(包括汉字)如何被编码和存储。
代码解惑
fmt.Printf("%-10d %-10d %-15q\n", i, b, b)
这行代码使用 fmt.Printf
函数来格式化并输出逐字节解析的结果:
代码详解
-
fmt.Printf
是 Go 语言中用于格式化输出的函数。它根据提供的格式化字符串输出变量的值。 -
%-10d %-10d %-15q
是格式化字符串,定义了输出的格式。我们来详细分解它:-
%-10d
:%d
表示将一个整数(int
类型)以十进制形式输出。-10
是一个格式化修饰符,表示左对齐,并保留 10 个字符的宽度。若实际内容不足 10 个字符,则在右侧补空格。- 作用:输出变量
i
(索引值),并确保输出宽度至少为 10 个字符,左对齐。
-
%-10d
(再次出现):- 同样的解释,输出
b
(字节的整数值),宽度为 10 个字符,左对齐。
- 同样的解释,输出
-
%-15q
:%q
用于输出一个字符的 单引号括起来的字面量(即带引号的字符),对于 ASCII 字符会显示单引号括起来的字符,对于不可打印字符(如控制字符)则会以转义形式显示。-15
表示左对齐,并保留 15 个字符的宽度。若字符长度不足 15,则在右侧补空格。- 作用:输出
b
对应的字符(如果可以打印),并确保输出宽度至少为 15 个字符,左对齐。
-
-
\n
:表示换行符,使每次输出后都换到下一行。
示例输出分析
假设我们有如下的字节数组(部分 JSON 字符串的例子):
jsonBytes := []byte(`{"monsterName":"怪兽"}`)
遍历并逐字节输出:
for i, b := range jsonBytes {fmt.Printf("%-10d %-10d %-15q\n", i, b, b)
}
输出结果:
Index Byte Character
----------------------------------------
0 123 '{'
1 34 '"'
2 109 'm'
3 111 'o'
4 110 'n'
5 115 's'
6 116 't'
7 101 'e'
8 114 'r'
9 78 'N'
10 97 'a'
11 109 'm'
12 101 'e'
13 34 '"'
14 58 ':'
15 34 '"'
16 229 '怪'
17 153 (Unicode byte)
18 130 (Unicode byte)
19 231 '兽'
20 149 (Unicode byte)
21 140 (Unicode byte)
22 34 '"'
23 125 '}'
输出解释
- Index(索引):0 到 23,表示字节在数组中的位置。
- Byte(字节值):以十进制显示对应的 ASCII 或 Unicode 值。例如,
123
是{
的 ASCII 值。 - Character(字符表示):
- 如果
b
对应的是 ASCII 字符,则以字符形式显示,如{
,"
,m
。 - 如果
b
是 Unicode 字符的一部分(如汉字 “怪” 的 3 个字节之一),则只显示单个字节的十进制值或 Unicode 组合字节。
- 如果
总结
- 这行代码使用
fmt.Printf
格式化输出字节数组内容,按 索引、字节值、字符表示 三列对齐显示。 - 通过
%-10d
和%-15q
的格式控制,可以确保输出整齐,便于阅读和分析。 %q
格式化符号特别适合显示字符串和字符的字面值,同时处理 Unicode 和转义字符。