(1)在计算机存储信息的最小单位是1个字节(byte),即8个bit,所以能表示的字符范围是0-255个。
(2)人类要表示的符号太多,无法用1个字节来完全表示。
要解决这个矛盾必须要有一个新的数据结构char,而从char到byte必须编码。
在计算机中提供了多种编码方式,常见的有ASCII、ISO-8859-1、GB2312、GBK、UTF-8、UTF-16等。其中GB2312、GBK、UTF-8、UTF-16都可以表示汉子,下面介绍几种编码方式。
ACSII码总共128个,用1个字节的低7位表示,0-31控制字符如回车、换行等,32-126是打印字符串,可以通过键盘输入(键盘上能够找到的)并且能够显示处理。
128个字符显然是不够用的,于是ISO在ASCII的基础上扩展出ISO-8859-1到ISO8859-15,其中ISO-8859-1涵盖了大多数西欧语言字符,所以应用最广泛。8829-1仍然是单字节编码,总共能表示256个字符。
该编码集的全称是《信息技术中文编码字符集》,它是双字节编码,总的编码范围是A1-F7,其中A1-A9是符号区,总共包含682个符号,B0-B7是汉子区,包含6763个汉字。
全称《汉字内码扩展规范》,是为了扩展GB2312,并加入更多的汉字。编码范围是8140-FEFE(去掉XX7F),总共23940个码位,它能表示21003个汉字,它的编码和GB2312兼容,也就是说用GB2312编码的汉字可以用GBK来解码,并且不会乱码。
Unicode(Universal Code 统一码)是ISO试图创建的超语言字典,世界上所有的语言都可以用这个字典来翻译。Unicode 只是一个符号集,它只规定了符号的二进制代码,却没有规定这个二进制代码应该如何存储。其实现方式主流的有UTF-8和UTF-16。
UTF-16具体定义了Unicode字符在计算机中的存取方法。UTF-16用两个字节来表示Unicode的转化格式,它采用定长的表示方法,即不论什么字符都可以使用两个字节来表示。两个字节是16bit,所以叫做UTF-16。UTF-16表示字符串非常方便,每两个字节表示一个字符,这就大大简化了字符串操作,这也是Java以UTF-16作为内存的字符存储格式的一个重要原因。
UTF-16使用两个字节来表示一个字符,有时候一个字节就够用,所以会造成存储空间的浪费,而且会增大网络传输的流量。
UTF-8采用了一种变长技术,每个编码区域有不同的字码长度。不同类型的字符可以由1-6个字节组成。UTF-8有如下规则:
(1)如果是1个字节,最高位(第8位)为0,则表示这是1个ASCII字符(00-7F)可见,所有ASCII编码已经是UTF-8编码了。
(2)如果是1个字节,以11开头,则连续的1的个数暗示这个字符的字节数。例如:110xxxxx代表它是双字节UTF-8的首字节。
(3)如果是1个字节,以10开始,代表它不是首字节,则需要向前查找才能得到当前字符的首字节。
例如:
1字节 0xxxxxxx
2字节 110xxxxx 10xxxxxx
3字节 1110xxxx 10xxxxxx 10xxxxxx
4字节 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
5字节 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
6字节 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
补充:参考网上的一个U8和unicode转换的例子
UTF-8中可以用来表示字符编码的实际位数最多有31位,即上表中x所表示的位。除去那些控制位(每字节开头的10等),这些x表示的位与UNICODE编码是一一对应的,位高低顺序也相同。 实际将UNICODE转换为UTF-8编码时应先去除高位0,然后根据所剩编码的位数决定所需最小的UTF-8编码位数。
下表总结了编码规则,字母x表示可用编码的位。
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,二进制0100111000100101,根据上表,可以发现4E25处在第三行的范围内(0000 0800-0000 FFFF),,因此"严"的UTF-8编码需要三个字节,即格式是"1110xxxx 10xxxxxx 10xxxxxx"。然后,从"严"的最后一个二进制位开始,依次从后向前填入格式中的x,多出的位补0。这样就得到了,"严"的UTF-8编码是"11100100 10111000 10100101",转换成十六进制就是E4B8A5。
验证如下:
import java.math.BigInteger; public class Client { public static void main(String[] args) throws Exception { // Integer.toHexString(‘严‘) 获取unicode码 // 十六进制unicode System.out.println(Integer.toHexString(‘严‘)); // 二进制的unicode System.out.println(hexString2binaryString(Integer.toHexString(‘严‘))); // 二进制的UTF-8 System.out.println(binary("严".getBytes("utf-8"), 2)); // 十六进制的UTF-8 System.out.println(binary("严".getBytes("utf-8"), 16)); } /** * 将byte[]转为各种进制的字符串 * * @param bytes * byte[] * @param radix * 基数可以转换进制的范围,从Character.MIN_RADIX到Character.MAX_RADIX, * 超出范围后变为10进制 * @return 转换后的字符串 */ public static String binary(byte[] bytes, int radix) { return new BigInteger(1, bytes).toString(radix);// 这里的1代表正数 } /** * 16进制转二进制 * * @param hexString * @return */ public static String hexString2binaryString(String hexString) { if (hexString == null || hexString.length() % 2 != 0) return null; String bString = "", tmp; for (int i = 0; i < hexString.length(); i++) { tmp = "0000" + Integer.toBinaryString(Integer.parseInt(hexString.substring(i, i + 1), 16)); bString += tmp.substring(tmp.length() - 4); } return bString; } // 2进制转16进制 public static String binaryString2hexString(String bString) { if (bString == null || bString.equals("") || bString.length() % 8 != 0) return null; StringBuffer tmp = new StringBuffer(); int iTmp = 0; for (int i = 0; i < bString.length(); i += 4) { iTmp = 0; for (int j = 0; j < 4; j++) { iTmp += Integer.parseInt(bString.substring(i + j, i + j + 1)) << (4 - j - 1); } tmp.append(Integer.toHexString(iTmp)); } return tmp.toString(); } }
结果:
4e25
0100111000100101
111001001011100010100101
e4b8a5
关于更多的参考:http://www.ruanyifeng.com/blog/2007/10/ascii_unicode_and_utf-8.html
补充:Little endian 和 Big endian编码方式
以汉字严为例,Unicode 码是4E25,需要用两个字节存储,一个字节是4E,另一个字节是25。存储的时候,4E在前,25在后,这就是 Big endian 方式;25在前,4E在后,这是 Little endian 方式。
第一个字节在前,就是"大头方式"(Big endian),第二个字节在前就是"小头方式"(Little endian)。
那么很自然的,就会出现一个问题:计算机怎么知道某一个文件到底采用哪一种方式编码?
Unicode 规范定义,每一个文件的最前面分别加入一个表示编码顺序的字符,这个字符的名字叫做"零宽度非换行空格"(zero width no-break space),用FEFF表示。这正好是两个字节,而且FF比FE大1。
如果一个文本文件的头两个字节是FE FF,就表示该文件采用大头方式;如果头两个字节是FF FE,就表示该文件采用小头方式。
例如,另一种方式获取unicode码:
System.out.println(binary("严".getBytes("unicode"), 16));
结果:(表示大头存储方式)
feff4e25
原文:https://www.cnblogs.com/qlqwjy/p/12581057.html