日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > java >内容正文

java

ansi编码_Java 字符编码

發布時間:2023/12/10 java 31 豆豆
生活随笔 收集整理的這篇文章主要介紹了 ansi编码_Java 字符编码 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

點擊上方藍字關注我們!

作者介紹

王云靜,Java 開發工程師,2018 年 7 月加入去哪兒網,目前在目的地 - 呼叫中心。曾獲得過 ACM 亞洲區域賽銅牌。

-----

基本概念

字符集

字符(Character)是各種文字和符號的總稱,包括各國家文字、標點符號、圖形符號、數字等。字符集(Character set)是多個字符的集合,字符集種類較多,每個字符集包含的字符個數不同,常見字符集名稱:ASCII 字符集、GB2312 字符集、BIG5 字符集、 GB18030 字符集、Unicode 字符集等。計算機要準確的處理各種字符集文字,就需要進行字符編碼,以便計算機能夠識別和存儲各種文字。

字符編碼

字符編碼(英語:Character encoding)也稱字集碼,是把字符集中的字符編碼為指定集合中某一對象(例如:比特模式、自然數序列、8 位組或者電脈沖),以便文本在計算機中存儲和通過通信網絡的傳遞。

為了理解字符集和字符編碼的關系,這里舉個簡單的例子,我們可以把字符集當成接口,把字符編碼當成接口的實現。Unicode是接口(字符集), UTF-8 / UTF-16 / UTF-32則是不同的實現(字符編碼)。

ANSI

ANSI 全稱(American National Standard Institite)美國國家標準學會(美國的一個非營利組織),首先 ANSI 不是指的一種特定的編碼,而是不同地區擴展編碼方式的統稱,各個國家和地區所獨立制定的兼容 ASCII 但互相不兼容的字符編碼,微軟統稱為 ANSI 編碼。

在簡體中文 windows 下使用文本文件保存”聯通“,則再次打開會顯示亂碼。這是因為 windows 下的文本文件默認使用 ansi 字符集,而簡體中文 windows 下的 ansi 字符集為 GB2312,”聯通“兩個字的 GB2312 編碼看起來和 UTF-8 非常相似,又因為我們沒有在文件開頭設置字符集標記(BOM),所以當我們再次打開該文件時,被識別為 UTF-8,因此出現亂碼。

Unicode

Unicode(統一碼、萬國碼、單一碼)是計算機科學領域里的一項業界標準,包括字符集、編碼方案等。Unicode 是為了解決傳統的字符編碼方案的局限而產生的,它為每種語言中的每個字符設定了統一并且唯一的二進制編碼,以滿足跨語言、跨平臺進行文本轉換、處理的要求。

  • CodeUnit:代碼單元/編碼單元,是 Unicode 編碼里一個?CodePoint需要的最少字節數

    例如:UTF-8 是一個字節,UTF-16 是兩個字節,UTF-32 是四個字節

  • CodePoint:代碼點,Unicode 規定的每一個字符就是一個?CodePoint

  • CodeSpace:代碼空間,所有的代碼點構成一個代碼空間,根據 Unicode 定義,總共有?1,114,112?個代碼點,編號從?0x0-0x10FFFF,也就是大概 110 多萬個字符

  • CodePlane:代碼平面,Unicode 標準把代碼點分成了17 個代碼平面,編號為 #0-#16。每個代碼平面包含 65,536(2^16)個代碼點(17*65,536=1,114,112)。#0 叫做?基本多語言平面(BMP:大部分常用的字符都坐落在這個平面內,比如 ASCII 字符,漢字等。代碼點范圍:0x0000-0xFFFF),其余平面叫做?補充平面

  • SurrogatePair:代理對,由一個?High-surrogate(高代理代碼點:?0xD800-0xDBFF)和一個?Low-surrogate(低代理代碼點:?0xDC00-0xDFFF)組成。這 2048 個代碼點位于 BMP 內,并且不是有效的字符代碼點,它們是為 UTF 編碼保留的。在 UTF-16 中它可以編碼 BMP 之外的代碼點

UTF-8

