`
cqupt123
  • 浏览: 66821 次
  • 性别: Icon_minigender_1
  • 来自: 成都
社区版块
存档分类
最新评论

探索java编码——UNICODE、GBK、UTF-8等

阅读更多

  必须知道的几个简单概念:

  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的字节序的存储方式,需要根据不同的硬件或者操作系统选择。

 

  当然,解码可以通过编码的逆向过程来得到对应的字符。到此为止,对字符集和编码的原理有了基本的认识。

分享到:
评论

相关推荐

    Java中压缩与解压--中文文件名乱码解决办法

    这里通过`getUTF8Bytes()`方法获取UTF-8编码的字节数组,以确保在无法使用指定编码时可以回退到UTF-8编码。 ### 2. 自定义ZipInputStream类(CnZipInputStream) #### (1) 创建并修改CnZipInputStream类 同样地,从...

    java汉字问题解决方案

    在计算机系统中,汉字是通过特定的编码方式来表示的,例如GBK、GB2312或Unicode(UTF-8)等。当Java程序在读取、处理或输出汉字时,如果编码不一致,就会出现乱码。例如,一个使用GBK编码的文本文件被当作UTF-8编码...

    2024码表IO流-字节流-HM

    从ASCII码表到GBK再到Unicode及其衍生的编码方案(如UTF-8),每一次进步都旨在更好地支持国际化的需求。在编程实践中,熟练掌握字符集相关的编码与解码操作,有助于解决多种场景下的文本处理问题。

    GB18030Tools

    1. 文件转换:支持批量转换GB18030编码的文本文件到Unicode(如UTF-8),反之亦然。 2. 字符查看:允许用户查看和比较不同编码下的字符表示,以便理解转换过程。 3. 预览与校验:在转换前后提供预览功能,确保转换...

    Win32 与 Android的编码统一问题1

    另一方面,Android操作系统基于Linux内核,其默认采用的是Unicode编码,最常见的是UTF-8,这是一种多字节编码方式,能够支持全球大部分语言的字符集,包括ASCII在内。因此,当在Android平台上开发或处理文本时,通常...

    使用MyEclipse遇到的乱码问题解决

    - 在编写代码时,推荐始终使用Unicode编码(如UTF-8),以保证跨平台和兼容性。 - 在处理文本输入输出时,使用`InputStreamReader`和`OutputStreamWriter`配合指定的字符集进行读写操作,避免乱码。 通过以上步骤...

    jspservlet取参数乱码问题分析

    - **高效性**:虽然UTF-8不是固定长度的编码方式,但它对英文等常用字符的编码效率较高,对于非英文字符,尽管占用更多空间,但仍然是一种高效的编码方案。 ##### 3.2 UTF-8解决方案示例 在实际应用中,可以将整个...

    Java中文问题及最优解决方法

    如果源文件中包含中文字符,则需确保该文件的编码格式与Java程序期望的一致,通常推荐使用UTF-8编码来避免潜在的问题。 ##### 2. 编译阶段 当使用`javac`命令编译Java源文件时,默认情况下它会使用操作系统的默认...

    Struts原理与实践 ——

    - 当创建数据库时,应明确指定其字符集设置,如使用GBK或UTF-8等。 - 如果需要支持多种语言,建议选择UTF-8作为数据库的字符集,因为它几乎包含了所有语言的字符。 2. **应用程序编码设置**: - 应用程序在处理...

    按byte截取字符串

    默认情况下,Java使用UTF-16编码,每个字符占用2个字节,但也可以根据需要使用其他编码,如GBK或UTF-8,它们的字节长度不同。 "按byte截取字符串"通常涉及到以下步骤: 1. **确定编码**:首先,明确字符串的编码...

    自动写的东东转码工具

    1. **字符编码**:字符编码是用来表示文本的数字系统,常见的有ASCII、Unicode(包括UTF-8、UTF-16等变体)等。不同的编码支持不同的字符集,选择合适的编码能确保文本正确显示。 2. **编码转换**:当不同编码的...

    汉字内码查询 反查询.zip

    3. 支持多种编码标准:好的汉字内码查询工具应支持常见的汉字编码标准,如GB2312、GBK、Unicode(包括UTF-8等变种)等。 在实际应用中,可能会遇到乱码问题,这通常是由于编码不匹配导致的。例如,一个文件用GBK...

    java字符集 后果节目应该不能

    例如,当读取或写入文件时,需要指定正确的字符编码,如UTF-8。如果不匹配,可能会导致中文字符无法正确显示。Java提供了`java.nio.charset`包,其中的类和接口可以帮助开发者进行字符编码和解码操作。 总之,Java...

    所有gb18030的汉字编码表

    在进行文件格式转换时,如从GBK转换为UTF-8,了解GB18030的编码规则可避免信息丢失。 综上所述,GB18030汉字编码表是中文信息处理中的关键标准,它不仅扩展了汉字的表示范围,还兼容了多种字符集,为现代信息技术...

    orai18n.jar

    字符集是计算机存储和显示文本的一组规则,例如ASCII、GBK和UTF-8等。不同的字符集支持不同范围的字符,对于中文环境,GBK和UTF-8更为常见。当数据库和应用程序的字符集不一致时,就可能导致数据在传输过程中出现...

    java io流学习笔记1

    它们可以根据指定的字符集(如UTF-8、GBK等)进行编码和解码,使不同编码格式的数据能够在字节流和字符流之间自由转换。 六、对象序列化与反序列化 Java还提供了ObjectInputStream和ObjectOutputStream,用于对象的...

    java代码-36 林志鹏 字符串

    - **编码与解码**:Java中的字符串默认使用Unicode编码,但与其他系统交互时可能涉及其他编码格式,如UTF-8或GBK。`getBytes()`和`new String(byte[], charset)`用于处理不同编码之间的转换。 - **字符串不可变性**...

    专升本专业课的笔记 微型计算机技术与接口技术 农学院授课教案2

    - **Unicode编码**:国际化的字符编码标准,支持世界上几乎所有语言的字符,常用的编码形式有UTF-8、UTF-16等。 - **汉字编码**:如GB2312、GBK等,用于表示汉字信息。 ### 汇编语言特点 - **执行速度快**:由于...

    J2EE总体的学习步骤

    2. IO流:熟悉字节流、字符流和对象序列化的概念,熟练运用各种流类,如InputStream、OutputStream、Reader、Writer等,并理解Unicode、GBK、UTF-8、ISO8859编码方式。 3. 多线程:学习线程的基本概念,掌握通过...

Global site tag (gtag.js) - Google Analytics