必须知道的几个简单概念:
1. unicode:字符集,包含了全世界几乎所有的字符。解除了ascii、iso-8859-1等字符集的局限。
2. unicode码:与每一个字符相对应的数字,一对一映射,常用的BMP区段范围在0x0000—0xffff。编码时,通过字符找到数字,解码时,相反。
3. utf-16:编码方式,将unicode中的每个字符转换成两个字节,并与BMP保持一致。由于编码方式简单,java内部采用utf-16的编码字符。
4. utf-8:编码方式,将字符转化为1-4个字节。如英文字母会用一个字节来表示,汉字则会使用三个字节来表示。
5. gbk:字符集、编码方式。gbk拥有自己的字符集以及对应的编码方式。向下兼容gb2312,而gb18030兼容了gbk。
6. ... ...
现在来看看我们常用的一个编码方法:String.getBytes(String charset);它具体做了什么。
1. charset指定了字符集以及使用的编码方式,比如传入:gbk。
//关键代码
Charset cs = lookupCharset(csn); // 这里会找到sun.nio.cs.ext.GBK
if (cs != null)
se = new StringEncoder(cs, csn); // 最终通过sun.nio.cs.ext.GBK.newEncoder()方法返回gbk的encoder
return se.encode(ca, off, len);
2. 通过调用encoder的ecode方法。
//关键代码
CoderResult cr = encodeLoop(in, out);// in为刚传入的字符序列,out为即将返回的字节序列
3. gbk ecoder的encodeLoop方法在其父类DoubleByteEncoder中实现。
protected CoderResult encodeLoop(CharBuffer src, ByteBuffer dst) {
if (true && src.hasArray() && dst.hasArray())
return encodeArrayLoop(src, dst);
else
return encodeBufferLoop(src, dst);
}
4. 按照array和buffer两种形式去编码,但是本质都是一样的,看其中的一个:encodeBufferLoop。
while (src.hasRemaining()) {
char c = src.get();
int b = encodeSingle(c);
if (b != -1) { // Single-byte character
if (dst.remaining() < 1)
return CoderResult.OVERFLOW;
mark++;
out.put((byte)b);//对单字节的处理,直接返回b对应的字节
continue;
}
// Double Byte character
int ncode = encodeDouble(c);
if (ncode != 0 && c != '\u0000') {
if (dst.remaining() < 2)
return CoderResult.OVERFLOW;
mark++;
//对双字节字符的处理
//第一个字节通过ncode的高8位组成
out.put((byte) ((ncode & 0xff00) >> 8));
//第二个字节由低8位组成
out.put((byte) ncode);
continue;
}
}
5. 那么以上的int类型的b和ncode是怎么来的呢?
protected int encodeSingle(char inputChar) {
if (inputChar < 0x80) // inputChar对应的int值小于128,即:1000 0000
return (byte)inputChar;
else
return -1;
}
protected int encodeDouble(char ch) {
int offset = index1[((ch & 0xff00) >> 8 )] << 8; //关注index1
//关注index2
return index2[offset >> 12].charAt((offset & 0xfff) + (ch & 0xff));
}
6.如何判断单字节还是双字节?
通过unicode码表,找到字符inputChar以及其对应的unicode码,例如:'一'对应的值为0x4E00。如果这个值小于0x80,则为单字节字符,否则为双字节。
如果是单字节字符,直接返回该unicode码的低8位。
如果为双字节字符,则通过index1和index2来定位对应的值。
7. index1和index2分别是什么?
在gbk encoder中分别定义了index1和index2,用来找到unicode码对应的gbk编码。
index2 由7个字符串组成,每个字符串定义了不同的gbk编码,形式如下:
private final static String innerIndex0=
"\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000"+...+
"\uA8A8\uA8A6\uA8BA\u0000\uA8AC\uA8AA\u0000\u0000"+...+
"\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000";
private final static String innerIndex1=
... ....
private final static String innerIndex6=
private final static String index2[] = {
innerIndex0,
innerIndex1,
innerIndex2,
innerIndex3,
innerIndex4,
innerIndex5,
innerIndex6
};
index1主要用于定位index2中的字符,代码如下:
private final static short index1[] = {
1, 2, 3, 4, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
6, 7, 8, 9, 10, 11, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0,
... ...
0, 0, 0, 0, 0, 0, 0, 0, 0, 108, 109, 0, 0, 0, 110, 111
};
这样GBK编码的整个过程就完成了(其中只展示了关键步骤)。
再来看看utf-8的编码过程有什么不一样呢,直接看sun.nio.cs.UTF_8.encodeBufferLoop方法。
private CoderResult encodeBufferLoop(CharBuffer src,
ByteBuffer dst)
{
while (src.hasRemaining()) {
int c = src.get(); // unicode码
if (c < 0x80) { // 128个US-ASCII字符只需一个字节编码
dst.put((byte)c);
} else if (c < 0x800) {
// 2 bytes, 11 bits
// 带有附加符号的拉丁文、希腊文、西里尔字母、亚美尼亚语、希伯来文、阿拉伯文、叙利亚文及它拿字母则需要二个字节编码
// 以11开头,代表由双字节组成
dst.put((byte)(0xc0 | ((c >> 06))));
dst.put((byte)(0x80 | (c & 0x3f)));
} else if (Surrogate.is(c)) {
// 极少使用的Unicode 辅助平面的字符使用四字节编码
// Have a surrogate pair
if (sgp == null)
sgp = new Surrogate.Parser();
int uc = sgp.parse((char)c, src);
// 以1111开头,代表由4个字节组成
dst.put((byte)(0xf0 | ((uc >> 18))));
dst.put((byte)(0x80 | ((uc >> 12) & 0x3f)));
dst.put((byte)(0x80 | ((uc >> 06) & 0x3f)));
dst.put((byte)(0x80 | (uc & 0x3f)));
mark++; //2 chars
} else {
// 3 bytes, 16 bits
// 其他BMP中的字符(这包含了大部分常用字)使用三个字节编码,中文字符一般使用3个字节编码。
// 以111开头,代表由三个字节字节组成
dst.put((byte)(0xe0 | ((c >> 12))));
dst.put((byte)(0x80 | ((c >> 06) & 0x3f)));
dst.put((byte)(0x80 | (c & 0x3f)));
}
}
return CoderResult.UNDERFLOW;
}
可以看到,utf-8并没有像gbk中index1和index2的定义。utf-16也是一样,也就是说utf编码并不会去查询其他的码表。类似gbk编码方式的还有很多,例如:韩文编码等。从这里可以看出,utf-8编码的方式更加简单,少了查询码表的过程,所以性能会更高一些。但是,对于中文等字符,utf-8采用3个字节来存储,占用了更多的存储空间,用于网络传输会占用更大的带宽,所以各有利弊。
utf-16则采用了unicode默认的编码方式,即直接将unicode码放入两个字节中,并分为big-endian和little-endian的字节序的存储方式,需要根据不同的硬件或者操作系统选择。
当然,解码可以通过编码的逆向过程来得到对应的字符。到此为止,对字符集和编码的原理有了基本的认识。
分享到:
相关推荐
这里通过`getUTF8Bytes()`方法获取UTF-8编码的字节数组,以确保在无法使用指定编码时可以回退到UTF-8编码。 ### 2. 自定义ZipInputStream类(CnZipInputStream) #### (1) 创建并修改CnZipInputStream类 同样地,从...
在计算机系统中,汉字是通过特定的编码方式来表示的,例如GBK、GB2312或Unicode(UTF-8)等。当Java程序在读取、处理或输出汉字时,如果编码不一致,就会出现乱码。例如,一个使用GBK编码的文本文件被当作UTF-8编码...
从ASCII码表到GBK再到Unicode及其衍生的编码方案(如UTF-8),每一次进步都旨在更好地支持国际化的需求。在编程实践中,熟练掌握字符集相关的编码与解码操作,有助于解决多种场景下的文本处理问题。
1. 文件转换:支持批量转换GB18030编码的文本文件到Unicode(如UTF-8),反之亦然。 2. 字符查看:允许用户查看和比较不同编码下的字符表示,以便理解转换过程。 3. 预览与校验:在转换前后提供预览功能,确保转换...
另一方面,Android操作系统基于Linux内核,其默认采用的是Unicode编码,最常见的是UTF-8,这是一种多字节编码方式,能够支持全球大部分语言的字符集,包括ASCII在内。因此,当在Android平台上开发或处理文本时,通常...
- 在编写代码时,推荐始终使用Unicode编码(如UTF-8),以保证跨平台和兼容性。 - 在处理文本输入输出时,使用`InputStreamReader`和`OutputStreamWriter`配合指定的字符集进行读写操作,避免乱码。 通过以上步骤...
- **高效性**:虽然UTF-8不是固定长度的编码方式,但它对英文等常用字符的编码效率较高,对于非英文字符,尽管占用更多空间,但仍然是一种高效的编码方案。 ##### 3.2 UTF-8解决方案示例 在实际应用中,可以将整个...
如果源文件中包含中文字符,则需确保该文件的编码格式与Java程序期望的一致,通常推荐使用UTF-8编码来避免潜在的问题。 ##### 2. 编译阶段 当使用`javac`命令编译Java源文件时,默认情况下它会使用操作系统的默认...
- 当创建数据库时,应明确指定其字符集设置,如使用GBK或UTF-8等。 - 如果需要支持多种语言,建议选择UTF-8作为数据库的字符集,因为它几乎包含了所有语言的字符。 2. **应用程序编码设置**: - 应用程序在处理...
默认情况下,Java使用UTF-16编码,每个字符占用2个字节,但也可以根据需要使用其他编码,如GBK或UTF-8,它们的字节长度不同。 "按byte截取字符串"通常涉及到以下步骤: 1. **确定编码**:首先,明确字符串的编码...
1. **字符编码**:字符编码是用来表示文本的数字系统,常见的有ASCII、Unicode(包括UTF-8、UTF-16等变体)等。不同的编码支持不同的字符集,选择合适的编码能确保文本正确显示。 2. **编码转换**:当不同编码的...
3. 支持多种编码标准:好的汉字内码查询工具应支持常见的汉字编码标准,如GB2312、GBK、Unicode(包括UTF-8等变种)等。 在实际应用中,可能会遇到乱码问题,这通常是由于编码不匹配导致的。例如,一个文件用GBK...
例如,当读取或写入文件时,需要指定正确的字符编码,如UTF-8。如果不匹配,可能会导致中文字符无法正确显示。Java提供了`java.nio.charset`包,其中的类和接口可以帮助开发者进行字符编码和解码操作。 总之,Java...
在进行文件格式转换时,如从GBK转换为UTF-8,了解GB18030的编码规则可避免信息丢失。 综上所述,GB18030汉字编码表是中文信息处理中的关键标准,它不仅扩展了汉字的表示范围,还兼容了多种字符集,为现代信息技术...
字符集是计算机存储和显示文本的一组规则,例如ASCII、GBK和UTF-8等。不同的字符集支持不同范围的字符,对于中文环境,GBK和UTF-8更为常见。当数据库和应用程序的字符集不一致时,就可能导致数据在传输过程中出现...
它们可以根据指定的字符集(如UTF-8、GBK等)进行编码和解码,使不同编码格式的数据能够在字节流和字符流之间自由转换。 六、对象序列化与反序列化 Java还提供了ObjectInputStream和ObjectOutputStream,用于对象的...
- **编码与解码**:Java中的字符串默认使用Unicode编码,但与其他系统交互时可能涉及其他编码格式,如UTF-8或GBK。`getBytes()`和`new String(byte[], charset)`用于处理不同编码之间的转换。 - **字符串不可变性**...
- **Unicode编码**:国际化的字符编码标准,支持世界上几乎所有语言的字符,常用的编码形式有UTF-8、UTF-16等。 - **汉字编码**:如GB2312、GBK等,用于表示汉字信息。 ### 汇编语言特点 - **执行速度快**:由于...
2. IO流:熟悉字节流、字符流和对象序列化的概念,熟练运用各种流类,如InputStream、OutputStream、Reader、Writer等,并理解Unicode、GBK、UTF-8、ISO8859编码方式。 3. 多线程:学习线程的基本概念,掌握通过...