UTF-8 的特點是對不同范圍的字符使用不同長度的編碼。對于 0x00-0x7F 之間的字符,UTF-8 編碼與 ASCII 編碼完全相同。UTF-8 編碼的最大長度是 4 個字節。

  • 對于單個字節的字符,第一位設為 0,后面的 7 位對應這個字符的 Unicode 碼點。

  • 對于需要使用 N 個字節來表示的字符(N > 1),第一個字節的前 N 位都設為 1,第 N + 1 位設為0,剩余的 N - 1 個字節的前兩位都設位 10,剩下的二進制位則使用這個字符的 Unicode 碼點來填充。

Unicode 編碼(十六進制)UTF-8 字節流(二進制)
000000-00007F0xxxxxxx
000080-0007FF110xxxxx 10xxxxxx
000800-00FFFF1110xxxx 10xxxxxx 10xxxxxx
010000-10FFFF11110xxx 10xxxxxx 10xxxxxx 10xxxxxx

UTF-8 編碼的最大長度是 4 個字節。從上表可以看出,4 字節模板有 21 個 x,即可以容納 21 位二進制數字。Unicode 的最大碼位 0x10FFFF 也只有 21 位。

例1:“漢”字的Unicode 編碼是 0x6C49。0x6C49 在 0x0800-0xFFFF 之間,使用3字節模板:1110xxxx10xxxxxx10xxxxxx。將0x6C49寫成二進制是:0110110001001001, 用這個比特流依次代替模板中的x,得到:111001101011000110001001,即 E6 B1 89。

例2:Unicode編碼0x20C30在0x010000-0x10FFFF 之間,使用 4 字節模板 11110xxx10xxxxxx10xxxxxx10xxxxxx。將 0x20C30 寫成 21 位二進制數字(不足 21 位就在前面補 0):000100000110000110000,用這個比特流依次代替模板中的 x,得到:11110000101000001011000010110000,即 F0 A0 B0 B0。

UTF-16

UTF-16 是 Unicode 的一種編碼方式,它用兩個字節來編碼 BMP 里的代碼點,用四個字節編碼其余平面里的代碼點。

為了書寫方便,我們把 Unicode 編碼記作 U。

  • 如果?U<0x10000,U 的 UTF-16 編碼就是 U 對應的 16 位無符號整數。

  • 如果?U≥0x10000,我們先計算 U'=U-0x10000,然后將 U' 寫成二進制形式:?yyyy yyyy yyxx xxxx xxxx,U 的 UTF-16 編碼(二進制)就是:?110110yyyyyyyyyy110111xxxxxxxxxx。

為什么 U' 可以被寫成 20 個二進制位?

Unicode 的最大碼位是 0x10FFFF,減去 0x10000 后,U 的最大值是 0xFFFFF,所以肯定可以用 20 個二進制位表示。

為什么把 0x10000-0x10FFFF編碼為 110110yyyyyyyyyy110111xxxxxxxxxx?

110110yyyyyyyyyy 的取值范圍為 1101100000000000- 1101101111111111,即? 0xD800-0xDBFF( High-surrogate)

110111xxxxxxxxxx 的取值范圍為 1101110000000000- 1101111111111111,即? 0xDC00-0xDFFF( Low-surrogate) 所以 110110yyyyyyyyyy110111xxxxxxxxxx正好是一個 SurrogatePair,也就是 4 個字節

例如:Unicode 編碼 0x20C30,減去 0x10000 后,得到 0x10C30,寫成二進制是:00010000110000110000。用前 10 位依次替代模板中的 y,用后 10 位依次替代模板中的 x,就 得到:11011000010000111101110000110000,即 0xD843 0xDC30。

UTF-32

UTF-32 編碼以 32 位無符號整數為單位。Unicode 的 UTF-32 編碼就是其對應的 32 位無符號整數。

大/小端

大小端是 CPU 處理多字節數的不同方式,其主要特點是字節序在內存中存儲位置不同。

  • 大端(Big-Endian):高字節序存儲在低地址,低字節序存儲在高地址。

  • 小端(Little-Endian):高字節序存儲在高地址,低字節序存儲在低地址。

