字节、字符串与字符编码

[size=large]Unicode的问题[/size]
需要注意的是,[color=red]Unicode只是一个符号集,它只规定了符号的二进制代码,却没有规定这个二进制代码应该如何存储。[/color]
比如,汉字“严”的unicode是十六进制数4E25,转换成二进制数足足有15位(100111000100101),也就是说这个符号的表示至少需要2个字节。表示其他更大的符号,可能需要3个字节或者4个字节,甚至更多。
这里就有两个严重的问题,[color=red]第一个问题是,如何才能区别unicode和ascii?计算机怎么知道三个字节表示一个符号,而不是分别表示三个符号呢?[/color]第二个问题是,我们已经知道,英文字母只用一个字节表示就够了,如果unicode统一规定,每个符号用三个或四个字节表示,那么每个英文字母前都必然有二到三个字节是0,这对于存储来说是极大的浪费,文本文件的大小会因此大出二三倍,这是无法接受的。
它们造成的结果是:1)[color=red]出现了unicode的多种存储方式,也就是说有许多种不同的二进制格式(utf-8,gbk),可以用来表示unicode[/color]。2)unicode在很长一段时间内无法推广,直到互联网的出现。

Unicode符号范围 | UTF-8编码方式
(十六进制) | (二进制)
--------------------+---------------------------------------------
0000 0000-0000 007F | 0xxxxxxx
0000 0080-0000 07FF | 110xxxxx 10xxxxxx
0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx
0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx

下面,还是以汉字“严”为例,演示如何实现UTF-8编码。

已知“严”的unicode是4E25(100111000100101),根据上表,可以发现4E25处在第三行的范围内(0000 0800-0000 FFFF),因此“严”的UTF-8编码需要三个字节,即格式是“1110xxxx 10xxxxxx 10xxxxxx”。然后,从“严”的最后一个二进制位开始,依次从后向前填入格式中的x,多出的位补0。这样就得到了,“严”的UTF-8编码是“11100100 10111000 10100101”,转换成十六进制就是E4B8A5。


================================================================

首先,要搞清楚 code point 和 encoding 的区别。Java 是遵循 unicode 4.0 标准的,而内部的 character 以 utf-16 作为 encoding。unicode 4.0 标准包含从 U+0000-U+FFFF 的基本多语言平面和 U+10000-U+10FFFF 的扩展平面的文字,这是 code point。Java 的 char 类型是 16 bit 的,所以单个 char 只支持基本平面内的文字,而扩展平面的文字是由一对 char 来表示的。

而 String.getBytes() 这个方法是按照指定的 encoding 返回字符串,一般中文系统的默认编码是 utf-8 (linux, mac) 或者 gbk/gb18030 (windows)。只要是基本平面内的文字,utf-8码的中文都是3字节的,而 gbk/gbk18030 是2字节的。
================================================================



char c='你';
System.out.print(Integer.toHexString(c));
System.out.println(" "+Integer.toBinaryString(c));
String s=Character.toString(c);
//用utf-8进行编码后的字节数组内容
for(byte b:s.getBytes("utf-8")){
System.out.print(Integer.toHexString(b));
System.out.println(" "+Integer.toBinaryString(b));
}
System.out.println("---------------");
//用gb2312进行编码后的字节数组内容
byte[] gbb = new String(s.getBytes("utf-8"),"utf-8").getBytes("gb2312");
for(byte b:gbb){
System.out.print(Integer.toHexString(b));
System.out.println(" "+Integer.toBinaryString(b));
}

//从Unicode转化为byte[]
s = new String(gbb,"gb2312");//由于gbb字节数组是经过gb2312编码,必须先解码还原成为Unicode
ByteBuffer byteBuffer = ByteBuffer.allocate(6);
CharBuffer charBuffer = byteBuffer.asCharBuffer();
charBuffer.put(s);
//用asCharBuffer()方法创建的视图与原buffer的位置、界限和标记值是相互独立的
//例如这里,byteBuffer.position()==0,所以要先设置position再flip,
byteBuffer.position(charBuffer.position()*2);
System.out.println(byteBuffer);
byteBuffer.flip();
System.out.println(byteBuffer);
System.out.println(charBuffer.flip());

while(byteBuffer.hasRemaining()){
byte b = byteBuffer.get();
System.out.print(Integer.toHexString(b));
System.out.println(" "+Integer.toBinaryString(b));
}

输出如下:

4f60 100111101100000
ffe4 11111111111111111111111111100100
ffbd 11111111111111111111111110111101
ffa0 11111111111111111111111110100000
---------------
ffffffc4 11111111111111111111111111000100
ffffffe3 11111111111111111111111111100011
java.nio.HeapByteBuffer[pos=2 lim=6 cap=6]
java.nio.HeapByteBuffer[pos=0 lim=2 cap=6]

4f 1001111
60 1100000


[size=large]ByteBuffer的asCharBuffer方法[/size]

[size=large]asCharBuffer[/size]
public abstract CharBuffer asCharBuffer()创建此字节缓冲区的视图,作为 char 缓冲区。
新缓冲区的内容将从此缓冲区的当前位置开始。此缓冲区内容的更改在新缓冲区中是可见的,反之亦然;这两个缓冲区的位置、界限和标记值是相互独立的。

新缓冲区的位置将为零,其容量和界限将为此缓冲区中所剩余的字节数的二分之一,其标记是不确定的。当且仅当此缓冲区为直接时,新缓冲区才是直接的,当且仅当此缓冲区为只读时,新缓冲区才是只读的。


返回:
新的 char 缓冲区
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值