由于写文件上传的时候讲文件byte[]变成String进行截取,然后再将截取的字符串变成byte[],结果发现最后得到的文件变成这样:
正确的:
错误的:
对比发现
FFD8 ====》3F(?)
FFE0 ====》3F(?)
然后我就怀疑是编码的问题,猜测是byte[]=>String=>byte[]的时候将内容改变了,然后将编码格式改成iso8859-1。然后就对了。所以就顺便学了一把编码的知识,在这里进行总结
## 各种编码方式 ##
iso8859-1:
属于单字节编码,最多能表示的字符范围是0-255,应用于英文系列。比如,字母’a’的编码为0x61=97。 ISO-8859-1编码是单字节编码,向下兼容ASCII,其编码范围是0x00-0xFF,0x00-0x7F之间完全和ASCII一致,0x80-0x9F之间是控制字符,0xA0-0xFF之间是文字符号。
GBK
全称叫《汉字内码扩展规范》,是国家技术监督局为 windows95 所制定的新的汉字内码规范,它的出现是为了扩展 GB2312,加入更多的汉字,它的编码范围是 8140~FEFE(去掉 XX7F且低位没有FF)总共有 23940 个码位,它能表示 21003 个汉字,它的编码是和 GB2312 兼容的,也就是说用 GB2312 编码的汉字可以用 GBK 来解码,并且不会有乱码。
UTF
UTF-16 具体定义了 Unicode 字符在计算机中存取方法。UTF-16 用两个字节来表示 Unicode 转化格式,这个是定长的表示方法,不论什么字符都可以用两个字节表示,两个字节是 16 个 bit,所以叫 UTF-16。UTF-16 表示字符非常方便,每两个字节表示一个字符,这个在字符串操作时就大大简化了操作,这也是 Java 以 UTF-16 作为内存的字符存储格式的一个很重要的原因。
UTF-8
UTF-16 统一采用两个字节表示一个字符,虽然在表示上非常简单方便,但是也有其缺点,有很大一部分字符用一个字节就可以表示的现在要两个字节表示,存储空间放大了一倍,在现在的网络带宽还非常有限的今天,这样会增大网络传输的流量,而且也没必要。而 UTF-8 采用了一种变长技术,每个编码区域有不同的字码长度。不同类型的字符可以是由 1~6 个字节组成。
有以下编码规则:
如果一个字节,最高位(第 8 位)为 0,表示这是一个 ASCII 字符(00 - 7F)。可见,所有 ASCII 编码已经是 UTF-8 了。
如果一个字节,以 11 开头,连续的 1 的个数暗示这个字符的字节数,例如:110xxxxx 代表它是双字节 UTF-8 字符的首字节。
如果一个字节,以 10 开始,表示它不是首字节,需要向前查找才能得到当前字符的首字节
总结:
****Java中String是Unicode码,也可以说是UTF-16,但Unicode和UTF的关系应该是:Unicode是一张编码表,记录了字符对应的码,但真正实现是在UTF上,存储和传输都是用UTF
对于 String new String (byte[]) 以及String.getbytes()
我认为String可以看做像c语言的char[] ,里面存储字符的Unicode码,每个字符两字节,如“中”输出他的char toHexString() 会看到是\u4e2d
然后new String (byte[])是将byte[] 按照编码方式解析成Unicode,至于怎么将不同的编码方式的byte[]变成Unicode,如GBK=》Unicode,我不清楚。
String.getbytes()方法是将String按照编码方式变成byte[];
我以前一直以为String和产生的byte[]是一样的东西,以为String的底层就是他的byte[],但其实String是Unicode,他产生的byte[]是和他不一样的,**
这里参考这个文章的内容:
https://www.ibm.com/developerworks/cn/java/j-lo-chinesecoding/
然后我发现“中”字输出getbytes(“utf-16”)输出4个字节,就很奇怪不是utf-16是定长2字节吗,查了资料发现
以汉字”严“为例,Unicode码是4E25,需要用两个字节存储,一个字节是4E,另一个字节是25。存储的时候,4E在前,25在后,就是Big
endian方式;25在前,4E在后,就是Little endian方式。 第一个字节在前,就是”大头方式“(Big
endian),第二个字节在前就是”小头方式“(Little endian)。
那么很自然的,就会出现一个问题:计算机怎么知道某一个文件到底采用哪一种方式编码?
Unicode规范中定义,每一个文件的最前面分别加入一个表示编码顺序的字符,这个字符的名字叫做”零宽度非换行空格“(ZERO WIDTH
NO-BREAK SPACE),用FEFF表示。这正好是两个字节,而且FF比FE大1。 如果一个文本文件的头两个字节是FE
FF,就表示该文件采用大头方式;如果头两个字节是FF FE,就表示该文件采用小头方式。
所以前两个字节是标志Little-endian||big-endian
–所以就明白为什么上次文件上传的时候会把FFD8变成3f了,是因为平台默认是GBK,我没有设置编码集的情况下,将byte按照GBK方式变成String,GBK两个字节两个字节的读,发现FFD8超过范围,就变成3f,
但是iso8859-1是单字节读,所以不会改变