[Java] java 的字符集和编码方案,
[Java] java 的字符集和编码方案,
Java使用内在的Unicode 表示字符。Unicode 试图用两个字节来表示世界上的所有字符。 这一点可以从java.nio.charset.CharsetEncoder 类中得到证明,他的encode 方法只接受CharBuffer 类型的参数。在java 中一个Char 等于两个字节。Unicode 的固有支持大大的简化了字符数据处理,但决不是自动的处理字符。您仍需要理解字符映射的工作原理以及如何处理多个字符集。
下面是4 个基本概念:
1 Character set(字符集) 字符的集合,也就是,带有特殊语义的符号。字母“A”是一个字符。“%”也是一个字符。没有内在数字价值,与ASCII,Unicode没关系,甚至和电脑也没有任何的直接联系。在电脑产生前,符号就已经存在了。(这里指的就是语言文字,如汉字"我",可以理解为中文的一个字符)
2 Coded character set(被编码的字符集合) 为每个字符集中的字符都指定一个数值(如字符A,可能用数值‘32’来表示)。可以对同一个字符集指定多个被编码的字符集。实际上“被编码的字符集” 也可以被称为字符集映射(Character set mapping),因为它把字符集和数值映射起来了。一般字符集映射由标准的组织定义的,如USASCII, ISO 8859-1, Unicode (ISO 10646-1), and JIS X0201 都是被编码的字符集合。
3 Character-encoding scheme (字符编码方案)是一个一个映射,把被编码的字符集合中的数字(这个数字表示一个字符)映射到八位字节(8 bit字节)的数组中。编码方案定义了如何把字符编码的序列表达为字节序列。字符编码的数值不需要与编码字节相同,也不需要是一对一或一对多个的关系。原则上,把字符集编码和解码近似视为对象的序列化和反序列化。
4 什么是 Charset: Charset 是一个专有名词,他包括 1 被编码的字符集 和 2编码方案。在java 中,用 java.nio.charset 包中的 Charset 类来表示。
可以这样理解: 字符集天然存在,如我们的汉语中的字。但是计算机只能处理0和1,所以必须对字符集编码,变成数字,计算机就能存储和识别了,这样计算机就知道是那个字了。但是为什么要存在字符编码方案呢?
原因有两点:
1 虽然我们对字符编码了,但是当需要存储或在网络上传输字符的时候,以这种编码来做合适吗?不一定合适吧。
2 目前的操作系统都是面向字节的,所以在字符集编码和字节序列之间需要一中转换。
下面来看看例子:
package aaa;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
public class CharsetTest {
/**
* @param args
*/
public static void main(String[] args) {
// String input = "\u00bfMa\u00f1ana?";
String input = "我我a";
String[] charsetNames = { "US-ASCII", "ISO-8859-1", "UTF-8",
"UTF-16BE", "UTF-16LE", "UTF-16" // , "X-ROT13"
};
for (int i = 0; i < charsetNames.length; i++) {
doEncode(Charset.forName(charsetNames[i]), input);
}
}
private static void doEncode(Charset cs, String input) {
cs.newEncoder();
ByteBuffer bb = cs.encode(input);
System.out.println("Charset: " + cs.name());
System.out.println(" Input: " + input);
System.out.println("Encoded: ");
for (int i = 0; bb.hasRemaining(); i++) {
int b = bb.get( );
int ival = ((int) b) & 0xff;
char c = (char) ival;
// Keep tabular alignment pretty
if (i < 10)
System.out.print(" ");
// Print index number
System.out.print(" " + i + ": ");
// Better formatted output is coming someday...
if (ival < 16)
System.out.print("0");
// Print the hex value of the byte
System.out.print(Integer.toHexString(ival));
// If the byte seems to be the value of a
// printable character, print it. No guarantee
// it will be.
if (Character.isWhitespace(c) || Character.isISOControl(c)) {
System.out.println("");
} else {
System.out.println(" (" + c + ")");
}
}
System.out.println ("");
}
}
运行代码,可以在控制台上看到如下结果:
Charset: US-ASCII
Input: 我我a
Encoded:
0: 3f (?)
1: 3f (?)
2: 61 (a)
// 分析: 因为ASCII 只识别char 中的最后7位,所以三个字符产生了三个字节。a 是ASCII 的字符,所以可以识别,正确编码。
Charset: ISO-8859-1
Input: 我我a
Encoded:
0: 3f (?)
1: 3f (?)
2: 61 (a)
Charset: UTF-8
Input: 我我a
Encoded:
0: e6 (æ)
1: 88
2: 91
3: e6 (æ)
4: 88
5: 91
6: 61 (a)
// 分析: UTF-8 是不定长的编码,他把下于0x80 的字符(这里指java 的字符,占两个字节)编码成一个字节,而大于0x80 的字符编码成2到6个字节。本例中,把“我”编码成3个字节,"a" 编码成1个字节。
Charset: UTF-16BE
Input: 我我a
Encoded:
0: 62 (b)
1: 11
2: 62 (b)
3: 11
4: 00
5: 61 (a)
// 分析: UTF-16 系列,把字符编码为2个字节。 所以共6个字节。
Charset: UTF-16LE
Input: 我我a
Encoded:
0: 11
1: 62 (b)
2: 11
3: 62 (b)
4: 61 (a)
5: 00
Charset: UTF-16
Input: 我我a
Encoded:
0: fe (þ)
1: ff (ÿ)
2: 62 (b)
3: 11
4: 62 (b)
5: 11
6: 00
7: 61 (a)
// 分析: 由于使用UTF-16,没有指定编码后两个字节的顺序,所以多了两个字节,表示采用字节顺序。fe ff 指定了顺序。
我的对java 的编码过程也不是很理解,欢迎大家讨论!
参考: <<Java NIO >> 一书
相关文章
- 暂无相关文章
用户点评