0
点赞
收藏
分享

微信扫一扫

手动实现UTF-8编码

灵魂跑者 2022-01-10 阅读 174
特别鸣谢:最近达内的刘苍松老师给我发了一段自己实现UTF-8编码的代码,我查阅了相关资料,并获得老师同意,特此记录一下
1. Unicode
2. UTF-8编码
3. UTF-8 的编码规则很简单
  1. 有人可能不理解,如果一个字符表示需要两个字节,为什么还能110开头,不会占用字符位数么?
  2. 其实仔细看上图Unicode符号编码范围,两个字节的编码只有0x07FF,也就是0b10000000 00000000 ~ 0b00000111 11111111,0b00000111 11111111前面正好五个0,正好可以填充110xxxxx 10xxxxxx,110+10正好5位
java中编码:以下内容都是从老师源码看来的
1. java如何存储字符
public class Test{
    public static void main(String[] args) {
        /*
         * Unicode 编码
         * - Java 中的字符存储的是一个符号的Unicode编码
         * - 可以显示为 10进制或16进制形式
         * - Java的字符范围 0 ~ 65535(FFFF)
         */
        char c1 = 'A';  // 41     65
        char c2 = '中'; // 4e2d   20013
        char c3 = '듏'; // U+B4CF 46287
        char c4 = 'α'; // U+03B1 945
        System.out.println((int)c1);//'A'十进制65,强转,字符转成Unicode码,英文和ASCII编码一样
        System.out.println(Integer.toHexString(c1));//A十六进制41
        System.out.println((int)c2);//'中'十进制20013
        System.out.println(Integer.toHexString(c2));//'中'十六进制4e2d
        System.out.println((int)c3);//'듏'十进制46287
        System.out.println(Integer.toHexString(c3));//듏'十六进制b4cf
        System.out.println((int)c4);//'α'十进制945
        System.out.println(Integer.toHexString(c4));//'α'十六进制3b1

    }
}
2. 如何将Unicode编码为UTF-8
  1. 3个字节,4个字节情况同理
3. 如何将UTF-8反编码为Unicode
代码

import java.util.Arrays;

public class Test{
    /*
     * 手工编写 UTF-8 编码
   Char. number range  |        UTF-8 octet sequence
      (hexadecimal)    |              (binary)
   --------------------+---------------------------------------------
   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 字节数组
     * @param str 被编码的字符串
     * @return 经过UTF-8编码以后字节数组
     */
    public static byte[] getBytes(String str){
        //预估一下返回值的最大情况,一个字符最大4字节
        byte[] bytes = new byte[str.length() * 4];
        //index 代表 bytes 数组中数据的存储位置
        int index = 0;
        //遍历字符串中每个字符,根据字符的Unicode编码范围,进行UTF-8编码
        //将编存储到 bytes,bytes中就是返回值UTF-8数据
        // str = Java
        for(int i=0; i<str.length(); i++){
            char c = str.charAt(i);
            //判断c范围,根据范围进行编码
            if(c <= 0x7F){//0b0000-0000 ~ 0b0111-1111
                // c 在 0 ~ 0x7F 范围内,是1字节编码,1字节编码添加到bytes
                bytes[index++] = (byte)c;//一字节编码就是ASCII,直接转即可
            }else if(c <= 0x7FF){//0b10000000 00000000 ~ 0b00000111 11111111
                // c 在 0x80 ~ 0x7FF 范围,处理两个字节的UTF-8编码
                //  b1         b2    将这两个字符
                // 110xxxxx 10xxxxxx 处理成UTF-8编码格式
                // b2截取字符的后6位
                // c & 00111111 可以拿到c的后6位,然后 | 10000000 正好填充10在前面
                //00000011 10111011  c
                //00000000 00111111  0x3f
                //00000000 00111011  c & 0x3f
                //         10000000
                //         10111011  c & 0x3f | 10000000
                int b2 = (c & 0x3f) | 0b10000000; //0b0011-1111
                // b1截取剩下的5位
                // (c >>> 6)先把后6为右移,方便我们运算剩下的5位,然后 & 00011111,拿到这5位,最后 | 11000000,正好填充110在前面
                //00000011 10111011  c
                //00000000 00001110  c >>> 6  无符号右位移6位
                //00000000 00011111  0x1f
                //00000000 00001110  (c >>> 6) & 0x1f
                //         11000000
                //         11001110  (c >>> 6) & 0x1f | 11000000
                int b1 = ((c >>> 6) & 0x1f) | 0b11000000; //0b11111
                bytes[index++] = (byte)b1;
                bytes[index++] = (byte)b2;
            }else if (c<0xffff){
                //处理3字节编码
                //1110xxxx 10xxxxxx 10xxxxxx
                int b3 = (c & 0b111111) | 0b10000000;
                int b2 = ((c >>> 6) & 0b111111) | 0b10000000;
                int b1 = ((c >>> 12) & 0b1111) | 0b11100000;
                bytes[index++] = (byte) b1;
                bytes[index++] = (byte) b2;
                bytes[index++] = (byte) b3;
            }
        }
        return Arrays.copyOf(bytes, index);
    }

