手动做UTF-8编码的转换

来源:互联网 发布:网络教育高升专毕业证 编辑:程序博客网 时间:2024/05/19 07:11

看到一个题目:不使用 String.getBytes() 等其他工具类/函数完成下面功能 

public static void main(String[] args) throws IOException {    String str = "Hello, 我们是中国人。";    byte[] utf8Bytes = toUTF8Bytes(str);    FileOutputStream fos = new FileOutputStream("f.txt");    fos.write(utf8Bytes);    fos.close();}public static byte[] toUTF8Bytes(String str) {    return null; // TODO}

 觉得挺有意思,便尝试去做,做完发现,还是费了不少力气,在这里分享下心得。

 1. utf-8的编码规则和表,参考这里http://blog.csdn.net/lk_cool/article/details/7344244,不赘述

 2. 实际上,unicode跟utf-8不是一回事,unicode是定长2字节,是一个编码标准,而utf-8是变长从1-4字节不等,是编码的实现。java内部默认采用unicode编码,并不代表utf-8

 3. 编码本质上是将一种表示转为另一种表示,等价转换而不丢失信息量。而utf-8编码采用变长编码,利用了赫夫曼树的规则,能够最快速区分并解析。

 4. 代码实现并不难,关键是有的小地方需要注意,一个是厘清int,char,byte三者关系;二是对位操作的熟练使用;三就是由于大小端以及编码规则,处理起来有些trick。

代码如下

/** *  */import java.io.FileOutputStream;import java.io.IOException;import java.util.ArrayList;import java.util.Collections;import java.util.List;/** * @author levi * */public class UTF8Encoder {public static void main(String[] args) throws IOException {    String str = "Hello, 我们是中国人。";    byte[] utf8Bytes = toUTF8Bytes(str);    FileOutputStream fos = new FileOutputStream("f.txt");    fos.write(utf8Bytes);    fos.close();}public static byte[] toUTF8Bytes(String str) {List<Integer> result = new ArrayList<Integer>();for (int i = 0;i < str.length(); i++) {int chari = (int)str.charAt(i);if(chari <= 127){result.add((127 ^ leftShiftN(1)) & chari);}else{//count n firstint n = 2;if(chari < 0x7ff){n = 2;}else if(chari < 0xffff){n = 3;}else if(chari < 0x10ffff){n = 4;}int lastByte = 0;List<Integer> rr = new ArrayList<Integer>();for(int j = 0; j < n; j++){int low = chari & 255;chari = chari >> 8;int x = 0;if(j == (n - 1)){int leftShiftN = leftShiftN(n);x = leftShiftN | lastByte;}else{x = (2 << 6) | (((low << (2 * j)) & (leftShiftN(2) ^ 255)) | lastByte);lastByte = (low & leftShiftN(2 * (j + 1))) >> 8 - 2 * (j + 1);}rr.add(x);}Collections.reverse(rr);result.addAll(rr);}}byte [] finalResult = new byte[result.size()];for (int i = 0;i < result.size();i++) {finalResult[i] = (byte)result.get(i).intValue();}    return finalResult; }private static int leftShiftN(int n){int orginal = 0;for(int i = 0; i < 7; i++){if(i < n){orginal |= 1;}orginal = orginal << 1;}return orginal;}}

java中也有自动探测编码的工具: http://jchardet.sourceforge.net/

0 0