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

Encoding 从io.Reader读取UTF-8编码字符串,encoding,utf-8,io,go,Encoding,Utf 8,Io,Go,我正在编写一个带有TCP套接字的小型通信协议。 我能够读写基本数据类型,如整数,但我不知道如何从字节片中读取UTF-8编码的字符串 协议客户端是用Java编写的,服务器是Go 正如我读到的:GO符文是32位长的,UTF-8字符是1到4字节长的,这使得简单地将一个字节片转换成一个字符串是不可能的 我想知道如何读写这个UTF-8流 注意 我拥有读取字符串所需的字节缓冲区长度。首先是一些理论: Go中的符文表示一个Unicode码点-一个在Unicode中分配给特定字符的数字。它是uint32的别名

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

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

正如我读到的:GO符文是32位长的,UTF-8字符是1到4字节长的,这使得简单地将一个字节片转换成一个字符串是不可能的

我想知道如何读写这个UTF-8流

注意 我拥有读取字符串所需的字节缓冲区长度。

首先是一些理论:

  • Go中的
    符文
    表示一个Unicode码点-一个在Unicode中分配给特定字符的数字。它是
    uint32
    的别名
  • 是一种Unicode编码-一种表示存储和传输方式的Unicode码点的格式。UTF-8可能使用1到4个字节来编码单个代码点
这如何映射移动数据类型:

  • []字节和
    字符串都存储一系列字节(Go中的
    字节是
    uint8
    的别名)

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

    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编码,特别是:
    • 字符串
      []符文
      之间的类型转换将字符串解析为UTF-8编码的代码点序列,并生成其中的一个片段。反向类型转换从符文片段中获取Unicode代码点,并生成UTF-8编码的字符串
    • 字符串上的
      范围
      循环通过组成字符串的Unicode代码点循环,而不仅仅是字节
Go还提供
字符串
[]字节
之间的类型转换以及返回。现在回想一下,字符串是只读的,而字节片不是。这意味着像

b := make([]byte, 1000)
io.ReadFull(r, b)
s := sting(b)
始终复制数据,无论是将切片转换为字符串还是反向转换。这会浪费空间,但类型安全并强制执行语义

现在回到你手头的任务

如果您使用的是相当小的字符串,并且没有内存压力,只需将由
io.Read()
(或其他)填充的字节片转换为字符串即可。请确保重用用于读取数据的切片,以减轻垃圾收集器的压力-也就是说,不要为每次新读取分配新切片,因为您要将读取代码输入的数据复制到字符串中

最后,如果您绝对不能复制数据(例如,您处理的是多兆字节的字符串,并且内存要求很紧),您可能会尝试通过不安全地使用内存来玩一些肮脏的把戏-这是一个如何将内存从字节片移植到字符串的示例。请注意,如果您恢复到这样的情况,您必须非常清楚,它可以自由地与任何新版本的Go决裂,甚至不能保证它能正常工作。

首先是一些理论:

  • Go中的
    符文
    表示一个Unicode码点-一个在Unicode中分配给特定字符的数字。它是
    uint32
    的别名
  • 是一种Unicode编码-一种表示存储和传输方式的Unicode码点的格式。UTF-8可能使用1到4个字节来编码单个代码点
这如何映射移动数据类型:

  • []字节和
    字符串都存储一系列字节(Go中的
    字节是
    uint8
    的别名)

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

    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编码,特别是:
    • 字符串
      []符文
      之间的类型转换将字符串解析为UTF-8编码的代码点序列,并生成其中的一个片段。反向类型转换从符文片段中获取Unicode代码点,并生成UTF-8编码的字符串
    • 字符串上的
      范围
      循环通过组成字符串的Unicode代码点循环,而不仅仅是字节
Go还提供
字符串
[]字节
之间的类型转换以及返回。现在回想一下,字符串是只读的,而字节片不是。这意味着像

b := make([]byte, 1000)
io.ReadFull(r, b)
s := sting(b)
始终复制数据,无论是将切片转换为字符串还是反向转换。这会浪费空间,但类型安全并强制执行语义

现在回到你手头的任务

如果您使用的是相当小的字符串,并且没有内存压力,只需将由
io.Read()
(或其他)填充的字节片转换为字符串即可。请确保重用用于读取数据的切片,以减轻垃圾收集器的压力-也就是说,不要为每次新读取分配新切片,因为您要将读取代码输入的数据复制到字符串中

最后,如果您绝对不能复制数据(例如,您处理的是多兆字节的字符串,并且内存要求很紧),您可能会尝试通过不安全地使用内存来玩肮脏的把戏-这是一个如何将内存从字节片移植到st的示例