    /**
     * 将UTF-8编码的字节数组解码为字符串(Unicode字符)
     * @param bytes UTF-8 编码的字节
     * @return 解码以后的字符串
     */
    public static String decode(byte[] bytes){
        char[] chs = new char[bytes.length];
        int index = 0;
        //遍历 字节 数组,检查每个字节:
        // 如果字节以0开头 则是单字节编码 0xxxxxxx
        // 如果是以 110 为开头 则是双字节编码 110xxxxx
        // 如果是 1110 开头则是3字节编码
        for (int i = 0; i < bytes.length ; ){
            int b1 = bytes[i++] & 0xff;
            if((b1 >>> 7) == 0){
                //检查 01001010 是否为 单字节编码 0xxxxxxx
                // b1       00000000 00000000 00000000 01001010
                // b1>>>7   000000000000000 00000000 00000000 0
                chs[index++] = (char) b1;
            }else if((b1 >>> 5) == 0b110){
                //检查是否为双字节编码 b1 11001110 b2 10111011
                // b1 -> int
                // b1      00000000 00000000 00000000 11001110
                // b1>>>5  0000000000000 00000000 00000000 110
                int b2 = bytes[i++] & 0xff;
                // b1      00000000 00000000 00000000 11001110
                // b2      00000000 00000000 00000000 10111011
                // c       00000000 00000000 0000001110 111011
                int c = ((b1 & 0b11111)<<6) | (b2 & 0b111111);
                chs[index++] = (char) c;
            }else if ((b1 >>> 4) == 0b1110){
                // 检查是否为3字节编码: 11101000 10100001 10101000
                int b2 = bytes[i++] & 0xff;
                int b3 = bytes[i++] & 0xff;
                int c = ((b1 & 0b1111)<<12) | ((b2 & 0b111111)<<6) | (b3 & 0b111111);
                //System.out.println("b1:" + Integer.toBinaryString(b1));
                //System.out.println("b2:" + Integer.toBinaryString(b2));
                //System.out.println("b3:" + Integer.toBinaryString(b3));
                //System.out.println("c:" + Integer.toBinaryString(c));
                chs[index++] = (char) c;
            }
        }

        return new String(chs, 0, index);
    }

    public static void main(String[] args) {
        String str = "Javaλ表达式";
        System.out.println("Unicode:");
        for(int i=0; i<str.length(); i++){
            char c = str.charAt(i);
            System.out.print(c);
            System.out.print(":");
            System.out.println(Integer.toBinaryString(c));
        }
        //调用手写UTF-8编码方法
        byte[] bytes = getBytes(str);
        for(byte b:bytes){
            System.out.println(Integer.toBinaryString(b & 0xff));
        }
        //检查手写的UTF-8解码运算
        String s = decode(bytes);
        System.out.println(s);
    }
}
举报

相关推荐

0 条评论