Java 的 char 是兩個(gè)字節(jié), 如何存 UTF-8 的字符的

問題

Java 的 char 是兩個(gè)字節(jié), 如何存 UTF-8 的字符的?

考什么

  • 是否熟悉 Java char 和字符串. (初級(jí))
  • 是否了解字符的映射和儲(chǔ)存細(xì)節(jié). (中級(jí))
  • 是否能觸類旁通, 橫向?qū)Ρ绕渌Z言. (高級(jí))

剖析

先分析題目中兩個(gè)名詞:

  1. char 是什么?
  2. UTF-8 是什么?
  3. 然后各占幾個(gè)字節(jié)?
  4. 和 Unicode 什么關(guān)系?

char 是什么

甲骨文官方對于原始數(shù)據(jù)類型 char 定義:

char: The char data type is a single 16-bit Unicode character. It has a minimum value of '\u0000' (or 0) and a maximum value of '\uffff' (or 65,535 inclusive).

可以知道主要以下三點(diǎn):

  • char 類型是原始類型
  • Unicode 字符 (額外補(bǔ)充, 采用 UTF-16 的形式)
  • 16 bit 即 2 個(gè)字節(jié)表示一個(gè)字符

char 里面存的是什么:

System.out.println(Integer.toHexString('慶'));

打印出來的 0x5e, 0x86, 它們 2 個(gè)就是 Unicode 的碼點(diǎn).

Java 中的 char 占用 2 個(gè)字節(jié).

UTF-8 是什么

UTF-8 是一種 Unicode 的編碼形式. 它可能占用 1~4 個(gè)字節(jié). 細(xì)節(jié)可以參考Unicode ASCII UTF-8 GBK關(guān)系

例如 char test = '慶' 是用 2 個(gè)字節(jié)進(jìn)行表示的. 而它是通過 UTF-8 編碼為 e5ba86 儲(chǔ)存的, 是 3 個(gè)字節(jié).

Unicode 和 ASCII 是字符集, 而 UTF-8, UTF-16 等是一種編碼. 是 unicode 的表現(xiàn)形式.

如何存儲(chǔ)字符

字符(人類認(rèn)知) -> char (字符集) -> byte (計(jì)算機(jī)存儲(chǔ))

char 中其實(shí)是 unicode 的字符集, 然后在計(jì)算機(jī)儲(chǔ)存中通過 UTF-8 進(jìn)行編碼儲(chǔ)存. 但請注意, 實(shí)際上 Java 中使用的是 UTF-16! 具體參考編碼之JVM之外與之內(nèi)

UTF-8與UTF-16的區(qū)別 -
UTF-8 最小的單位是 1 個(gè)字節(jié), UTF-16 最小的單位是 2 個(gè)字節(jié).
也就是說, 如果這個(gè)字符是 a甘萧,a 對應(yīng) 65嘹承,在 UTF-8 中它實(shí)際上和 ASCII 碼相同, 也就是7個(gè)比特就可以表示. 但在 UTF-16 中仍要2個(gè)字節(jié)的.

如果把字符換成中文:
類似于“中”字宋税,占1個(gè)char就可以了,也就是2個(gè)字節(jié)忠荞。同樣是用UTF-16。

額外的, 注意字節(jié)序的問題

byte[] bytes= "中".getBytes("utf-16");
System.out.println(bytes.length);

for(byte b : bytes) {
    System.out.print(Integer.toHexString(Byte.toUnsignedInt(b)));
}

結(jié)果是:

4
fe ff 4e 2d

其中 fe ff 是字節(jié)序的標(biāo)志. 不是代表的真正的內(nèi)容, 而只是讓讀取這個(gè)數(shù)據(jù)的人知道字節(jié)序是什么.

觸類旁通 Python 的字符串

byteString = "I'm a byte string."

# 例如utf-8,字面量會(huì)用utf-8編碼成字節(jié)存入字符串
# coding = utf-8
byteString = "中國"      # .py文件中存放的是 UTF-8 編碼后的字節(jié)
unicodeString = u"中國"  # .py文件中存放的是 UCS2(~UTF-16) 編碼后的字節(jié)

Python 是解釋執(zhí)行的, 源文件與執(zhí)行時(shí)內(nèi)存中字符串內(nèi)容一致夫嗓。
Javac指定編碼將字符串統(tǒng)一轉(zhuǎn)為 MUTF-8 (調(diào)用這個(gè)命令的時(shí)候需要給個(gè)encoding的參數(shù))

// Java
byte[] byteString= "中".getBytes("utf-8"); 

// 對應(yīng) Python
byteString = "中國"

// Java
String unicodeString = "中國" 

