JAVA 与字符集

字符集:

ASCII:American Standard Code for Information Interchange,美国信息互换标准代码 

    一个字节,其编码范围是0x00-0x7F,共128个字符。

  最高位为0,低 7 位表示128个字符,包括大小写字母、数字0-9、标点符号、非打印字符(换行符、制表符等4个)以及控制字符(退格、响铃等)组成。

 

ISO-8859-1(扩展字符集):对ASCII扩展,主要支持欧洲使用的语言。

   一个字节,向下兼容ASCII,其编码范围是0x00-0xFF,共256个字符。

   0x00-0x7F之间完全和ASCII一致,0x80-0xFF之间是扩展部分。

GB2312:信息交换用汉字编码字符集, ASCII 的中文扩展,用于中文编码。

  汉字占用二个字节,字母数字占一个字节,是为了兼容ASCII(中国人也需要字母和数字)  

  由于ASCII最高位为0,因此GB2312 规定汉字的两个字节的最高位都是1。 

  第一个字节(高字节)从0xA1用到 0xF7,第二个字节(低字节)从0xA1到0xFE,这样我们就可以组合出大约7000多个简体汉字了,ASCII 里本来就有的数字、标点、字母也都编了两个字节长的编码,这就是常说的”全角”字符,而原来在ASCII码就叫”半角”字符了



 

 文件长度为3个字节,前二位为中文汉字,最后一个字节为字母A。

 GBK: 汉字内码扩展规范

由于GB2312编码不够用,所以出现GBK编码。GBK编码是GB2312的扩展,是向上兼容的,因此GB2312中的汉字的编码与GBK中汉字的相同。

汉字占用二个字节,字母数字占一个字节,是为了兼容ASCII(中国人也需要字母和数字)  

第一个字节(高字节)从0x81用到 0xFE,第二个字节(低字节)从0x40到0xFE(不再要求低字节最高位为1),这样能表示更多的汉字,共23940个码位,共收录了21003个汉字,完全兼容GB2312-80标准,支持国际标准ISO/IEC10646-1和国家标准GB13000-1中的全部中日韩汉字,并包含了BIG5编码中的所有汉字。

 

ASNI编码:

不同的国家和地区制定了不同的标准,由此产生了 GB2312、GBK、Big5、Shift_JIS 等各自的编码标准。这些使用 1 至 4 个字节来代表一个字符的各种汉字延伸编码方式,称为 ANSI 编码。在简体中文Windows操作系统中,ANSI 编码代表 GBK 编码;在日文Windows操作系统中,ANSI 编码代表 Shift_JIS 编码。 

 

 

unicode:

 ”Universal Multiple-Octet Coded Character Set”,简称 UCS

Unicode也是一种字符编码方法,不过它是由国际组织设计,可以容纳全世界所有语言文字的编码方案。Unicode的学名是"Universal Multiple-Octet Coded Character Set",简称为UCS。UCS可以看作是"Unicode Character Set"的缩写。

Unicode用数字0-0x10FFFF来映射这些字符,最多可以容纳1114112个字符,或者说有1114112个码位。

Unicode只是一个符号集,相当于字典,它只规定了符号的二进制代码,却没有规定这个二进制代码应该如何存储。UTF-8UTF-16UTF-32都是将数字转换到程序数据的编码方案。

Unicode 兼容ASCII码,前128个字符与ASCII相同。

UCS-2 unicode的第一版本,用2个字节表示所有语言文字符号,取值范围为 U+0000~U+FFFF。

UCS-4 unicode的第二版本,用4个字节表示所有语言文字符号,它的范围为 U+00000000~U+7FFFFFFF,其中 U+00000000~U+0000FFFF和UCS-2是一样的

UCS-4, UCS-2 和 ASCII是向下兼容的,只要前面补0就可以了。

 

 

 

Big Endian和Little Endian

一个字符编码占多个字节,有高字节和低字节,怎样的排列顺序? 

Big Endian:规定高字节在前

Little Endian:规定低字节在前。

BOM:

BOM是指字节序标志(Byte Order Mark),是为了区分big还是little字节序的,通常在UTF格式文件头位置。

