您的位置:首页 > 移动开发 > Android开发

编码,解码,乱码,转码的理解。

2016-06-03 23:30 330 查看
对计算机而言,任何数字都是二进制的,字符也是用十六进制(其实也是二进制)来表示:比如”中文”,正常情况下(即没有错误的时候)存储为”4e2d 6587”,如果charset为”gbk”,则被编码为”d6d0 cec4”,然后返回字节”d6 d0 ce c4”.如果charset为”utf8”则最后是”e4 b8 ad e6 96 87”.如果是”iso8859-1”,则由于无法编码,最后返回 “3f 3f”(两个问号)。所以这里可以明确一个道理:不是我们看到的所有字符,都可以被解码,比如“ABCDEFG”,可以被解码为“utf-8”,也可以是“GBK”,甚至是“ASCII”,但是不能是”hex”,因为”HEX”不支持“G”这种编码格式。

也就是说,在不同编码格式下,字符的底层是不一样的,但是,我们明白一个道理就好:把字符正常显示就是一个查表的过程,当我们从网络,web,DB中接受数据的时候,我们得到肯定是一串01二进制(最底层),然后我们要做到找个“表”,去翻译这些二进制串,这个表就是字符编码。如server给我的是gbk的01序列,我们却用utf-8的01序列来做翻译,当然会乱码。所以乱码的根本原因在于我们没有找到一个张合适“翻译表”去翻译对方给我们的语言。

下面说说关于java中的字符串string的关于编码的方法吧:

1.String.getByte(“”); 这个函数是解码函数,就是说,原来的字符可以被哪种编码方式支持,我们就可以按照个编码过程来解码,这个函数返回的就是一串字节数组,也就是一个二进制的序列。

比如:

String str = "中国";
try {
byte[] bytes = str.getBytes("gbk");
byte[] bytes2=str.getBytes("utf-8");
byte[] byte3=str.getBytes("ascii");//不支持
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}


2.new String(byte[],”charset”) 这是java字符串处理的另一个标准函数,和上一个函数的作用相反,将字节数组按照charset编码进行组合识别,最后转换为unicode存储。参考上述getBytes的例子,”gbk” 和”utf8”都可以得出正确的结果”4e2d 6587”,但iso8859-1最后变成了”003f 003f”(两个问号)。

这个函数的使用示例如下:

try {
String s="中国";
byte []b=s.getBytes("utf-8");
String str=new String(b,"utf-8");
String str1=new String(b,"gbk");
System.out.println(str);
System.out.println(str1);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}


运行上面的程序,你会发现,str 打印出来是正确,str1打印出来,是乱码的。

也就是说,原来是什么编码方式的,再从原来解码的方式来编码,才是正确的!其实,想想,乱码的原理,大概也就是这样的逻辑,

比如说java的IO流中有一个InputStreamReader(InputStream in,Charset cs) ,就是一个转码的桥梁,其实他做了什么呢?就是上面那段代码里面干的事情!比如网络中发送的字符是utf-8的,那么那个参数就写入“utf-8”,同理,如果是服务器是“gbk”,那就写“gbk,我们常常这样使用。(其实这个类就是主要负责转码操作的,因为转码)。

BufferedReader m_bufferedreader=new BufferedReader(new InputStreamReader(m_inputstream,encode));
StringBuffer m_stringbuffer=new StringBuffer();
while((one_line_string=m_bufferedreader.readLine())!=null){
m_stringbuffer.append(one_line_string);//缓存String


样使用字符流还缓存流来读入网络中的字符,还用stirngBufferer来做缓存,效率很高。

然后我说下这次项目里面碰到的一个问题:

我写一个网盘的客户端,要得到服务器发过来的HTTP报文中的报文头的文件,所以我要从报文中得到文件的名称,英文好还,中文就呵呵了,又乱码了,我和后台的人沟通,结果发现对的啊,也是无语了,后来查找了好久才看到,原来HTTP报文的问题:

HTTP报文的编码格式只能是ISO-8895-1,不能是别的,所以,HTTP服务器在发送报文的时候,把原来的编码的格式先转换成为了ISO-8895-1

就是这样的形式:

String s="计算机";
try {
byte []b=s.getBytes("utf-8");
String str=new String(b,"iso-8895-1");
System.out.println(str);
catch (UnsupportedEncodingException e) {
e.printStackTrace();


所以,在不管你说server的编码格式utf-8,我这边也是用utf-8来解析的啊,都没用的,你拿到的就是一串乱码的东西,这样解决才是正确的:

byte []b=filename.getBytes("ISO-8859-1");
filename=new String(b,"utf-8");


也许这个例子不好理解,我做个比喻,服务器,先是把一个字符串(假设原来的是utf-8的编码格式的),然后服务器将这个字符串按照utf-8的标准,解码为二进制的字符串,再进行ISO-8859-1的编码,发送给客户端(但是,不管变成什么样子的二进制,原来的字符串想正确显示,就只有通过utf-8),所以,现在客户端就必须先将拿到的字符串先按照ISO-8859-1解码为原来的utf-8的二进制,再重新组合成为utf-8.方能正确显示

这就是这个问题的所在,我看到网上很少有解答的,还是给大家说下吧。主要是HTTP的报文头部的限制编码的问题。

编码一直都是一个比较基础的问题,无论在网络,加密解密,还是数据库操作,都请注重。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  android 二进制