1. 程式人生 > >Java web 中的中文亂碼問題總結

Java web 中的中文亂碼問題總結

一、亂碼由來:

       大家都知道一句話:“亂碼是因為編碼和解碼方式不一致造成的”。 那麼,我們為什麼要編碼呢?

       那就要從計算機底層是如何表示人類語言(包括字母、中文文字、其他符號等等)說起了:

       字元:人們使用的記號,抽象意義上的一個符號。 '1', '中', 'a', '$', '¥', ……

       位元組:

計算機中儲存資訊的最小單元是1個位元組(byte),即8個bit(二進位制,要麼是1,要麼是0),假設用一個位元組表示人類語言的字元,最多也就可以展示2的8次方=256個,要表示世界上那麼多種字元,這明顯遠遠不夠用。

       所以要計算機表示和人類語言符號統一起來,就需要編碼,也就是從字元到位元組或者從位元組到字元的轉換過程,具體各種編碼格式、編碼規則又有不同:

        1、ASCII碼(American Standard Code for Information Interchange,美國資訊交換標準程式碼):用1個位元組的低7位來表示共計128個字元,是基於

拉丁字母的一套電腦編碼系統,主要用於顯示現代英語和其它西歐語言。它是現今最通用的單位元組編碼系統,並等同於國際標準ISO/IEC 646。  

如下表,有了這個統一的標準之後,大家讀到計算機中某個位元組是:01000001,那麼我們就知道它代表的是大寫字母A;

Bin(二進位制)

Oct(八進位制)

Dec(十進位制)

Hex(十六進位制)

縮寫/字元

解釋

0000 0000

0

0

00

NUT(null)

空字元

0000 0001

1

1

01

SOH(start of headline)

標題開始

0000 0010

2

2

02

STX (start of text)

正文開始

0000 0011

3

3

03

ETX (end of text)

正文結束

0000 0100

4

4

04

EOT (end of transmission)

傳輸結束

0000 0101

5

5

05

ENQ (enquiry)

請求

0000 0110

6

6

06

ACK (acknowledge)

收到通知

0000 0111

7

7

07

BEL (bell)

響鈴

0000 1000

10

8

08

BS (backspace)

退格

0000 1001

11

9

09

HT (horizontal tab)

水平製表符

0000 1010

12

10

0A

LF (NL line feed, new line)

換行鍵

0000 1011

13

11

0B

VT (vertical tab)

垂直製表符

0000 1100

14

12

0C

FF (NP form feed, new page)

換頁鍵

0000 1101

15

13

0D

CR (carriage return)

回車鍵

0000 1110

16

14

0E

SO (shift out)

不用切換

0000 1111

17

15

0F

SI (shift in)

啟用切換

0001 0000

20

16

10

DLE (data link escape)

資料鏈路轉義

0001 0001

21

17

11

DC1 (device control 1)

裝置控制1

0001 0010

22

18

12

DC2 (device control 2)

裝置控制2

0001 0011

23

19

13

DC3 (device control 3)

裝置控制3

0001 0100

24

20

14

DC4 (device control 4)

裝置控制4

0001 0101

25

21

15

NAK (negative acknowledge)

拒絕接收

0001 0110

26

22

16

SYN (synchronous idle)

同步空閒

0001 0111

27

23

17

ETB (end of trans. block)

結束傳輸塊

0001 1000

30

24

18

CAN (cancel)

取消

0001 1001

31

25

19

EM (end of medium)

媒介結束

0001 1010

32

26

1A

SUB (substitute)

代替

0001 1011

33

27

1B

ESC (escape)

換碼(溢位)

0001 1100

34

28

1C

FS (file separator)

檔案分隔符

0001 1101

35

29

1D

GS (group separator)

分組符

0001 1110

36

30

1E

RS (record separator)

記錄分隔符

0001 1111

37

31

1F

US (unit separator)

單元分隔符

0010 0000

40

32

20

(space)

空格

0010 0001

41

33

21

!

歎號

0010 0010

42

34

22

"

雙引號

0010 0011

43

35

23

#

井號

0010 0100

44

36

24

$

美元符

0010 0101

45

37

25

%

百分號

0010 0110

46

38

26

&

和號

0010 0111

47

39

27

'

閉單引號

0010 1000

50

40

28

(

開括號

0010 1001

51

41

29

)

閉括號

0010 1010

52

42

2A

*

星號

0010 1011

53

43

2B

+

加號

0010 1100

54

44

2C

,

逗號

0010 1101

55

45

2D

-

減號/破折號

0010 1110

56

46

2E

.

句號

00101111

57

47

2F

/

斜槓

00110000

60

48

30

0

數字0

00110001

61

49

31

1

數字1

00110010

62

50

32

2

數字2

00110011

63

51

33

3

數字3

00110100

64

52

34

4

數字4

00110101

65

53

35

5

數字5

00110110

66

54

36

6

數字6

00110111

67

55

37

7

數字7

00111000

70

56

38

8

數字8

00111001

71

57

39

9

數字9

00111010

72

58

3A

:

冒號

00111011

73

59

3B

;

分號

00111100

74

60

3C

<

小於

00111101

75

61

3D

=

等號

00111110

76

62

3E

>

大於

00111111

77

63

3F

?

問號

01000000

100

64

40

@

電子郵件符號

01000001

101

65

41

A

大寫字母A

01000010

102

66

42

B

大寫字母B

01000011

103

67

43

C

大寫字母C

01000100

104

68

44

D

大寫字母D

01000101

105

69

45

E

大寫字母E

01000110

