Java判斷字節(jié)流是否是 UTF8編碼方法示例
Java 判斷字節(jié)流是否是 UTF8 編碼
遇到本來設(shè)計時使用 GBK 編碼處理的地方,在實際使用過程導(dǎo)入了 UTF8 編碼,造成了顯示文本為亂碼的現(xiàn)象,在了解 UTF8,GBK 編碼和 Unicode 標(biāo)準(zhǔn)之后,編寫了 Java 判斷字節(jié)流是否是 UTF8 編碼的程序,如果是 UTF8 編碼,則轉(zhuǎn)換成 GBK 編碼。
編碼的基礎(chǔ)知識
Unicode 是一種標(biāo)準(zhǔn),GBK 和 UTF8 是具體是編碼格式。Java 的字符都是以 Unicode 進(jìn)行存儲的,占兩或四個字節(jié)(看版本,且 Unicode 編碼中對應(yīng)關(guān)系是存在 0x00 的編碼的)。Java 中的 getBytes() 方法是和平臺(編碼)相關(guān)的,在中文系統(tǒng)中返回的可能是 GBK 或 GBK2312,在英文系統(tǒng)中返回的可能是 ISO-8859-1。
- Unicode 標(biāo)準(zhǔn):是計算機(jī)科學(xué)領(lǐng)域里的一項業(yè)界標(biāo)準(zhǔn),包括字符集、編碼方案等,它為每種語言中的每個字符設(shè)定了統(tǒng)一并且唯一的二進(jìn)制編碼,以滿足跨語言、跨平臺進(jìn)行文本轉(zhuǎn)換、處理的要求。
- GBK 編碼:漢字內(nèi)碼擴(kuò)展規(guī)范,國標(biāo),漢字占兩個字節(jié)。
- UTF8 編碼:針對 Unicode 的可變長度字符編碼,用 1 到 6 個字節(jié)編碼 Unicode 字符,漢字一般占 3 個字節(jié)。
UTF8 編碼格式
如果 Unicode 字符由 2 個字節(jié)表示,則編碼成 UTF8 很可能需要 3 個字節(jié)。而如果 Unicode 字符由 4 個字節(jié)表示,則編碼成 UTF8 可能需要 6個字節(jié)。用 4 個或 6 個字節(jié)去編碼一個 Unicode 字符可能太多了,但很少會遇到那樣的 Unicode 字符。
UTF8 編碼規(guī)則:如果只有一個字節(jié)則其最高二進(jìn)制位為 0,如果是多字節(jié),其第一個字節(jié)從最高位開始,連續(xù)的二進(jìn)制位值為 1,1 的個數(shù)決定了其編碼的字節(jié)數(shù),其余各字節(jié)均以 10 開頭。
// Unicode6.1定義范圍:0~10 FFFF // 20 0000 ~ 3FF FFFF 和 400 0000 ~ 7FFF FFFF 屬于 UCS-4,UTF8 現(xiàn)在已經(jīng)棄用了這部分內(nèi)容 --------------------------------------------------------------------------------- n | Unicode (十六進(jìn)制) | UTF - 8 (二進(jìn)制) --+-----------------------+------------------------------------------------------ 1 | 0000 0000 - 0000 007F | 0xxxxxxx 2 | 0000 0080 - 0000 07FF | 110xxxxx 10xxxxxx 3 | 0000 0800 - 0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx 4 | 0001 0000 - 0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx --------------------------------------------------------------------------------- // 以下部分棄用 5 | 0020 0000 - 03FF FFFF | 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 6 | 0400 0000 - 7FFF FFFF | 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx ---------------------------------------------------------------------------------
Java 如何判斷單個字符編碼是否是 UTF8
假設(shè)當(dāng)前需要判定一個 byte[] 數(shù)組內(nèi)的編碼是否是 UTF8 編碼,這個 byte[] 是 String 通過 getBytes() 方法獲取的,判斷單個字符的編碼步驟如下:
- 從 byte[] 數(shù)組中獲取一個 byte 并將它轉(zhuǎn)換成無符號類型的 int 變量 value
- 判斷 value 是否是 ASCII 字符(小于 0x80)
- 判斷 value 是否是無效字符(大于 0x80,小于 0xC0,參照 UTF8 編碼規(guī)則)
- 確認(rèn)該字符編碼的是幾字節(jié) UTF8
- 確認(rèn)該字符編碼的除第一個字節(jié)外的字節(jié)是否滿足 10xxxxxx 格式
PS:
Java getBytes() 獲取的是帶符號的十六進(jìn)制,實際處理時需要使用無符號十六進(jìn)制。
GBK 和 UTF8 中 ASCII 字符的值是一樣的。
具體程序
將十六進(jìn)制流中的所有編碼按照單個判定的方式便利一遍,如果有不符合 UTF8 編碼規(guī)則的字符出現(xiàn),則該十六進(jìn)制流就不是 UTF8 編碼格式的字串。
public static int byteToUnsignedInt(byte data) { return data & 0xff; } public boolean isUTF8(byte[] pBuffer) { boolean IsUTF8 = true; boolean IsASCII = true; int size = pBuffer.length; int i = 0; while (i < size) { int value = byteToUnsignedInt(pBuffer[i]); if (value < 0x80) { // (10000000): 值小于 0x80 的為 ASCII 字符 if (i >= size - 1) { if (IsASCII) { // 假設(shè)純 ASCII 字符不是 UTF 格式 IsUTF8 = false; } break; } i++; } else if (value < 0xC0) { // (11000000): 值介于 0x80 與 0xC0 之間的為無效 UTF-8 字符 IsASCII = false; IsUTF8 = false; break; } else if (value < 0xE0) { // (11100000): 此范圍內(nèi)為 2 字節(jié) UTF-8 字符 IsASCII = false; if (i >= size - 1) { break; } int value1 = byteToUnsignedInt(pBuffer[i + 1]); if ((value1 & (0xC0)) != 0x80) { IsUTF8 = false; break; } i += 2; } else if (value < 0xF0) { IsASCII = false; // (11110000): 此范圍內(nèi)為 3 字節(jié) UTF-8 字符 if (i >= size - 2) { break; } int value1 = byteToUnsignedInt(pBuffer[i + 1]); int value2 = byteToUnsignedInt(pBuffer[i + 2]); if ((value1 & (0xC0)) != 0x80 || (value2 & (0xC0)) != 0x80) { IsUTF8 = false; break; } i += 3; } else if (value < 0xF8) { IsASCII = false; // (11111000): 此范圍內(nèi)為 4 字節(jié) UTF-8 字符 if (i >= size - 3) { break; } int value1 = byteToUnsignedInt(pBuffer[i + 1]); int value2 = byteToUnsignedInt(pBuffer[i + 2]); int value3 = byteToUnsignedInt(pBuffer[i + 3]); if ((value1 & (0xC0)) != 0x80 || (value2 & (0xC0)) != 0x80 || (value3 & (0xC0)) != 0x80) { IsUTF8 = false; break; } i += 3; } else { IsUTF8 = false; IsASCII = false; break; } } return IsUTF8; }
UTF8 編碼轉(zhuǎn) GBK 編碼
// Unicode String unicodeString = "張三"; // 獲取 UTF8 編碼 byte[] nameUTF8 = unicodeString.getBytes("utf-8"); // UTF8 編碼轉(zhuǎn) str String str = new String(name, "utf-8"); // 獲取 GBK 編碼 byte[] nameGBK = str.getBytes("gbk");
以上就是Java判斷字節(jié)流是否是 UTF8編碼方法示例的詳細(xì)內(nèi)容,更多關(guān)于Java字節(jié)流UTF8編碼判斷的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
詳解Java如何在CompletableFuture中實現(xiàn)日志記錄
這篇文章主要為大家詳細(xì)介紹了一種slf4j自帶的MDC類,來記錄完整的請求日志,和在CompletableFuture異步線程中如何保留鏈路id,需要的可以參考一下2023-04-04詳解SpringBoot Mongo 自增長ID有序規(guī)則
本文主要介紹springboot基于mongodb有序id生成,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下2021-09-09使用SpringBoot進(jìn)行身份驗證和授權(quán)的示例詳解
在廣闊的 Web 開發(fā)世界中,身份驗證是每個數(shù)字領(lǐng)域的守護(hù)者,在本教程中,我們將了解如何以本機(jī)方式保護(hù)、驗證和授權(quán) Spring-Boot 應(yīng)用程序的用戶,并遵循框架的良好實踐,希望對大家有所幫助2023-11-11