對于任何字符編碼,編碼單元的順序是由編碼方案指定的,與 endian 無關。例如 GBK 的編碼單元是字節,用兩個字節表示一個漢字。這兩個字節的順序是固定的,不受 CPU 字節序的影響。UTF-16 的編碼單元是 word(雙字節),word 之間的順序是編碼方案指定的,word 內部的字節排列才會受到 endian 的影響。

在網絡上傳輸數據時,由于數據傳輸的兩端對應不同的硬件平臺,采用的存儲字節順序可能不一致。所以在 TCP/IP 協議規定了在網絡上必須采用網絡字節順序,也就是大端模式。

JVM 屏蔽了大小端問題,默認為大端,并且可使用 ByteOrder.nativeOrder()查詢處理器和內存系統的大小端,在使用 ByteBuffer 時,也可以使用 ByteBuffer.order()進行設置。

BOM

Unicode 的學名是 "Universal Multiple-Octet Coded Character Set",簡稱為 UCS。UCS 可以看作是 "Unicode Character Set" 的縮寫。在 UCS 編碼中有一個叫做?"Zero Width No-Break Space",中文譯名作“零寬無間斷間隔”的字符,它的編碼是? FEFF。而 FFFE 在 UCS 中是不存在的字符,所以不應該出現在實際傳輸中。UCS 規范建議我們在傳輸字節流前,先傳輸字符 "Zero Width No-Break Space"。這樣如果接收者收到 FEFF,就表明這個字節流是 Big-Endian 的;如果收到 FFFE,就表明這個字節流是 Little- Endian 的。因此字符 "Zero Width No-Break Space" (“零寬無間斷間隔”)又被稱作 BOM(即 Byte Order Mark)。

UTF-8 BOM:UTF-8 以字節為編碼單元,沒有字節序的問題,但可以用 BOM 來表明編碼方式。字符 "Zero Width No-Break Space" 的 UTF-8 編碼是 EF BB BF。(windows 系統默認使用 UTF-8 BOM 編碼,需要注意)

文件:D:\bom.txt,編碼:UTF-8-BOM

代碼:

public static void main(String[] args) throws Exception {

File file = new File("D:\\\\bom.txt");

FileInputStream inputStream = new FileInputStream(file);

int i = 0;

while ((i = inputStream.read()) != -1) {

System.out.printf("0x"+Integer.toHexString(i) + " ");

}

}

結果

0xef0xbb0xbf0xe60xb50x8b0xe80xaf0x950x550x540x460x380x200x420x4f0x4d

常見BOM

BOM Encoding字符編碼
EF BB BFUTF-8 BOM
FE FFUTF-16 (big-endian)
FF FEUTF-16 (little-endian)
00 00 FE FFUTF-32 (big-endian)
FF FE 00 00UTF-32 (little-endian)

Java 字符編碼

Java 使用 Unicode 字符集并且使用 UTF-16 字符編碼。

Java 語言規范規定,Java 的 char 類型是 UTF-16的code unit,也就是一定是 16 位(2 字節)。

一個字符到底占用多少個字節?

對于 Java 中的 char 類型來說的話,固定占用 2 字節,但是為什么使用 newString("字").getBytes().length返回的是 3,這是因為 getBytes實際是做了 編碼轉換(內碼轉外碼),你可以顯式傳入一個參數來指定編碼,否則它會使用 缺省編碼來轉換。

對于肉眼可見的字符來說,這個取決于字符編碼,同一個字符在不同的編碼下占用不同的字節。例如:漢字的"字","字"在 GBK 編碼下占 2 字節,在 UTF-8 編碼下占 3 字節,在 UTF-32 編碼下占 4 字節。

內碼 & 外碼

  • 內碼:程序內部使用的字符編碼,特別是某種語言實現其 char 或 String 類型在內存里用的內部編碼。Java 的內碼就是 UTF-16。

  • 外碼:程序與外部交互時外部使用的字符編碼。簡單的來說就是除了內碼都可以認為是“外碼”(包括 class 文件的編碼)。

