解決java socket在傳輸漢字時出現截斷導致亂碼的問題

sunfulv發表於2021-06-23

解決java socket在傳輸漢字時出現截斷導致亂碼的問題

當使用socket進行TCP資料傳輸時,傳輸的字串會編碼成位元組陣列,當採用utf8編碼時,數字與字母長度為1個位元組,而漢字一般為3個位元組。這裡參考

字符集之在UTF-8中,一個漢字為什麼需要三個位元組? - 苦澀的茶 - 部落格園 (cnblogs.com)

UTF-8 往事 (taoshu.in)

如果傳輸的字串是數字,字元和漢字混雜。在資料的接收端,每次呼叫read方法接收的byte陣列的長度是一定的,由於數字,字母和漢字對應的utf8編碼長度不同,可能會導致末尾的漢字被截斷。舉個例子:假定接收端每次呼叫read方法讀取的byte陣列長度為20。而傳送短髮送的字串為"Hello World! 你好中國!",轉換為byte陣列為

[0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x20, 0x57, 0x6F, 0x72, 0x6C, 0x64, 0x21, 0x20, 0xE4, 0xBD, 0xA0, 0xE5, 0xA5, 0xBD, 0xE4, 0xB8, 0xAD, 0xE5, 0x9B, 0xBD, 0x21]

在接收端第一次讀取的byte陣列長度為20,對應的byte陣列為

[0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x20, 0x57, 0x6F, 0x72, 0x6C, 0x64, 0x21, 0x20, 0xE4, 0xBD, 0xA0,0xE5, 0xA5, 0xBD, 0xE4]

轉換為字元變成了"Hello World! 你好�",最後一個字元出現了亂碼。這是因為前19個位元組(英文空格加標點共13個位元組,前兩個漢字佔六個位元組)轉換成了字串"Hello World! 你好",最後一個位元組對應著字元"中"的三個位元組[0xE4, 0xB8, 0xAD]中的第一個位元組0xE4。但是這個一個位元組(首位為1沒有對應的單位元組字元)是無法轉換為可見的字元的。這裡就是出現了隔斷。"中"的剩餘兩個位元組為下一個read讀取得到的位元組陣列的前兩兩個位元組。

為了能夠解決截斷問題,我們需要判斷接收到的位元組陣列的最後兩個位元組是否為被截斷的漢字字元的對應的位元組。

為了解決這個問題,首先了解一個漢字字元在UTF-8編碼中的格式。基本上常見的漢字字元在UTF-8編碼中都是三個位元組。在UTF-8編碼方案中,對三位元組編碼的每個位元組都做了規定:

如果是三個位元組編碼的話,那麼第一個位元組的前三位為111,第四位為0,剩餘的兩個位元組的前兩位都為10

比如漢字"中",對應的三個位元組用二進位制可以表示為:[1110 0100, 1011 1000, 1010 1101]

滿足UTF-8編碼的要求。

如何根據編碼格式來判斷得到的byte陣列的最後一個或者兩個位元組是被截斷的呢。

我們將byte陣列的最後兩個位元組定義為firstByte和secondByte,分別對應著導數第二個位元組和倒數第一個位元組。

情況1:

如果倒數第一個位元組符合1110 xxxx格式,說明這個位元組對應著漢字字元的第一個位元組,剩餘的兩個位元組在下個被接收的byte陣列中,發生了截斷。我們需要暫存最後一個位元組secondByte,與下面一個byte陣列的前兩個位元組組合在一起解析出漢字。

情況2:

如果倒數第二個位元組的二進位制符合1110 xxxx格式,那就說明這個位元組對應著漢字字元的第一個位元組,最後一個位元組對應漢字字元的第二個位元組,第三個位元組為下個被接收的byte陣列第一個位元組,發生了截斷。這種情況需要將byte陣列的最後兩個字元儲存起來,與下面一個byte陣列的第一個位元組結合起來才能解析出對應的漢字。

對於如何判斷一個位元組是否符合1110 xxxx格式,這裡我們採用掩碼的方式,保留firstByte的前四位,遮蔽後四位(將後四位置零)。

判斷 firstByte & 11100000 == 11100000是否成立,如果成立對應第一種情況,否則

判斷secondByte & 11100000 == 11100000是否成立,成立則對應第二種情況

相關文章