unicode/utf16 包详解
概述
unicode/utf16 包实现了 UTF-16 序列的编码和解码功能。
主要用途:
- UTF-16 编码和解码
- rune 与 UTF-16 码元序列转换
- 代理对(surrogate pair)处理
- 与 Windows API 交互
- 处理 Java/.NET 字符串
核心概念:
- UTF-16:16 位 Unicode 编码格式
- rune:Go 中的 Unicode 码点类型(int32 的别名)
- 代理对:用于编码 U+10000 以上码点的两个 16 位码元
- 高代理(High Surrogate):U+D800 到 U+DBFF
- 低代理(Low Surrogate):U+DC00 到 U+DFFF
Go 版本要求:所有 Go 版本
包导入
import "unicode/utf16"
函数详解(按 A-Z 分层归类)
A
AppendRune
func AppendRune(a []uint16, r rune) []uint16
作用:将 Unicode 码点 r 的 UTF-16 编码追加到 p 的末尾并返回扩展后的缓冲区
参数说明:
a:目标 uint16 切片r:要编码的 Unicode 码点
返回值:
- 扩展后的 uint16 切片
说明:
- 如果 rune 不是有效的 Unicode 码点,会追加 U+FFFD 的编码
- 对于 U+10000 以上的码点,会使用代理对(2 个 uint16)
- 对于不需要代理对的码点,只使用 1 个 uint16
示例:
// 追加基本多文种平面(BMP)内的字符
a := []uint16{}
a = utf16.AppendRune(a, 'A')
a = utf16.AppendRune(a, '中')
fmt.Printf("%v\n", a)
// 输出:[65 20013]
// 追加需要代理对的字符(emoji)
a = []uint16{}
a = utf16.AppendRune(a, '😀') // U+1F600
fmt.Printf("%v\n", a)
// 输出:[55357 56832] (代理对)
// 追加无效码点
a = utf16.AppendRune(a, unicode/utf8.MaxRune+1)
fmt.Printf("%v\n", a)
// 输出:[55357 56832 65533] (65533 是 U+FFFD)
// 批量追加
runes := []rune{'H', 'e', 'l', 'l', 'o'}
var buf []uint16
for _, r := range runes {
buf = utf16.AppendRune(buf, r)
}
fmt.Printf("%v\n", buf)
// 输出:[72 101 108 108 111]
D
Decode
func Decode(s []uint16) []rune
作用:返回 UTF-16 编码 s 表示的 Unicode 码点序列
参数说明:
s:UTF-16 编码的 uint16 切片
返回值:
- 解码后的 rune 切片
说明:
- 自动处理代理对
- 无效的代理对会被替换为 U+FFFD
示例:
// 解码 BMP 字符
s := []uint16{72, 101, 108, 108, 111} // "Hello"
runes := utf16.Decode(s)
fmt.Println(string(runes))
// 输出:Hello
// 解码包含代理对的字符串
s = []uint16{20013, 25991} // "中文"
runes = utf16.Decode(s)
fmt.Println(string(runes))
// 输出:中文
// 解码 emoji(代理对)
s = []uint16{55357, 56832} // 😀 的代理对
runes = utf16.Decode(s)
fmt.Printf("%c\n", runes[0])
// 输出:😀
// 解码混合内容
s = []uint16{72, 105, 20013, 55357, 56832} // "Hi 中😀"
runes = utf16.Decode(s)
fmt.Println(string(runes))
// 输出:Hi 中😀
// 无效代理对的处理
s = []uint16{55357} // 只有高代理,不完整
runes = utf16.Decode(s)
fmt.Printf("%c\n", runes[0])
// 输出: (U+FFFD)
DecodeRune
func DecodeRune(r1, r2 rune) rune
作用:返回代理对的 UTF-16 解码
参数说明:
r1:高代理(high surrogate)r2:低代理(low surrogate)
返回值:
- 解码后的 Unicode 码点
- 如果代理对无效,返回 U+FFFD
说明:
- r1 应该在 U+D800 到 U+DBFF 范围内
- r2 应该在 U+DC00 到 U+DFFF 范围内
- 如果不符合上述范围,返回 U+FFFD
示例:
// 解码有效的代理对
r1 := rune(0xD83D) // 高代理
r2 := rune(0xDE00) // 低代理
decoded := utf16.DecodeRune(r1, r2)
fmt.Printf("%c (U+%04X)\n", decoded, decoded)
// 输出:😀 (U+1F600)
// 解码无效的代理对
r1 = 0xD800 // 高代理
r2 = 0xD800 // 应该是低代理,但这是高代理
decoded = utf16.DecodeRune(r1, r2)
fmt.Printf("%c (U+%04X)\n", decoded, decoded)
// 输出: (U+FFFD)
// 单个有效值(不是代理对)
decoded = utf16.DecodeRune('A', 'B')
fmt.Printf("%c\n", decoded)
// 输出: (U+FFFD)
E
Encode
func Encode(s []rune) []uint16
作用:返回 Unicode 码点序列 s 的 UTF-16 编码
参数说明:
s:rune 切片
返回值:
- UTF-16 编码的 uint16 切片
说明:
- 对于 U+10000 以上的码点,会使用代理对
- 对于 BMP 内的码点,直接编码为单个 uint16
示例:
// 编码 BMP 字符
runes := []rune{'H', 'e', 'l', 'l', 'o'}
encoded := utf16.Encode(runes)
fmt.Printf("%v\n", encoded)
// 输出:[72 101 108 108 111]
// 编码中文字符
runes = []rune{'中', '文'}
encoded = utf16.Encode(runes)
fmt.Printf("%v\n", encoded)
// 输出:[20013 25991]
// 编码 emoji(需要代理对)
runes = []rune{'😀', '😃', '😄'}
encoded = utf16.Encode(runes)
fmt.Printf("%v\n", encoded)
fmt.Printf("Length: %d\n", len(encoded))
// 输出:[55357 56832 55357 56835 55357 56836]
// Length: 6 (每个 emoji 使用 2 个 uint16)
// 编码混合内容
runes = []rune{'A', '中', '😀'}
encoded = utf16.Encode(runes)
fmt.Printf("%v\n", encoded)
// 输出:[65 20013 55357 56832]
EncodeRune
func EncodeRune(r rune) (r1, r2 rune)
作用:返回给定 rune 的 UTF-16 代理对 r1, r2
参数说明:
r:要编码的 Unicode 码点
返回值:
r1:高代理(high surrogate)r2:低代理(low surrogate)- 如果 rune 不是有效的 Unicode 码点或不需要编码,返回 (U+FFFD, U+FFFD)
说明:
- 只有 U+10000 到 U+10FFFF 范围内的码点需要代理对
- BMP 内的码点(U+0000 到 U+FFFF)不需要代理对
- 无效的码点(超出范围或代理对本身)返回 (U+FFFD, U+FFFD)
示例:
// 编码需要代理对的字符
r1, r2 := utf16.EncodeRune('😀') // U+1F600
fmt.Printf("r1=U+%04X, r2=U+%04X\n", r1, r2)
// 输出:r1=U+D83D, r2=U+DE00
// 验证解码
decoded := utf16.DecodeRune(r1, r2)
fmt.Printf("Decoded: %c\n", decoded)
// 输出:Decoded: 😀
// 编码 BMP 字符(不需要代理对)
r1, r2 = utf16.EncodeRune('A')
fmt.Printf("r1=U+%04X, r2=U+%04X\n", r1, r2)
// 输出:r1=U+FFFD, r2=U+FFFD (表示不需要代理对)
// 编码无效码点
r1, r2 = utf16.EncodeRune(unicode/utf8.MaxRune + 1)
fmt.Printf("r1=U+%04X, r2=U+%04X\n", r1, r2)
// 输出:r1=U+FFFD, r2=U+FFFD
// 编码代理对本身(无效)
r1, r2 = utf16.EncodeRune(0xD800)
fmt.Printf("r1=U+%04X, r2=U+%04X\n", r1, r2)
// 输出:r1=U+FFFD, r2=U+FFFD
I
IsSurrogate
func IsSurrogate(r rune) bool
作用:报告指定的 Unicode 码点是否可以出现在代理对中
参数说明:
r:要检查的 Unicode 码点
返回值:
- 如果是高代理或低代理返回 true,否则返回 false
说明:
- 高代理范围:U+D800 到 U+DBFF
- 低代理范围:U+DC00 到 U+DFFF
- 代理对本身不能单独出现在有效的 Unicode 文本中
示例:
// 检查高代理
fmt.Println(utf16.IsSurrogate(0xD800))
// 输出:true
fmt.Println(utf16.IsSurrogate(0xDBFF))
// 输出:true
// 检查低代理
fmt.Println(utf16.IsSurrogate(0xDC00))
// 输出:true
fmt.Println(utf16.IsSurrogate(0xDFFF))
// 输出:true
// 检查非代理字符
fmt.Println(utf16.IsSurrogate('A'))
// 输出:false
fmt.Println(utf16.IsSurrogate('中'))
// 输出:false
fmt.Println(utf16.IsSurrogate('😀'))
// 输出:false
// 检查边界值
fmt.Println(utf16.IsSurrogate(0xD7FF)) // 代理对之前
// 输出:false
fmt.Println(utf16.IsSurrogate(0xE000)) // 代理对之后
// 输出:false
R
RuneLen
func RuneLen(r rune) int
作用:返回 rune 的 UTF-16 编码中的 16 位码元数量
参数说明:
r:要计算的 Unicode 码点
返回值:
- 码元数量(1 或 2)
- 如果 rune 不是有效的 UTF-16 编码值,返回 -1
说明:
- BMP 内的码点(U+0000 到 U+FFFF):1 个码元
- 辅助平面码点(U+10000 到 U+10FFFF):2 个码元(代理对)
- 无效的码点(超出范围或代理对本身):-1
示例:
// ASCII 字符
fmt.Println(utf16.RuneLen('A'))
// 输出:1
// 中文字符(BMP 内)
fmt.Println(utf16.RuneLen('中'))
// 输出:1
// emoji(需要代理对)
fmt.Println(utf16.RuneLen('😀'))
// 输出:2
// 辅助平面字符
fmt.Println(utf16.RuneLen('𐀀')) // U+10000
// 输出:2
// 无效码点
fmt.Println(utf16.RuneLen(unicode/utf8.MaxRune + 1))
// 输出:-1
// 代理对本身
fmt.Println(utf16.RuneLen(0xD800))
// 输出:-1
类型详解
unicode/utf16 包不导出任何类型,所有功能通过函数提供。
典型示例
1. 基本编码和解码
package main
import (
"fmt"
"unicode/utf16"
)
func main() {
// 原始字符串
str := "Hello, 世界!😀"
// 转换为 rune 切片
runes := []rune(str)
// 编码为 UTF-16
encoded := utf16.Encode(runes)
fmt.Printf("UTF-16: %v\n", encoded)
// 解码回 rune
decoded := utf16.Decode(encoded)
fmt.Printf("Decoded: %s\n", string(decoded))
// 验证
fmt.Printf("Match: %v\n", string(runes) == string(decoded))
}
2. 处理代理对
package main
import (
"fmt"
"unicode/utf16"
)
func main() {
// emoji 需要代理对
emoji := '😀' // U+1F600
// 编码为代理对
r1, r2 := utf16.EncodeRune(emoji)
fmt.Printf("High surrogate: U+%04X\n", r1)
fmt.Printf("Low surrogate: U+%04X\n", r2)
// 解码代理对
decoded := utf16.DecodeRune(r1, r2)
fmt.Printf("Decoded: %c (U+%04X)\n", decoded, decoded)
// 检查长度
fmt.Printf("UTF-16 length: %d\n", utf16.RuneLen(emoji))
// 验证代理
fmt.Printf("Is r1 surrogate: %v\n", utf16.IsSurrogate(r1))
fmt.Printf("Is r2 surrogate: %v\n", utf16.IsSurrogate(r2))
}
3. 使用 AppendRune 构建 UTF-16 序列
package main
import (
"fmt"
"unicode/utf16"
)
func main() {
// 构建 UTF-16 序列
var buf []uint16
text := []rune("Hello 世界 😀")
for _, r := range text {
buf = utf16.AppendRune(buf, r)
}
fmt.Printf("UTF-16: %v\n", buf)
fmt.Printf("Length: %d uint16s\n", len(buf))
// 解码验证
decoded := utf16.Decode(buf)
fmt.Printf("Decoded: %s\n", string(decoded))
}
4. 计算 UTF-16 长度
package main
import (
"fmt"
"unicode/utf16"
)
func main() {
texts := []string{
"Hello",
"世界",
"😀😃😄",
"Hello 世界 😀",
}
for _, text := range texts {
runes := []rune(text)
// 计算 UTF-16 长度
var utf16Len int
for _, r := range runes {
utf16Len += utf16.RuneLen(r)
}
fmt.Printf("%q:\n", text)
fmt.Printf(" Runes: %d\n", len(runes))
fmt.Printf(" UTF-16 length: %d\n", utf16Len)
fmt.Printf(" UTF-8 length: %d\n", len(text))
fmt.Println()
}
}
5. 与 Windows API 交互
package main
import (
"fmt"
"unicode/utf16"
)
// 模拟 Windows API 调用
func windowsAPIExample() {
// Go 字符串
goStr := "Hello, 世界!"
// 转换为 UTF-16(Windows 使用 UTF-16)
runes := []rune(goStr)
utf16Str := utf16.Encode(runes)
fmt.Printf("Go string: %s\n", goStr)
fmt.Printf("UTF-16: %v\n", utf16Str)
fmt.Printf("UTF-16 length: %d\n", len(utf16Str))
// 添加 NUL 终止符(Windows API 需要)
utf16Str = append(utf16Str, 0)
// 模拟从 Windows API 返回 UTF-16 字符串
returnedUTF16 := utf16Str[:len(utf16Str)-1] // 移除 NUL
// 转换回 Go 字符串
returnedRunes := utf16.Decode(returnedUTF16)
returnedStr := string(returnedRunes)
fmt.Printf("Returned: %s\n", returnedStr)
}
func main() {
windowsAPIExample()
}
6. 处理无效输入
package main
import (
"fmt"
"unicode/utf16"
)
func main() {
// 无效的代理对序列
invalid := []uint16{0xD800} // 只有高代理
decoded := utf16.Decode(invalid)
fmt.Printf("Invalid high surrogate: %c (U+%04X)\n", decoded[0], decoded[0])
// 错误的代理对顺序
invalid = []uint16{0xDC00, 0xD800} // 低代理在前
decoded = utf16.Decode(invalid)
fmt.Printf("Wrong order: %c %c\n", decoded[0], decoded[1])
// 无效的码点
fmt.Println(utf16.RuneLen(0x110000)) // 超出范围
// 输出:-1
// 代理对本身
fmt.Println(utf16.IsSurrogate(0xD800)) // 高代理
// 输出:true
fmt.Println(utf16.IsSurrogate(0xDC00)) // 低代理
// 输出:true
}
7. 比较 UTF-8 和 UTF-16
package main
import (
"fmt"
"unicode/utf8"
"unicode/utf16"
)
func main() {
texts := []rune{
'A', // ASCII
'é', // Latin-1
'中', // CJK
'😀', // Emoji
'𐀀', // Supplementary
}
fmt.Printf("%-10s %-10s %-10s %-10s\n", "Rune", "UTF-8", "UTF-16", "Name")
fmt.Println(strings.Repeat("-", 50))
for _, r := range texts {
utf8Len := utf8.RuneLen(r)
utf16Len := utf16.RuneLen(r)
fmt.Printf("%-10c %-10d %-10d %-10s\n",
r, utf8Len, utf16Len, fmt.Sprintf("U+%04X", r))
}
}
8. 字符串转换工具
package main
import (
"fmt"
"unicode/utf16"
)
// StringToUTF16 将 Go 字符串转换为 UTF-16 切片
func StringToUTF16(s string) []uint16 {
return utf16.Encode([]rune(s))
}
// UTF16ToString 将 UTF-16 切片转换为 Go 字符串
func UTF16ToString(s []uint16) string {
return string(utf16.Decode(s))
}
func main() {
original := "Hello, 世界!😀"
// 转换为 UTF-16
utf16Str := StringToUTF16(original)
fmt.Printf("UTF-16: %v\n", utf16Str)
// 转换回字符串
recovered := UTF16ToString(utf16Str)
fmt.Printf("Recovered: %s\n", recovered)
// 验证
fmt.Printf("Match: %v\n", original == recovered)
}
9. 分析文本的 UTF-16 编码
package main
import (
"fmt"
"unicode/utf16"
)
func analyzeUTF16(text string) {
runes := []rune(text)
var bmpCount, surrogatePairCount int
for _, r := range runes {
length := utf16.RuneLen(r)
if length == 1 {
bmpCount++
} else if length == 2 {
surrogatePairCount++
}
}
fmt.Printf("Text: %q\n", text)
fmt.Printf("Total runes: %d\n", len(runes))
fmt.Printf("BMP characters: %d\n", bmpCount)
fmt.Printf("Surrogate pairs: %d\n", surrogatePairCount)
fmt.Printf("UTF-16 length: %d uint16s\n",
bmpCount+surrogatePairCount*2)
fmt.Println()
}
func main() {
analyzeUTF16("Hello")
analyzeUTF16("世界")
analyzeUTF16("😀😃😄")
analyzeUTF16("Hello 世界 😀")
}
10. 高效的 UTF-16 编码
package main
import (
"fmt"
"unicode/utf16"
)
// 预分配缓冲区进行高效编码
func efficientEncode(text string) []uint16 {
runes := []rune(text)
// 估算 UTF-16 长度(大多数情况是 1:1)
estimatedLen := len(runes)
// 预分配缓冲区
buf := make([]uint16, 0, estimatedLen)
for _, r := range runes {
buf = utf16.AppendRune(buf, r)
}
return buf
}
func main() {
text := "Hello, 世界!😀"
encoded := efficientEncode(text)
fmt.Printf("Original: %s\n", text)
fmt.Printf("UTF-16: %v\n", encoded)
fmt.Printf("Length: %d uint16s\n", len(encoded))
// 解码验证
decoded := utf16.Decode(encoded)
fmt.Printf("Decoded: %s\n", string(decoded))
}
最佳实践
1. 使用 Encode/Decode 进行批量转换
// 推荐:批量转换
runes := []rune(text)
utf16Str := utf16.Encode(runes)
// 解码
decoded := utf16.Decode(utf16Str)
2. 使用 AppendRune 增量构建
// 推荐:增量构建
var buf []uint16
for _, r := range runes {
buf = utf16.AppendRune(buf, r)
}
3. 预分配缓冲区
// 预估长度并预分配
estimatedLen := len(runes)
buf := make([]uint16, 0, estimatedLen)
4. 检查代理对
// 检查是否需要代理对
if utf16.RuneLen(r) == 2 {
// 需要代理对
r1, r2 := utf16.EncodeRune(r)
}
5. 处理 NUL 终止符
// Windows API 需要 NUL 终止
utf16Str = append(utf16Str, 0)
// 移除 NUL 终止符
if len(utf16Str) > 0 && utf16Str[len(utf16Str)-1] == 0 {
utf16Str = utf16Str[:len(utf16Str)-1]
}
6. 验证代理对
// 验证高代理和低代理
if utf16.IsSurrogate(r1) && utf16.IsSurrogate(r2) {
decoded := utf16.DecodeRune(r1, r2)
}
与其他包配合
unicode/utf8 包
import (
"unicode/utf8"
"unicode/utf16"
)
// UTF-8 和 UTF-16 之间转换
func utf8ToUTF16(utf8Str string) []uint16 {
return utf16.Encode([]rune(utf8Str))
}
func utf16ToUTF8(utf16Str []uint16) string {
return string(utf16.Decode(utf16Str))
}
syscall 包(Windows)
import (
"syscall"
"unicode/utf16"
)
// Windows API 调用
func windowsExample() {
str := "Hello"
utf16Str := utf16.Encode([]rune(str))
// 传递给 Windows API
syscall.SomeWindowsAPI(&utf16Str[0])
}
strings 包
import (
"strings"
"unicode/utf16"
)
// 使用 strings.Builder
func buildUTF16(text string) []uint16 {
var builder strings.Builder
builder.Grow(len(text))
builder.WriteString(text)
return utf16.Encode([]rune(builder.String()))
}
注意事项
1. UTF-16 长度不总是等于 rune 数量
text := "😀" // 1 个 rune
runes := []rune(text)
utf16Str := utf16.Encode(runes)
fmt.Println(len(runes)) // 1
fmt.Println(len(utf16Str)) // 2 (代理对)
2. 代理对本身是无效的
// 代理对不能单独出现
fmt.Println(utf16.IsSurrogate(0xD800)) // true
fmt.Println(utf16.RuneLen(0xD800)) // -1 (无效)
3. BMP 内的字符不需要代理对
// BMP 字符(U+0000 到 U+FFFF)
fmt.Println(utf16.RuneLen('A')) // 1
fmt.Println(utf16.RuneLen('中')) // 1
// 辅助平面字符(U+10000 到 U+10FFFF)
fmt.Println(utf16.RuneLen('😀')) // 2
4. EncodeRune 对 BMP 字符返回特殊值
// BMP 字符不需要代理对
r1, r2 := utf16.EncodeRune('A')
fmt.Printf("r1=U+%04X, r2=U+%04X\n", r1, r2)
// 输出:r1=U+FFFD, r2=U+FFFD (表示不需要代理对)
5. 无效的代理对会被替换
// 不完整的代理对
invalid := []uint16{0xD800} // 只有高代理
decoded := utf16.Decode(invalid)
fmt.Printf("%c (U+%04X)\n", decoded[0], decoded[0])
// 输出: (U+FFFD)
6. 字节序问题
// UTF-16 有字节序问题(大端/小端)
// Go 的 utf16 包使用本机字节序
// 在网络传输或文件存储时可能需要添加 BOM
7. NUL 字符处理
// UTF-16 中 NUL 是 0x0000
// 与 C 字符串交互时需要注意 NUL 终止符
utf16Str := append(utf16.Encode(runes), 0) // 添加 NUL 终止
快速参考
函数速查表
| 函数 | 说明 |
|---|---|
AppendRune | 追加 UTF-16 编码到切片 |
Decode | 解码 UTF-16 为 rune 序列 |
DecodeRune | 解码代理对 |
Encode | 编码 rune 序列为 UTF-16 |
EncodeRune | 编码单个 rune 为代理对 |
IsSurrogate | 检查是否是代理 |
RuneLen | 获取 UTF-16 编码长度 |
Unicode 范围速查表
| 范围 | UTF-16 码元数 | 说明 |
|---|---|---|
| U+0000 - U+D7FF | 1 | 基本多文种平面(BMP) |
| U+D800 - U+DBFF | - | 高代理区(无效) |
| U+DC00 - U+DFFF | - | 低代理区(无效) |
| U+E000 - U+FFFF | 1 | BMP(包括私有区) |
| U+10000 - U+10FFFF | 2 | 辅助平面(需要代理对) |
代理对范围
| 类型 | 范围 | 说明 |
|---|---|---|
| 高代理 | U+D800 - U+DBFF | 代理对的高位 |
| 低代理 | U+DC00 - U+DFFF | 代理对的低位 |
常见模式
// 编码
utf16Str := utf16.Encode([]rune(text))
// 解码
text := string(utf16.Decode(utf16Str))
// 增量构建
var buf []uint16
for _, r := range runes {
buf = utf16.AppendRune(buf, r)
}
// 检查是否需要代理对
if utf16.RuneLen(r) == 2 {
// 需要代理对
}
// 编码代理对
r1, r2 := utf16.EncodeRune(r)
// 解码代理对
decoded := utf16.DecodeRune(r1, r2)
总结
unicode/utf16 包提供了完整的 UTF-16 编码支持功能:
核心功能:
- rune 与 UTF-16 码元序列的相互转换
- 代理对编码和解码
- UTF-16 长度计算
- 代理检查
主要函数:
- 编码:
Encode、EncodeRune、AppendRune - 解码:
Decode、DecodeRune - 检查:
IsSurrogate、RuneLen
使用场景:
- Windows API 交互(Windows 使用 UTF-16)
- Java/.NET 字符串处理
- 某些文件格式(如 Windows 注册表)
- 网络协议(如某些版本的 HTTP)
使用建议:
- 使用 Encode/Decode 进行批量转换
- 使用 AppendRune 增量构建
- 预分配缓冲区提高效率
- 注意代理对的处理
- 与 Windows API 交互时添加 NUL 终止符
- 理解 BMP 和辅助平面的区别
典型用法:
// 编码
utf16Str := utf16.Encode([]rune(text))
// 解码
text := string(utf16.Decode(utf16Str))
// 处理代理对
if utf16.RuneLen(r) == 2 {
r1, r2 := utf16.EncodeRune(r)
// 使用 r1, r2
}
通过 unicode/utf16 包,可以方便地处理 UTF-16 编码的文本,特别是与 Windows 系统和其他使用 UTF-16 的平台进行交互。