Java字符编码

今天在往oracle数据库里插入数据时发现,往一个20字节的字段里插入8个汉字加上一个括号,并没有提示字段超长。猜想数据库应该并没有用万国码(utf-8)。

查询数据库编码sql:select * from nls_database_parameters where parameter ='NLS_CHARACTERSET';

蹦出来这么一个玩意,是该好好捋一捋的时候了

字节、位的关系

字节(byte) 
二进制位,懂点计算机的都知道,计算机是不认识汉字,字母等等的,它只认识0和1。0和1代表了两种状态,高电平,低电平。。。(这儿不详细叙述),所以位是计算机内部数据储存的最小单位。

   位( bit)

字节是计算机中数据处理的基本单位。
字节的标准定义:一个字节均为8位。由于上述所讲每个位或者是0或者是1,所以一个8位的字节包含256种可能的0,1组合。列如,int 占用4(byte),它就占用32位,理论上它能表示2^32,但
是它还需要一个符号位,和一个0。它就只能表示-2^31 ~ 2^31 - 1之间的整数,所以:
int a;
a+1<a;//这种情况是存在的的

 GBK编码

      前面我们说到,计算机只能识别0和1,那要处理字符和文本怎么处理呢?那就必须先把文本转换为数字才能处理。众所周知,计算机是美国人发明的玩意,他最开始根本就没想过带第三世界的人民玩,最开始的计算机只有8位。八位的字节一共可以组合出256(2的8次方)种不同的状态。他们就把所有的大小写字符、数字、标点符号、空格用连续的字节状态表示。这就是最开始的ASCII码,比如大写字母A的编码是65,

小写字母z的编码是122。后来欧美其他一些国家也加入,就有了从128到255这一页的”扩展字符集“。等到了中国人加入的时候已经没有字符可以使用了,况且中国有十多万汉字,常用汉字都有6000多,我们就

保留了1-127号的字符,但两个大于127的字符连在一起时,就表示一个汉字,前面的一个字节(他称之为高字节)从0xA1用到0xF7,后面一个字节(低字节)从0xA1到0xFE,这样我们就可以组合出大约7000多

个简体汉字了,这就叫做叫做 GB2312。而7000多个汉字显然是不够的,后来在GB2312的基础上进行了扩展,结果扩展之后的编码方案被称为 GBK 标准。所以这里一个汉字算两个英文字符,即汉字占用两个字节,英文占用一个字节。

 Unicode编码

 到这里你可以想象了,中国人自己搞了一套gbk,韩国人搞一套Euc-kr,冲突就不可避免了,这就出现了乱码。这时Unicode(统一码、万国码、单一码)出现了,它将所有的语言符号都包含了进去,unicode统一用两个字节,也就是16位来统一表示所有的字符。所以Unicode中一个字符就是一般的两个字节。

如果把ASCII编码的A用Unicode编码,只需要在前面补0就可以,因此,A的Unicode编码是00000000 01000001

前面Unicode解决了乱码的问题,但是英语只需要用到8位,高8位永远是0,因为Unicode在保存英文时会多浪费掉一倍的空间,虽然现在硬盘或者内存都很廉价,但是在网络传输中,这个问题就凸显出来了,你可以这样想想,本来1M的带宽在ANSI下可以代表1024*1024个字符,但是在Unicode下却只能代表1024*1024/2个字符。也就是1MB/s的带宽只能等价于512KB/s。为解决unicode如何在网络上传输的问题,于是面向传输的众多 UTF(UCS Transfer Format)标准出现了,顾名思义,UTF-8就是每次8个位传输数据,而UTF-16就是每次16个位。

但是这样又导致了一个问题,虽然UTF-8可以使用一个字节来表示ANSI下的符号,但是对于其它类似汉语的符号,得需要两个字节来表示,所以计算机不知道如何去截取一个符号,也就是一个符号对应的二进制的截取开始位置和截取结束位置。所以为了解决Unicode下的ANSI符号的空间浪费和网络传输下如何截取字符的问题,UTF规定:如果一个符号只占一个字节,那么这个8位字节的第一位就为0。如果为两个字节,那么规定第一个字节的前两位都为1,然后第一个字节的第三位为0,第二个字节的前两位为10,然后如果是三个字节的话,那么第一个字节的前三位为111,第四位为0,剩余的两个字节的前两位都为10。按照这样的算法去思考一个中文字符的UTF-8是怎么表示的:一个中文字符需要两个字节来表示,两个字节一共是16位,那么UTF-8下,两个字节是不够的,因为两个字节下,第一个字节已经占据了三位:110,然后剩余的一个字节占据了两位:10,现在就只剩下11位,与Unicode下的两个字节,16位去表示任意一个字符是相悖的。所以就使用三个字节去表示非ANSI字符:三个字节下,一共是24位,第一个字节头四位是:1110,后两个字节的前两位都是:10,那么24位-8位=16位,刚好两个字节去表示Unicode下的任意一个非ANSI字符。这也就是为什么UTF-8需要使用三个字节去表示一个非ANSI字符的原因了!

最后在安利点干货:

其中用得最广的utf-8 是一种变长的编码方式。它可以使用1~4个字节表示一个符号,根据不同的符号而变化字节长度,当字符在ASCII码的范围时,就用一个字节表示,保留了ASCII字符一个字节的编码做为它的一部分,注意的是unicode一个中文字符占2个字节,而UTF-8一个中文字符占3个字节)。从unicode到uft-8并不是直接的对应,而是要过一些算法和规则来转换。

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字符到内存里,编辑完成后,保存的时候再把Unicode转换为UTF-8保存到文件:

所以你看到很多网页的源码上会有类似的信息,表示该网页正是用的UTF-8编码。

  题外话:

  然,中国的汉字多达10多万,常用的汉字3500左右[08年统计],如果用3个字节来表示,一共只有2^16(65535)种可能,不足以表示10多万的汉字。所以中日韩的超大字符集是采用的4个字节来表示的,多达6万多个。但是平时使用超大字符集的概率0.01%都不到。所以我们一般认为日常的中文在UTF-8中占三个字节 即可!

多个字节提供的位数超过了所需要的,多余的位以0补全到编码前面

 参考:http://blog.chinaunix.net/uid-12014716-id-4152047.html

            http://www.cnblogs.com/web21/p/6092414.html





 
 



 



 

原文地址:https://www.cnblogs.com/magic101/p/7800891.html