Android判断文本编码格式

前几天项目中有这样的需求:客户端需要在服务器下载一个文本文件显示出来。bug是mac上的中文显示乱码。通过查找看项目中的老代码,原因是代码中使用的是通过BOM头的判断方式来判断文本的编码格式,如果没有BOM头,代码中就都识别成了GBK编码。虽然在Windows上utf-8的编码都默认添加BOM头,但是也可以使用无BOM头的UTF-8来保存的,而在mac上默认的UTF-8都是无BOM的编码格式,所以代码中就会将无BOM的UTF-8判断成GBK来进行编码,所以会乱码。趁这个机会,将编码的知识补了一下,最后说说bug的修复方式。如果你只是想知道怎么判断编码格式可以直接跳到最后

Unicode字符集

Unicode是一个字符集,就是在Unicode字符集中定义了几乎全世界所有的文字和符号。ASCII字符集、ISO 8859字符集、GB2312字符集、BIG5字符集、GB18030字符集等。
其中后面列举出来的字符集都是区域性的字符集,Unicode却包含了几乎所有的文字和符号
我们知道在计算机中是使用字节来表示字符的,那么几个字节来表示一个字符就是我们平时所说的编码格式,常见的编码格式有UTF-8、UTF-16、UTF-32、GBK、ANSI。

UTF家族

先来说UTF家族,UTF编码格式是针对Unicode字符集进行的编码格式,包括UTF-8,UTF-16,UTF-32,其中UTF-16使用两个自己表示一个字符,UTF-32使用四个字节表示一个字符,这两种都是使用固定的字节来表示一个字符,而UTF-8则是可变长度的编码方式,它根据字符的长度进行动态修改,使用1-4个字节来进行每个字符的编码,相对于上面两种编码方式,在存储数据的时候就极大的节省了空间,因为是可变长度的来表示字符,所以UTF-8是兼容ASCII码表的,同时也可以表示Unicode中所有的字符,所以UTF-8是最流行的编码方式

GBK

而GBK是专门的中文编码方式,是在ASCII表的基础上扩展来的,所以GBK也是兼容ASCII表的,随着GBK字符集的不断更迭,GBK编码方式也在不断进化,GBK2312,GBK18030,但是都支持向前兼容,所以使用GBK18030是可以兼容之前所有的GBK编码方式的。其中BIG5指的是繁体中文的编码方式

ANSI

再说说ANSI编码,其实这种编码不是一种固定的编码格式,它是根据你操作系统的语言来确定使用什么编码方式的,如果系统是简体中文的保存就默认使用GBK,如果系统是英文的保存默认使用ASCII,其他国家语言的系统就默认对应国家的编码方式,在Linux系统上也是这样的

大端小端

下面再来说说大端序和小端序:
所谓的大端序,小端序就是在数据在传输过程中,由于不同电脑CPU硬件不同,在读数据的时候,假如cpu是大端序那么字节高位在前,如果cpu是小端序那么字节低位在前。例如,一个“奎”的Unicode编码是594E,“乙”的Unicode编码是4E59。如果我们收到UTF-16字节流“594E”,那么这是“奎”还是“乙”?如果BOM是大端序,那么代码点就应该是594E,那么就是“奎”,如果BOM是小端序,那么代码点就应该是4E59,就是“乙”了。

这样在传输的过程中就会产生错误,多字节的Unicode编码方式定义了一个"字节顺序标记(Byte Order Mark)"即BOM,它是一个特殊的非打印字符,你可以把它包含在文档的开头来指示你所使用的字节顺序,让计算机知道谁在高位,谁在低位。对于UTF-16,字节顺序标记是U+FEFF。如果收到一个以字节FF FE开头的UTF-16编码的文档,你就能确定它的字节顺序是小端了;如果它以FE FF开头,则可以确定字节顺序大端的了。

在这里插入图片描述

如果细心的话,你会看到上面说大端序和小端序的时候有一个限定词,就是「多字节的Unicode编码」,那么如果不是多字节的时候呢?不如UTF-8

我们知道UTF-8 是可变长度的编码方式,有的字符使用的是单字节编码,不如英文字母,有的字符是多字节编码,UTF-8是没有大小端序之分的,为什么呢?就是因为UTF-8是以单字节为编码单元的,而数据的读取的最小单位就是字节,所以说对于 UTF-8 来说根本就没有顺序的问题,你可能会问,你不是说 UTF-8 也有多字节编码的时候吗?是的,即使是多字节编码的时候,也是以一个字节为一个单元来处理的,而 UTF-16 和 UTF-32 的单元分别是 2 个字节和 4 个字节。

最后说说那个bug的修复,在我们实际使用中其实就两种编码,UTF-8和GBK。UTF-8有无BOM和有BOM的区别,而GBK是没有BOM头的,所有要区分的就是无BOM的UTF-8和GBK,百度Google了好久也没有找到方法,最后问了公司里搞C的同事才知道原来有一个库,可以直接判断,传入文本的byte字节,返回编码格式。github上没有的,在Unicode官网才有,叫 ICU4J,也有对应的 C 库,叫ICU4C,对于我这样的只会用java语言的初级程序员,只能用第一个了。

使用这个库可以很容易的判断一个文本的编码格式,我稍微封装了一下,传入文本的字节数组就可以获取到编码格式

public String getEncode(byte[] bytes){

        CharsetDetector detector = new CharsetDetector();

        detector.setText(bytes);
        CharsetMatch match = detector.detect();
        String encode = match.getName();
        return encode;

    }

踩过的坑

1)将流先转换成String,然后在转成字节数组

在传入字节数组的时候我是先通过流转成的String,然后在转成 byte[]传入接口中的,这样识别的编码是不正确的,后来改成了直接将流转成 byte[]的方式就好用了。转换代码如下:

private static byte[] readInputSream(InputStream in) throws IOException {

        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        int len = 0;
        byte[] buffer = new byte[1024];
        while ((len = in.read(buffer)) != -1) {
            baos.write(buffer, 0, len);
        }
        in.close();
        return baos.toByteArray();

    }
2)直接将流传入库的接口中

在ICU4J这个库中是有方法来接收一个InputStream来判断文本的编码的,就是上面的setText方法,它可以接收一个输入流,但是当我直接将文本转换成输入流传入之后,总是会抛出异常,最后也没有搞懂为什么,既然传入字节数组好用,所以我也没有太纠结这个东西,这里告诉大家,如果你用到的话,注意一下这点。

欢迎关注我的微信公众号,我会把一些生活的感想和投资方面的总结写到公众号,希望你能来和我一起交流技术之外的东西。

  • 4
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值