內碼轉外碼:

  • String.getBytes(StringcharsetName):將內存中的字符串用 UTF-16 編碼轉換為指定編碼的 byte 序列。

  • String.getBytes():將內存中的字符串用 UTF-16 編碼轉換為缺省編碼的 byte 序列。

外碼轉內碼:

  • newString(byte[]bytes,Stringcharset):就是把字節流以指定的編碼轉換為 UTF-16 編碼的字節流存入內存中。

  • Java 的 class 文件是以 UTF-8 的方式來編碼的。JVM 讀取 class 文件時需要把 UTF-8 編碼轉換為 UTF-16 編碼讀入內存。

注意:編碼和解碼的“字符編碼”必須要一致才能解碼成想要的字符串。

缺省編碼

可以在啟動 JVM 時通過 -Dfile.encoding=UTF-8 來設置,否則使用操作系統環境下的缺省編碼,可通過 Charset.defaultCharset()獲取編碼。

通常,Windows 系統下是 GBK,Linux 和 Mac 是 UTF-8。

如果使用 IDE,則會使用工程的缺省編碼,具體編碼是什么,需要看具體使用的 IDE,可以百度來修改 IDE 的編碼。(有可能會遇到某 IDE 在啟動項目時控制臺打印的日志是亂碼,這就是編碼搞得鬼) 因為 getBytes受缺省編碼的影響而得到的結果不同,所以在使用該方法時,建議顯示指定編碼。

String#length()獲取的是真正的字符串長度嗎?

不是,它獲取的僅僅是代碼單元的數量,而真正的字符串長度是代碼點數量,可使用 String#codePointCount()來獲取。

原因:我們在上面介紹過,某字符的代碼點>=0x10000時UTF-16 編碼會占用 4 個字節,又因為 Java 的 char 固定就是 2 字節,所以我們需要使用 2 個 char 來表示該字符,那么在使用 String#length()獲取字符串長度時,就會出錯。

為什么我們平時都是使用該方法獲取字符串長度呢?因為 BMP 里定義了我們使用的大部分字符,并且我們基本使用不到 BMP 之外的字符。

為什么 Java 不使用定長編碼呢?

Java 設計之初 UTF-16 確實是定長編碼,只不過后來 Unicode 的字符變多了之后,UTF-16 變成了變長編碼。

Java 5.0 版本既要支持 Unicode 4.0 同時要保證向后兼容性,不得不開始使用 UTF-16 ?作為內部編碼方式。

代碼點/代碼單元分析

代碼

// 16進制,10進制,2進制

// 0xd801, 55297, 1101 1000 0000 0001

// 0xdc01, 56321, 1101 1100 0000 0001

// UTF-16編碼: 0xDB01 0xDC01, 3624000513, 110110 00 0000 0001 110111 00 0000 0001

// U': 0x401, 1025, 00 0000 0001 00 0000 0001

// Unicode碼點 = U'+0x10000: 0x10401, 66561, 0000 0000 0000 0001 0000 0100 0000 0001

String str = "\ud801\udc01";

System.out.println("str = "+ str);

// 代碼單元(char)

System.out.println("str.length() = "+ str.length());

// 代碼點

System.out.println("str.codePointCount(0, str.length()) = "+ str.codePointCount(0, str.length()));

// 代碼點的int值,如果當前位置是高代理,則返回高代理+低代理對應代碼點的int值,否則,會返回當前位置的int值

// 高代理,返回高代理+低代理對應代碼點的int值

System.out.println("str.codePointAt(0) = "+str.codePointAt(0));

// 低代理,返回該位置的int值

System.out.println("str.codePointAt(1) = "+ str.codePointAt(1));

// char,高代理

System.out.println("str.charAt(0) = "+ str.charAt(0));

// char, 低代理

System.out.println("str.charAt(1) = "+ str.charAt(1));

結果

str = ?

str.length() = 2

str.codePointCount(0, str.length()) = 1

str.codePointAt(0) = 66561

str.codePointAt(1) = 56321

str.charAt(0) = ?

str.charAt(1) = ?

編解碼

