Java Conversion from String to bytes is not one-one?

在工作中遇到了标题所述的问题,当一个字节数组编码成字符串后再获得字符串的字节数组,发现会和一开始的字节序列不同。上网查询了一番,发现Stack Overflow上有同样的问题,现在就来分析一下为什么会出现这种情况。

 byte[] bytes1 = {-1, 127, 0, 38, 97, 104, 55, 110, 50, -24, -48, 59, -20, -6, 64, 1, 4, 107, 56, 54 };      
   String msg  = new String( bytes1, "UTF-8" );        
   byte[] bytes2 = msg.getBytes( "UTF-8" );                            
   for( byte curr : bytes1 ) {         
       System.out.print( curr );
       System.out.print( ", " );
   }
   System.out.println();
   for( byte curr : bytes2 ) { 
       System.out.print( curr );
       System.out.print( ", " );
   }
//output 
-1, 127, 0, 38, 97, 104, 55, 110, 50, -24, -48, 59, -20, -6, 64, 1, 4, 107, 56, 54, 

-17, -65, -67, 127, 0, 38, 97, 104, 55, 110, 50, -17, -65, -67, -17, -65, -67, 59, -17, -65, -67, -17, -65, -67, 64, 1, 4, 107, 56, 54,    

在分析这个问题前,我们需要了解一下Unicode编码的知识。

需要注意的是,Unicode 只是一个符号集,它只规定了符号的二进制代码,却没有规定这个二进制代码应该如何存储。
比如,汉字严的 Unicode 是十六进制数4E25,转换成二进制数足足有15位(100111000100101),也就是说,这个符号的表示至少需要2个字节。表示其他更大的符号,可能需要3个字节或者4个字节,甚至更多。

简单来说,Unicode就是定义了一个符号对应的二进制码。utf-8,utf-16等代表不同的实现方式。

下面我们看一下utf-8如何实现Unicode

Unicode符号范围 | UTF-8编码方式
(十六进制) | (二进制)
———————-+———————————————
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

拿上面的来说,二进制码是100111000100101,选择上表的其中一行将x替换为二进制码,选择哪一行呢,第一行只有7位可以填,第二行只有11位可以填,所以都不行。第三行有16个,比我们目标值多一位,我们从右往左填,高位补0即可。结果为11100100 10111000 10100101


这时候我们观察一下上面的输出,所有的正数都能正确输出,所有的负数都会变成-17, -65, -67

导致这个问题原因在于utf-8是有编码规则的,如上图所示,当java从字节数组里二进制转str时,发现不符合utf-8规定的格式,该字符就会被替代成(可以参考JDK文档)。的Unicode码为'\ufffd',对应的字节就是-17, -65, -67

对于上面的案例我们可以直接使用utf-16bl(大头方式)或者utf-16le(小头方式)编码,直接用utf-16编码会多两个字节FE FF表示是大头方式,有兴趣可以自行查询了解。


参考链接

  1. https://stackoverflow.com/questions/9298381/java-conversion-from-string-to-bytes-is-not-one-one
  2. http://www.ruanyifeng.com/blog/2007/10/ascii_unicode_and_utf-8.html
  3. https://docs.oracle.com/javase/8/docs/api/java/nio/charset/CharsetEncoder.html
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值