我正在使用TCP套接字编写一个小型通信协议。 我能够读取和写入基本数据类型,如整数,但我不知道如何从一个字节片段读取UTF-8编码的字符串。
协议客户端是用Java编写的,服务器是Go。
根据我的描述:GO符文长度为32位,UTF-8字符长度为1到4个字节,因此无法简单地将字节切片强制转换为字符串。
我想知道如何读取和写入这个UTF-8流。
注意 我有足够的字节缓冲区长度来读取字符串。
答案 0 :(得分:4)
首先是一些理论:
rune
表示Unicode代码点 - 分配给Unicode中特定字符的数字。它是uint32
的别名。这如何映射Go数据类型:
[]byte
和string
都存储了一系列字节(Go中的byte
是uint8
的别名。
主要区别在于字符串是不可变的,所以尽管你可以
b := make([]byte, 2)
b[0] = byte('a')
b[1] = byte('z')
你不能
var s string
s[0] = byte('a')
后者的事实甚至无法明确设置字符串长度(如虚构的s := make(string, 10)
)。
string
和[]rune
之间的类型转换将字符串解析为UTF-8编码的代码点序列,并生成它们的一部分。反向类型转换从符文切片中获取Unicode代码点,并生成UTF-8编码的字符串。range
循环遍历包含字符串的Unicode代码点,不是只是字节。 Go还提供string
和[]byte
之间的类型转换。现在回想一下,字符串是只读的,而字节片则不是。这意味着像
b := make([]byte, 1000)
io.ReadFull(r, b)
s := sting(b)
无论您是将切片转换为字符串还是返回,始终复制数据。这浪费了空间,但是类型安全并强制执行语义。
现在回到你手边的任务。
如果你使用相当小的字符串并且没有内存压力,只需将由io.Read()
(或其他)填充的字节切换转换为字符串。确保重用您正在使用的切片来读取数据以减轻垃圾收集器的压力 - 也就是说,不要为每个新读取分配新切片,因为您要复制读取代码关闭到字符串的数据。
最后,如果你绝对必须不复制数据(比如,你正在处理多兆字节的字符串,并且你有严格的内存要求),你可能会尝试通过以下方式来玩肮脏的技巧不安全使用内存 - here是一个如何将内存从字节切片移植到字符串的示例。请注意,如果你恢复到这样的状态,你必须非常清楚它可以自由地打破任何新版本的Go,并且它甚至都不能保证完全正常工作。