可通過 Charset.availableCharsets().keySet()來查看 Java 到底支持哪些字符編碼。

  • 編碼:字符集的字符 => 字節數組

  • 解碼:字節數組 => 字符集的字符

  • 編碼轉換:字符集1的字符 => 字符集 2 的字符,通過這里可以看出如果 2 個字符集的某個字符沒有對應關系,那么就會導致亂碼

其他來源的字符保存到 JVM 內存需要經歷:字節數組 => 指定編碼的字符 => Unicode 字符 => UTF-16 編碼后的字節數組。

JVM 內存保存到其他地方需要經歷:UTF-16 編碼后的字節數組 => Unicode字符 => 指定編碼的字符 => 字節數組。

由此可以看出,系統內部是做了一步字符集轉換。如果兩個字符集沒有轉換規則,那么就會使用 Codepage(代碼頁),有興趣的同學可以自行去了解代碼頁。

代碼

public static void main(String[] args) throws Exception {

byte[] utf8Bytes = "編碼轉換".getBytes("UTF-8");

System.out.println("UTF-8 = " + new String(utf8Bytes, "UTF-8"));

System.out.printf("utf8Bytes = ");

print(utf8Bytes);

String gbk = new String(utf8Bytes, "GBK");

System.out.println("GBK = " + gbk);

byte[] gbkBytes = gbk.getBytes("GBK");

System.out.printf("gbkBytes = ");

print(gbkBytes);

byte[] isoBytes = gbk.getBytes("ISO-8859-1");

System.out.printf("isoBytes = ");

print(isoBytes);

System.out.println("UTF-8 = " + new String(gbkBytes, "UTF-8"));

}

private static void print(byte[] bytes) {

for (int i = 0; i < bytes.length; i++) {

System.out.printf(bytes[i] + " ");

}

System.out.println();

}

結果

UTF-8= 編碼轉換

utf8Bytes = -25-68-106-25-96-127-24-67-84-26-115-94

GBK = 緙栫爜杞崲

gbkBytes = -25-68-106-25-96-127-24-67-84-26-115-94

isoBytes = 636363636363

UTF-8= 編碼轉換

1. "編碼轉換"以?UTF-8編碼存儲在class文件。

2. JVM 加載 class 文件,從常量池中讀取”編碼轉換“,并使用 UTF-8 解碼為 Unicode 字符,然后使用 UTF-16 編碼保存到 JVM 內存。

3. "編碼轉換".getBytes("UTF-8"),直接從 JVM 內存獲取"編碼轉換"的字節數組,然后使用 UTF-16 解碼為 Unicode 字符,最后使用 UTF-8 編碼為新的字節數組。(UTF-16 => UTF-8)

4. new String(utf8Bytes, "GBK"),使用 GBK 把 bytes 解碼為 GBK 字符,然后把 GBK 字符轉換為 Unicode 字符,最后使用 UTF-16 編碼保存到 JVM 內存(GBK 和 Unicode 無聯系,所以通過代碼頁來完成轉換)。

瀏覽器/Tomcat/Mysql

瀏覽器編碼

URI:不同瀏覽器采用的編碼方案不同。例如 chrome 使用 UTF-8 header:ISO-8859-1 body:根據 Content-Type 來進行編碼

chrome瀏覽器

瀏覽器解碼

header:ISO-8859-1 body:首先查看 Content-Type 中是否存在編碼方案,其次若返回的是 html 格式,則查看 meta 中是否指定字符集,最后使用瀏覽器默認字符集解碼

在 chrome 中可以使用插件手動修改默認的字符集。例如:Set Character Encoding。

tomcat 編碼

依賴于應用程序

tomcat 解碼

依賴于 server.xml 的 Connector 的配置

UTIEncoding:URI 的編碼,tomcat7 默認 ISO-8859-1,tomcat8 默認 UTF-8。

useBodyEncodingForURI:使 URI 編碼等于 request.setCharacterEncoding()設置的編碼。

request.setCharacterEncoding():指定 body 體的編碼方式,必須在第一次獲取 body 體內容之前設置。

Spring

如果使用 Spring MVC,則需要配置 CharacterEncodingFilter為第一個過濾器,并且指定編碼。