106

70

46

F

大寫字母F

01000111

107

71

47

G

大寫字母G

01001000

110

72

48

H

大寫字母H

01001001

111

73

49

I

大寫字母I

01001010

112

74

4A

J

大寫字母J

01001011

113

75

4B

K

大寫字母K

01001100

114

76

4C

L

大寫字母L

01001101

115

77

4D

M

大寫字母M

01001110

116

78

4E

N

大寫字母N

01001111

117

79

4F

O

大寫字母O

01010000

120

80

50

P

大寫字母P

01010001

121

81

51

Q

大寫字母Q

01010010

122

82

52

R

大寫字母R

01010011

123

83

53

S

大寫字母S

01010100

124

84

54

T

大寫字母T

01010101

125

85

55

U

大寫字母U

01010110

126

86

56

V

大寫字母V

01010111

127

87

57

W

大寫字母W

01011000

130

88

58

X

大寫字母X

01011001

131

89

59

Y

大寫字母Y

01011010

132

90

5A

Z

大寫字母Z

01011011

133

91

5B

[

開方括號

01011100

134

92

5C

\

反斜槓

01011101

135

93

5D

]

閉方括號

01011110

136

94

5E

^

脫字元

01011111

137

95

5F

_

下劃線

01100000

140

96

60

`

開單引號

01100001

141

97

61

a

小寫字母a

01100010

142

98

62

b

小寫字母b

01100011

143

99

63

c

小寫字母c

01100100

144

100

64

d

小寫字母d

01100101

145

101

65

e

小寫字母e

01100110

146

102

66

f

小寫字母f

01100111

147

103

67

g

小寫字母g

01101000

150

104

68

h

小寫字母h

01101001

151

105

69

i

小寫字母i

01101010

152

106

6A

j

小寫字母j

01101011

153

107

6B

k

小寫字母k

01101100

154

108

6C

l

小寫字母l

01101101

155

109

6D

m

小寫字母m

01101110

156

110

6E

n

小寫字母n

01101111

157

111

6F

o

小寫字母o

01110000

160

112

70

p

小寫字母p

01110001

161

113

71

q

小寫字母q

01110010

162

114

72

r

小寫字母r

01110011

163

115

73

s

小寫字母s

01110100

164

116

74

t

小寫字母t

01110101

165

117

75

u

小寫字母u

01110110

166

118

76

v

小寫字母v

01110111

167

119

77

w

小寫字母w

01111000

170

120

78

x

小寫字母x

01111001

171

121

79

y

小寫字母y

01111010

172

122

7A

z

小寫字母z

01111011

173

123

7B

{

開花括號

01111100

174

124

7C

|

垂線

01111101

175

125

7D

}

閉花括號

01111110

176

126

7E

~

波浪號

01111111

177

127

7F

DEL (delete)

刪除

        2、ISO-8859-1:  ISO組織在ASCII碼基礎上擴充套件到256個字元,制定的標準,涵蓋了大多數西歐語言字元。

        3、GB2312:全稱是《資訊交換用漢字編碼字符集》,是由中國國家標準總局1980年釋出,1981年5月1日開始實施的一套國家標準;採用雙位元組編碼,共包含682個符號和6763個漢字。

        4、GBK:全稱《漢字內碼擴充套件規範》(GBK即“國標”、“擴充套件”漢語拼音的第一個字母),擴充套件的GB2312,向下相容GB2312,也就是說,GB2312編碼的,用GBK解碼是沒問題的,不會出現亂碼;

         5、GB18030:應用不廣泛,相關資料後續有機會補上;

         6、UTF-16: 說到UTF(Unicode TransferFormat :把Unicode轉做某種格式),就必須先說一說Unicode(Universal code 統一碼),Unicode是ISO組織建立的一個世界統一性的字典,但是不是具體的編碼格式標準。  UTF-16是一個實現版本,但是它使用定長2個位元組來表示所有的字元。值得一提的是,定長表示大大簡化了字串的操作,很適合本地磁碟和記憶體之間使用,這是Java以UTF-16作為記憶體的字元儲存格式的一個重要原因。

          7、UTF-8: 雖然UTF-16在字元表示上簡單方便,但是在網路傳輸中同時也造成了儲存空間的浪費、網路傳輸流量的增加,這是其缺點。

UTF-8採用了變長的技術,不同型別的字元,可以由1~6個字元來表示,適合網路傳輸。基本編碼規則如下:

小結:   這下我們就知道亂碼怎麼來的:假設某字元是用GBK的編碼規則編碼的,如果我用ISO-8859-1的規則去解碼,那麼原本GBK兩個位元組代表的一個字元,在ISO-8859-1的規則下就會拆成2個單獨的位元組,也就不可能正確翻譯出原來的字元是什麼了。

二、Java中亂碼產生的可能情況(需要字元和位元組轉換的場景):

        1、I/O操作:包括磁碟I/O和網路I/O(關於Java中處理I/O問題,我將會以另一篇博文呈現)

        2、在記憶體中操作的編碼:也就是資料型別轉換操作。

             如下,從列印結果可以看到,編碼格式保持一致的重要性。

String str = "我是中文";
byte[] b = str.getBytes("UTF-8");

String str1 = new String(b,"UTF-8");
System.out.println(str1);  //我是中文

String str2 = new String(b,"GBK");
System.out.println(str2);//鎴戞槸涓枃

 

三、後記:

Java web  中涉及的編解碼,同樣後面會在另外一篇博文中呈現。

 

相關部落格:Eclipse console 輸出中文亂碼問題