我正在寫一個帶有TCP套接字的小型通信協議。 我能夠讀寫整數等基本數據類型,但我不知道如何從一個字節片段讀取UTF-8編碼的字符串。從io.Reader讀取UTF-8編碼字符串
協議客戶端是用Java編寫的,服務器是Go。
由於每個I讀:GO符是32位的長和UTF-8字符是1至4個字節長,讓不能夠簡單地投下字節切片爲字符串。
我想知道我怎麼能讀寫這個UTF-8流。
注意 我有字節緩衝區的時間長度來讀取字符串。
我正在寫一個帶有TCP套接字的小型通信協議。 我能夠讀寫整數等基本數據類型,但我不知道如何從一個字節片段讀取UTF-8編碼的字符串。從io.Reader讀取UTF-8編碼字符串
協議客戶端是用Java編寫的,服務器是Go。
由於每個I讀:GO符是32位的長和UTF-8字符是1至4個字節長,讓不能夠簡單地投下字節切片爲字符串。
我想知道我怎麼能讀寫這個UTF-8流。
注意 我有字節緩衝區的時間長度來讀取字符串。
一些理論第一:
rune
表示Unicode代碼點—分配到以Unicode一個特定字符的數字。這是uint32
的別名。這怎麼映射上的Go數據類型:
兩個[]byte
和string
店一系列字節(圍棋一個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碼點,不只是字節。圍棋還提供string
和[]byte
和背部之間的類型轉換。現在回想一下,字符串是隻讀的,而字節片不是。這意味着像
b := make([]byte, 1000)
io.ReadFull(r, b)
s := sting(b)
總是複製數據,無論一個結構,如果你轉換一個切片一個字符串或背部。這浪費了空間,但是是類型安全的並且強化了語義。
現在回到你手頭的任務。
如果你有合理的小字符串的工作,並不在內存壓力下,只是轉換由io.Read()
(或其他)充滿了你的字節切片字符串。請務必重複使用您正在使用的切片讀取數據,以緩解垃圾收集器—的壓力,也就是說,不要爲每次新讀取分配新切片,因爲您要複製放入其中的數據由閱讀代碼關閉到一個字符串。
最後,如果你絕對必須向不復制數據(比如,你在處理多兆字節的字符串,你有嚴格的內存需求),你可以嘗試通過不安全工作發揮搞鬼與內存— here是一個例子,你可能如何移植內存從字節切片到字符串。請注意,如果你恢復到這樣的狀態,你必須非常清楚地知道它可以隨任何新版本的Go一起打破,而且它甚至不能保證工作。
只是爲了澄清:當我將字節片「轉換」爲字符串時,不只是「投」字節,而是構造一個新的字符串? – Mikhas
@Mikhas,這是正確的:數據字節被複制。這是Go的幾個地方之一,它的開發者爲了簡化類型轉換而做出了實用的選擇,允許這種*隱藏成本*。再想一想:如果Go只是將一個字節片段的數據「投」到一個字符串中,那麼原始片段不會以某種方式在投射後自動捕獲並存在,並且您將能夠更改字符串的內容*保證爲只讀*通過將與該字符串共享數據的片。這會違反語義(這就是'implantSlice'黑客所做的)。 – kostix
@Mikhas,[另請參閱](http://stackoverflow.com/a/12754757/720999) – kostix