如果使用 Spring Boot,則無需配置,默認配置了 OrderedCharacterEncodingFilter,默認編碼為 UTF-8。

這個過濾器只是針對的 body 體,至于 get 請求還依賴于使用的 tomcat 版本,如使用 Tomcat7,則需要去 server.xml 配置 UTIEncoding/useBodyEncodingForURI,若使用 Tomcat8,無需配置。

Mysql

character_set_client:客戶端數據解析、編碼的字符集?

character_set_connection:連接層字符集

character_set_database:當前數據庫的字符集

character_set_server:服務器內部操作字符集?

character_set_results:查詢結果字符集?

character_set_system:系統源數據(字段名等)字符集

setnames utf8mb4等同于同時設置 character_set_client, character_set_connection, character_set_results這三個字符集

1. 客戶端使用特定字符集編碼 SQL 發送到服務端。

2. 服務端接受到字節流后,使用?character_set_client進行解碼。

3. 服務端解碼后,會使用?character_set_connection進行編碼,然后傳給存儲引擎。若?character_set_connection和?character_set_client不同,則發生字符集轉換操作。

4. 存儲引擎查詢表時,若表字符集和?character_set_connection不同,則發生字符集轉換操作。

5. 存儲引擎查詢到結果之后,會使用?character_set_results進行編碼返回給客戶端,若表字符集和?character_set_results不同,則發生字符集轉換操作。

6. 客戶端接受到結果后,使用特定字符集進行解碼。

客戶端charactersetclientcharactersetconnection表字符集charactersetresults結果
utf8utf8utf8/gbkutf8/gbkutf8正常
gbkgbkutf8/gbkutf8/gbkgbk正常
gbkutf8utf8/gbkutf8/gbkgbk亂碼
gbkgbkutf8/gbkutf8/gbkutf8亂碼
gbkutf8utf8/gbkutf8/gbkutf8亂碼

由此表可以看出,必須保證 客戶端、 character_set_client、 character_set_results一致才可以保證數據正常。如果使 character_set_connection、 表字符集和上面 3 個保持一致,可以減少字符集轉換。

Connector / J

在使用Connector / J時,創建連接語句 jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&characterEncoding=utf8基本都會配置 useUnicode和 characterEncoding。否則創建連接時,驅動程序會自動檢測 character_set_server并使用該字符集。

要覆蓋客戶端自動檢測到的編碼,請使用 characterEncoding 服務器連接 URL 中的屬性。指定字符編碼時,請使用 Java 樣式的名稱。

對于 Connector / J 5.1.46 及更早版本:為了使用 utf8mb4 字符集進行連接,服務器必須配置為 charactersetserver=utf8mb4。如果不是這種情況,UTF-8則characterEncoding在連接字符串中使用時,它將映射到 MySQL 字符集名稱 utf8。

對于 Connector / J 5.1.47 及更高版本:當 UTF-8 用于 characterEncoding 連接字符串中,它映射到 MySQL 的字符集的名字 utf8mb4。

參考資料

  • https://www.cnblogs.com/binarylei/p/10760233.html

  • https://baike.baidu.com/item/Unicode/750500?fr=aladdin

  • https://baike.baidu.com/item/%E5%AD%97%E7%AC%A6%E9%9B%86/946585?fr=aladdin

  • https://baike.baidu.com/item/%E5%AD%97%E7%AC%A6%E7%BC%96%E7%A0%81/8446880

  • https://www.zhihu.com/question/27562173/answer/76208352

  • http://www.imooc.com/article/26166

  • http://www.fmddlmyy.cn/text6.html

  • https://blog.csdn.net/duduniao999/article/details/80872701

  • https://www.cnblogs.com/lanhaicode/p/11214827.html

  • https://www.zhihu.com/question/30945431/answer/50046808

  • https://www.cnblogs.com/jave1ove/p/7454966.html

  • https://jingyan.baidu.com/article/148a1921189b234d71c3b1df.html

  • https://dev.mysql.com/doc/connector-j/5.1/en/connector-j-reference-charsets.html

【END】

總結

以上是生活随笔為你收集整理的ansi编码_Java 字符编码的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。