十万个为什么 「你知道为什么汉字有时会乱码变成问号(?)或别的字符吗?」

十万个为什么「 你知道为什么汉字有时会乱码变成(问号)<?>或别的字符 吗?」


这个问题说小不小,说大也大,就瞎扯一下,我有兴趣就多说一点,没兴趣的部分就不多扯

你们转载加个原作者行吧,咋回事小老弟,你不和我通知一声啊,咋还不尊重人呢, 原地址:Sclifftop「你知道为什么汉字有时会乱码变成问号(?)或别的字符吗?」


至于字符集和编码方式的区别,现在都混起来了,你可以用编码两个字包括它们所有

从哪开始说呢,先从字符集开始说吧

先说下ASCII,我发现很多人把后面的当作罗马数字2(Ⅱ),不是的,它是两个大写的字母i,全称是American Standard Code for Information Interchange,共定义了128个字符,它是针对英语,后面又有了扩展的ASCII码

但还是不够,就有了Unicode字符集,Unicode采用了16位,基本上包含了所有的语言字符

ISO-8859-1:单字节编码,向下兼容ASCII,0x00-0x7F之间完全和ASCII一致,0x80-0x9F之间是控制字符,0xA0-0xFF之间是文字符号


然后稍微说下几个编码方式

UTF-8:是一种针对Unicode的可变长度字符编码,俗称万国码,海贼里大妈想要实现的最终理想就是“建立万国”,她建立万国是要干什么(可能是用来日)后面剧情没看,别问,问就是不知道

GB2312:GB2312是1980年由国家标准总局发布,适用于汉字处理,每个汉字和符号用两个字节来表示

GBK:最初是由微软对GB2312的扩展

还有很多UTF-16什么的,你们自己去查


然后就是产生的原因

为了便于理解,我就拿java来说,不为别的,就是因为俺有现成的源码可以看,就问你气不气,如果你不知道那没关系,慢慢看代码,看不懂也不影响下面结论,相差不大,都是从C,C++,java过来的,有什么看不懂的

先说下java其中的方法getBytes

getBytes方法使用指定的字符集将字符串编码为byte序列,并将结果存储到一个新的byte数组中

先看下getBytes方法是什么样的

  951       public byte[] getBytes(String charsetName)
  952           throws UnsupportedEncodingException
  953       {
  954           if (charsetName == null) throw new NullPointerException();
  955           return StringCoding.encode(charsetName, value, offset, count);
  956       }
  
  976       public byte[] getBytes(Charset charset) {
  977           if (charset == null) throw new NullPointerException();
  978           return StringCoding.encode(charset, value, offset, count);
  979       }


  994       public byte[] getBytes() {
  995           return StringCoding.encode(value, offset, count);
  996       }

根据上面看以看出它调用的是StringCoding.encode()方法,那我们看下encode方法

  326       static byte[] encode(String charsetName, char[] ca, int off, int len)
  327           throws UnsupportedEncodingException
  328       {
  329           StringEncoder se = deref(encoder);
  330           String csn = (charsetName == null) ? "ISO-8859-1" : charsetName;
  331           if ((se == null) || !(csn.equals(se.requestedCharsetName())
  332                                 || csn.equals(se.charsetName()))) {
  333               se = null;
  334               try {
  335                   Charset cs = lookupCharset(csn);
  336                   if (cs != null)
  337                       se = new StringEncoder(cs, csn);
  338               } catch (IllegalCharsetNameException x) {}
  339               if (se == null)
  340                   throw new UnsupportedEncodingException (csn);
  341               set(encoder, se);
  342           }
  343           return se.encode(ca, off, len);
  344       }
  382       static byte[] encode(char[] ca, int off, int len) {
  383           String csn = Charset.defaultCharset().name();
  384           try {
  385               return encode(csn, ca, off, len);
  386           } catch (UnsupportedEncodingException x) {
  387               warnUnsupportedCharset(csn);
  388           }
  389           try {
  390               return encode("ISO-8859-1", ca, off, len);
  391           } catch (UnsupportedEncodingException x) {
  392               // If this code is hit during VM initialization, MessageUtils is
  393               // the only way we will be able to get any kind of error message.
  394               MessageUtils.err("ISO-8859-1 charset not available: " + x.toString());
  396               // If we can not find ISO-8859-1 (a required encoding) then things
  397               // are seriously wrong with the installation.
  398               System.exit(1);
  399               return null;
  400           }
  401       }
  402   }

Charset.defaultCharset()是干嘛的知道不,这是获取默认的编码格式,那问题又来了,这个“默认”指的是什么,听好了嗷,“默认”指的是环境变量里配置的编码格式

从源码可以看出,如果你不指定编码,那就先获取默认的编码格式,如果没有或者是不支持那就使用ISO-8859-1

那你又想问了,如果不支持ISO-8859-1怎么办,怎么办?能怎么办,国际通用到你这都变不通用了,我也不知道该怎么办


先写下面的一段代码:
在这里插入图片描述
结果:
在这里插入图片描述

根据上面可以看出,如果使用ISO-8859-1,截取一个字节后中文字符编码会变为63,你随便找个表,就拿ASCII说,63对应的就是字符?

至于其他的乱码字符(例如:�Ĵ�ʡ�ɶ��� ����),和上面的差不多,截取的太多或太少,转译出来的就会有变化,至于变成了什么那就要去表里查一下了


维尼聚合工具


猜你喜欢

转载自blog.csdn.net/S_clifftop/article/details/101022541
今日推荐