EF BB BF    UTF-8
FE FF     UTF-16/UCS-2, big endian
FF FE     UTF-16/UCS-2, little endian
FF FE 00 00  UTF-32/UCS-4, little endian.
00 00 FE FF  UTF-32/UCS-4, big-endian.  
不带BOM头的UTF文件,默认为 little endian。
 

 

UTF-16 :UCS Transfer Format 16

 UTF-16 用两个字节来表示 Unicode 转化格式,对应UCS-2。UTF-16 表示字符非常方便,每两个字节表示一个字符,这个在字符串操作时就大大简化了操作,这也是 Java 以 UTF-16 作为内存的字符存储格式的一个很重要的原因。

UTF-16需要在文件头上识别字节排列顺序(BOM)。

    
EF BB BF    UTF-8
FE FF     UTF-16/UCS-2, big endian
FF FE     UTF-16/UCS-2, little endian
FF FE 00 00  UTF-32/UCS-4, little endian.
EF BB BF    UTF-8 FE FF     UTF-16/UCS-2, big endian FF FE     UTF-16/UCS-2, little endian FF FE 00 00  UTF-32/UCS-4, little endian. 00 00 FE FF  UTF-32/UCS-4, big-endian.   不带BOM头的UTF文件,默认为 little endian。        UTF-32 :UCS Transfer Format 32  UTF-32 用四个字节来表示 Unicode 转化格式,对应UCS-4。   UTF-32需要在文件头上识别字节排列顺序(BOM)。

UTF-8: UCS Transfer Format 8

UTF-8(8-bit Unicode Transformation Format)是一种针对Unicode的可变长度字符编码,UTF-8用1到6个字节编码UNICODE字符

UTF-16和UTF-32表示ASCII,会浪费很多字节,只有一个字节表示符号其它字节都为0x00。

UTF-8编码规则:

 

 

1)前128个字符,一个字节表示:最高位为0,低七位编码表示128个字符----》与ASCII编码相同。相比UTF-16,UTF-32节省了很多空间。

2)其它字符,用多字节表示,第一个字节高位开始,有几个1就表示该字符由几个字节组成,之后的每个字节的高二位均为10,除了以上控制位,其余位表示UNICODE码。

 

Unicode/UCS-4
bit数
UTF-8
byte数
备注
0000 ~
007F
0~7
0XXX XXXX
1

  
0080 ~
07FF
8~11
110X XXXX
10XX XXXX
2

  
0800 ~
FFFF
12~16
1110XXXX
10XX XXXX
10XX XXXX
3
基本定义范围:0~FFFF
1 0000 ~
1F FFFF
17~21
1111 0XXX
10XX XXXX
10XX XXXX
10XX XXXX
4
Unicode6.1定义范围:0~10 FFFF

 java 内部使用UTF-16表示字符char,字符串String ,StringBuilder:

System.out.println(System.getProperty("file.encoding"));
char asciiA ='A';
System.out.format("A.hex=%x", (short) asciiA);//显示41,自动把第一个字节的0去掉了。
System.out.println();
char zhong = '中';
System.out.format("中.hex=%x", (short) zhong);//对第二个参数(短整型)格式化为十六进制输出,0x开头 //输出4e2d
System.out.println();
char zhong1 = 0x4e2d;//unicode代码数值表示"中"
System.out.println("0x4e2d=" + zhong);
String zhong2 = "\u4e2d";//unicode代码字符串表示
System.out.println("\\u4e2d=" + zhong);//输出"中"
FileOutputStream fos = new FileOutputStream("charset.txt");
fos.write(zhong>>8);
fos.write(zhong);//用字节流输出"中"字的编码,文件中显示:4E 2A
fos.write(asciiA>>8);
fos.write(asciiA); //用字节流输出"A"字的编码,文件中显示:00 41
fos.close();
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream("charset1.txt"),"UTF-16"));
bw.write(zhong);
bw.write(asciiA);
bw.close();
    输出结果:

   

