从io.Reader读取UTF-8编码的字符串

时间:2013-11-25 14:07:44

标签: encoding utf-8 io go

我正在使用TCP套接字编写一个小型通信协议。 我能够读取和写入基本数据类型,如整数,但我不知道如何从一个字节片段读取UTF-8编码的字符串。

协议客户端是用Java编写的,服务器是Go。

根据我的描述:GO符文长度为32位,UTF-8字符长度为1到4个字节,因此无法简单地将字节切片强制转换为字符串。

我想知道如何读取和写入这个UTF-8流。

注意 我有足够的字节缓冲区长度来读取字符串。

1 个答案:

答案 0 :(得分:4)

首先是一些理论:

  • Go中的rune表示Unicode代码点 - 分配给Unicode中特定字符的数字。它是uint32的别名。
  • UTF-8是Unicode 编码 - 的格式,表示存储和传输方式的Unicode代码点。 UTF-8可能使用1到4个字节来编码单个代码点。

这如何映射Go数据类型:

  • []bytestring都存储了一系列字节(Go中的byteuint8的别名。

    主要区别在于字符串是不可变的,所以尽管你可以

    b := make([]byte, 2)
    b[0] = byte('a')
    b[1] = byte('z')
    

    你不能

    var s string
    s[0] = byte('a')
    

    后者的事实甚至无法明确设置字符串长度(如虚构的s := make(string, 10))。

  • 虽然Go中的字符串包含抽象字节(您可以自由存储它们,比如使用Windows-1252编码的字符),但某些Go语句和类型转换字符串解释为以UTF编码-8,特别是:
    • 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,并且它甚至都不能保证完全正常工作。