ASCII,GB2312,GBK,Unicode,UTF-8字符编码到底是什么?
- 谈到这个问题就不得不说在编码过程中遇到的乱码问题,我想这应该是每一个程序员都头疼的问题。
首先,我们需要思考:
一. 什么是字符?
字符指类字形单位或符号,包括字母、数字、运算符号、标点符号和其他符号,通常由8个二进制位(一个字节)来表示一个字符。 字符是计算机中经常用到的二进制编码形式,也是计算机中最常用到的信息形式。
二.什么是字符集?
字符集简单来说就是字符的集合,它的种类比较多,每种字符集包含的字符个数不同,常见字符集有:ASCII字符集、GB2312字符集、BIG5字符集、GB18030字符集、Unicode字符集等。
三.什么是字符编码?
字符编码也称字集码,是把字符集中的字符编码为指定集合中某一对象(例如:比特模式、自然数序列、8位组或者电脉冲),以便文本在计算机中存储和通过通信网络的传递。常见的例子包括将拉丁字母表编码成摩斯电码和ASCII。其中,ASCII将字母、数字和其它符号编号,并用7比特的二进制来表示这个整数。通常会额外使用一个扩充的比特,以便于以1个字节的方式存储。
- 好了,了解这些基础知识后,我们可以进一步了解常见的字符编码
ASCII
-
我们应该清楚电脑中所有数据其实都是二进制数据,电脑真正能识别的只有0和1,1byte = 8bit,因此总共有2^7=128种表示方式,包括32个字符,其余96位为英文字母和标点符号及运算符号等,其二进制表示范围为00000000-01111111。
GB2312
- GB2312 是最早一版的中文编码,每个字占据2bytes。由于要和ASCII兼容,所以2b字节最高位不可以为0了(否则和ASCII会有冲突)。在GB2312中收录了6763个汉字以及682个特殊符号,已经囊括了生活中最常用的所有汉字。每个汉字及符号以两个字节来表示。第一个字节称为“高位字节”,第二个字节称为“低位字节”,“高位字节”使用了0xA1-0xF7(把01-87区的区号加上0xA0),“低位字节”使用了0xA1-0xFE(把01-94加上 0xA0)。 由于一级汉字从16区起始,汉字区的“高位字节”的范围是0xB0-0xF7,“低位字节”的范围是0xA1-0xFE,占用的码位是 72*94=6768,其中有5个空位是D7FA-D7FE。(摘自百度百科,了解即可)
GBK
-GBK编码,是在GB2312标准基础上的内码扩展规范,使用了双字节编码方案,其编码范围从8140至FEFE(剔除xx7F),共23940个码位,共收录了21003个汉字,完全兼容GB2312标准
Uncoide
- Unicode是计算机科学领域里的一项业界标准,包括字符集、编码方案等,它可以容纳世界上所有文字和符号,Unicode为世界上所有字符都分配了一个唯一的数字编号,这个编号范围从 0x000000 到 0x10FFFF(十六进制),有110多万,每个字符都有一个唯一的Unicode编号,这个编号一般写成16进制,在前面加上U+。具体的符号对硬表可以查看官网。
UTF-8
- UTF-8可以说是我们最常用的编码格式了,之前的Uncoide功能确实强大,包含了世界的所有文字和编号,但它只规定了符号的二进制代码,却没有规定这个二进制代码应该如何存储,直到互联网的普及催生了UTF-8编码方式。
UTF-8 的编码规则很简单,只有二条:对于单字节的符号,字节的第一位设为0,后面7位为这个符号的 Unicode 码。因此对于英语字母,UTF-8 编码和 ASCII 码是相同的。
对于n字节的符号(n > 1),第一个字节的前n位都设为1,第n + 1位设为0,后面字节的前两位一律设为10。剩下的没有提及的二进制位,全部为这个符号的 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 编码非常简单。如果一个字节的第一位是0,则这个字节单独就是一个字符;如果第一位是1,则连续有多少个1,就表示当前字符占用多少个字节。
列:
public static void main(String[] args) throws UnsupportedEncodingException {
String s = "汉";
byte[] bytes = s.getBytes(StandardCharsets.UTF_8);
for (byte b:bytes){
System.out.println("该字节的十进制数str是 : " + b);
System.out.println("该字节的2进制数str是 : "+ (Integer.toBinaryString(b)+""));
System.out.println("该字节的16进制数是 : U+"+ (Integer.toHexString(b)+""));
}
}
结果:
该字节的十进制数str是 : -26
该字节的2进制数str是 : 11111111111111111111111111100110
该字节的16进制数是 : U+ffffffe6
该字节的十进制数str是 : -79
该字节的2进制数str是 : 11111111111111111111111110110001
该字节的16进制数是 : U+ffffffb1
该字节的十进制数str是 : -119
该字节的2进制数str是 : 11111111111111111111111110001001
该字节的16进制数是 : U+ffffff89
由此可见汉是由三个字节组成的,其二进制是11100110 10110001 10001001,转换成16进制为e6b189;
- 针对对常见乱码问题怎样解决?
乱码一般都是string字符串转换错误引起的,我们需要首先了解下string常用的两个编码转换方式getBytes(Charset charset)和new String(byte[] bytes,Charset charset)
/**
* Encodes this {@code String} into a sequence of bytes using the given
* {@linkplain java.nio.charset.Charset charset}, storing the result into a
* new byte array.
*
* <p> This method always replaces malformed-input and unmappable-character
* sequences with this charset's default replacement byte array. The
* {@link java.nio.charset.CharsetEncoder} class should be used when more
* control over the encoding process is required.
*
* @param charset
* The {@linkplain java.nio.charset.Charset} to be used to encode
* the {@code String}
*
* @return The resultant byte array
*
* @since 1.6
*/
public byte[] getBytes(Charset charset) {
if (charset == null) throw new NullPointerException();
return StringCoding.encode(charset, value, 0, value.length);
}
/**
* Constructs a new {@code String} by decoding the specified array of
* bytes using the specified {@linkplain java.nio.charset.Charset charset}.
* The length of the new {@code String} is a function of the charset, and
* hence may not be equal to the length of the byte array.
*
* <p> This method always replaces malformed-input and unmappable-character
* sequences with this charset's default replacement string. The {@link
* java.nio.charset.CharsetDecoder} class should be used when more control
* over the decoding process is required.
*
* @param bytes
* The bytes to be decoded into characters
*
* @param charset
* The {@linkplain java.nio.charset.Charset charset} to be used to
* decode the {@code bytes}
*
* @since 1.6
*/
public String(byte bytes[], Charset charset) {
this(bytes, 0, bytes.length, charset);
}
- getBytes(Charset charset)方法表明的很清楚,用给定的字符编码生成字节数组,并将结果放入新数组里,简单来说就是把我们认识的字变成一连串不认识的编码符;new String(byte[] bytes,Charset charset)通过给定字符编码解码数组并构造新的字符串,简单来说就是把我们不认识的字符变成我们熟悉的字。因此,常见解决办法主要有以下几种:
1.
String msg = request.getParameter("message");
String str = new String(msgs.getBytes(StandardCharsets.ISO_8859_1),StandardCharsets.UTF_8)
2.
request.setCharacterEncoding("UTF-8");
3.
在你的web.xml里加入如下几行:
<filter>
<filter-name>CharacterEncoding</filter-name>
<filter-class>filters.SetCharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>CharacterEncoding</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
4.
打开tomcat的server.xml文件,加入如下一行:URIEncoding=“UTF-8"
完整的应如下:
<Connector
port="80" maxThreads="150" minSpareThreads="25" maxSpareThreads="75"
enableLookups="false" redirectPort="8443" acceptCount="100"
debug="0" connectionTimeout="20000"
disableUploadTimeout="true"
URIEncoding="UTF-8"
/>
原创不易, 如需转载,请注明出处!
——纯生啤酒_