GBK
A.hex=41
中.hex=4e2d
0x4e2d=中
\u4e2d=中
   charset.txt文件二进制视图:

 

    charset1.txt文件二进制视图:




 在文本视图下可以显示出汉字:ucs-2 对应UTF-16

 对比charst.txt文件,本文件只是多了一个UTF-16的BOM头。 

 
 charset.txt 输出代码改为如下,即与charset1一至了:

   FileOutputStream fos = new FileOutputStream("charset.txt");
           fos.write(0xfe);//加上UTF-16 BOM 头 高位在前 Big Endian
           fos.write(0xff);
           fos.write(zhong>>8);
           fos.write(zhong);//用字节流输出"中"字的编码,文件中显示:4E 2A
           fos.write(asciiA>>8);
           fos.write(asciiA); //用字节流输出"A"字的编码,文件中显示:00 41
           fos.close();

 JVM 中文显示过程:

 

  从任何数据源通过指定字符集读取字符,转换为了UTF-16保存在内存中,输出时再转换为指定的字符集输出。只要在jvm内字符的utf-16格式正确,则可以正确输出任何字符集形式。

 

  输入数据源1:java类中写中文

   javac 将java源文件编译为class文件,中文以utf-8存储,jvm启动读取class文件后,中文由UTF-8-->UTF-16。 再根据程序代码指定字符集输出。

   字符集转换:utf-8-->utf-16

  在JAVAC编译的时候,如果没有用-encoding参数指定JAVA源程序的编码格式,则javac.exe首先获得操作系统默认采用的编码格式System.getProperty("file.encoding")(wind7下默认字符集为GBK),认为是该源文件的编码格式。然后根据该字符集

对java源程序编译,源文件中的显示定义的中文,根据字符集读取再转换为UTF-8保存到class文件。

 

 如果编译时使用的字符集与源文件不匹配,则:

   1.编译不通过

    2.以不匹配的字符集读取源文件的中文就乱码了,再换为UTF-8保存到class中也是乱码。

 

 编码时指定源文件编码:javac -encoding GBK Test.java

 

 如果类中写的中文,输出时乱码,则该java源文件编译时指定的字符集不正确。 

 

 java class 文件中字符以UTF-8方式保存。

  

public class TestClass {
   static  String x="中A";
}
编译为class后,查看十六进制:

 E4 B8 AD 是“中”字的UTF-8编码
  41是“A”字的UTF-8编码

 

  输入数据源2:文件

java以字符流方式读取文件时,如果未指定字符集(如FileReader),则使用系统字符集,window下默认为GBK,linux下看系统配置。

  字符集转换:默认或指定字符集-utf-16

如果字符集与源文件不匹配,就会乱码。 

 

java 参数:file.encoding 设置JAVA reader writer IO时默认的字符集(对应读取、写入文件、编译JAVA源文W件)。



 以上为UTF-8格式文件,不指定字符集,则按默认字符集(GBK)读取,为乱码:



 二种方式解决:

  1.在运行时指定-Dfile.encoding=UTF-8 



 

  2.指定读取文件的字符集

BufferedReader br = new BufferedReader( new InputStreamReader(new FileInputStream("c:\\charset.txt"),"UTF-8"));

 

     输入数据源3:数据库

    java 读取数据库是通过jdbc驱动,   驱动由数据库厂商实现,在厂商需要实现ResultSet接口:

   ResultSet API:

Object getObject(int columnIndex)
以 Java 编程语言中 Object 的形式获取此 ResultSet 对象的当前行中指定列的值。
String getString(int columnIndex)
以 Java 编程语言中 String 的形式获取此 ResultSet 对象的当前行中指定列的值。

 

    如果通过ResultSet 获取出来的数据是乱码,则为驱动问题或驱动参数问题 。 

   jdbc驱动以指定字符集(或默认字符集)等方式读取出来的数据,为GBK格式或UTF-8格式,都可以正确的转换为jvm内部的utf-16。 

字符集转换:默认或指定字符集-UTF-16。

 

输出到控制台:System.out

  System.out 对象输出的数据,采用系统默认字符集输出。 

 字符集转换:utf-16 ->默认字符集

输出到文件

  使用FileWriter :采用系统默认字符集。

  或使用指定字符集的Writer对象。

   字符集转换:utf-16 ->默认或指定字符集

输出到数据库:

  字符集转换: utf-16 ->默认或指定字符集

 

   

 

猜你喜欢

转载自java12345678.iteye.com/blog/2261474