// 對應(yīng) Python
unicodeString = u"中國"

unicodeString可以調(diào)用encode(),byteString得decode().

讓人迷惑的字符串長度

字符串長度不等于字符數(shù)!

// emoji 代表表情
String emoji= "emoji"
System.out.println(emoji.length());

我們會(huì)認(rèn)為看到的是 1, 所以輸出是 1, 但表情占用 2 個(gè) char. 輸出 2. 在 Java 中看到的字符串長度不一定是輸出的字符串長度.

但在 Python 中

String emoji= u"emoji"
print(len(emoji))

python >= 3.2, 結(jié)果為 1, 低版本為 2.
所以有時(shí)候輸入框有字符限制, 用戶輸入一個(gè)表情減 2 個(gè)字符, 這不合理.

Java 9并沒有改變字符串長度和字符數(shù)不一致的情況桐玻。
但是對拉丁(Latin)字符的存儲(chǔ)空間做了優(yōu)化. UTF-16 對于 a, b, c 等可以1個(gè)字節(jié)的還是需要兩個(gè)字節(jié)來存. 會(huì)改用 byte 來存. 節(jié)省一般空間.
字符串長度 篙挽!= 字符數(shù)

題目結(jié)論

  • Java char 不存 UTF-8 的字節(jié),而是 UTF-16 的.
  • Unicode 通用字符集占2個(gè)字節(jié)镊靴,例如“中”.
  • Unicode 是擴(kuò)展字符集需要用一對char來表示铣卡,例如“emoji”
  • Unicode 是字符集, 不是編碼.
  • Java String 字符串長度不等于字符數(shù).
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市偏竟,隨后出現(xiàn)的幾起案子煮落,更是在濱河造成了極大的恐慌,老刑警劉巖踊谋,帶你破解...
    沈念sama閱讀 218,941評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件蝉仇,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)轿衔,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,397評論 3 395
  • 文/潘曉璐 我一進(jìn)店門沉迹,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人害驹,你說我怎么就攤上這事鞭呕。” “怎么了裙秋?”我有些...
    開封第一講書人閱讀 165,345評論 0 356
  • 文/不壞的土叔 我叫張陵琅拌,是天一觀的道長。 經(jīng)常有香客問我摘刑,道長进宝,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,851評論 1 295
  • 正文 為了忘掉前任枷恕,我火速辦了婚禮党晋,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘徐块。我一直安慰自己未玻,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,868評論 6 392
  • 文/花漫 我一把揭開白布胡控。 她就那樣靜靜地躺著扳剿,像睡著了一般。 火紅的嫁衣襯著肌膚如雪昼激。 梳的紋絲不亂的頭發(fā)上庇绽,一...
    開封第一講書人閱讀 51,688評論 1 305
  • 那天,我揣著相機(jī)與錄音橙困,去河邊找鬼瞧掺。 笑死,一個(gè)胖子當(dāng)著我的面吹牛凡傅,可吹牛的內(nèi)容都是我干的辟狈。 我是一名探鬼主播,決...
    沈念sama閱讀 40,414評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼夏跷,長吁一口氣:“原來是場噩夢啊……” “哼哼转!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起槽华,我...
    開封第一講書人閱讀 39,319評論 0 276
  • 序言:老撾萬榮一對情侶失蹤释簿,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后硼莽,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體庶溶,經(jīng)...
    沈念sama閱讀 45,775評論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡煮纵,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,945評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了偏螺。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片行疏。...
    茶點(diǎn)故事閱讀 40,096評論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖套像,靈堂內(nèi)的尸體忽然破棺而出酿联,到底是詐尸還是另有隱情,我是刑警寧澤夺巩,帶...
    沈念sama閱讀 35,789評論 5 346
  • 正文 年R本政府宣布贞让,位于F島的核電站,受9級(jí)特大地震影響柳譬,放射性物質(zhì)發(fā)生泄漏喳张。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,437評論 3 331
  • 文/蒙蒙 一美澳、第九天 我趴在偏房一處隱蔽的房頂上張望销部。 院中可真熱鬧,春花似錦制跟、人聲如沸舅桩。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,993評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽擂涛。三九已至,卻和暖如春聊记,著一層夾襖步出監(jiān)牢的瞬間歼指,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,107評論 1 271
  • 我被黑心中介騙來泰國打工甥雕, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人胀茵。 一個(gè)月前我還...
    沈念sama閱讀 48,308評論 3 372
  • 正文 我出身青樓社露,卻偏偏與公主長得像,于是被迫代替她去往敵國和親琼娘。 傳聞我的和親對象是個(gè)殘疾皇子峭弟,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,037評論 2 355

推薦閱讀更多精彩內(nèi)容