十万个为什么「 你知道为什么汉字有时会乱码变成(问号)<?>或别的字符 吗?」
这个问题说小不小,说大也大,就瞎扯一下,我有兴趣就多说一点,没兴趣的部分就不多扯
你们转载加个原作者行吧,咋回事小老弟,你不和我通知一声啊,咋还不尊重人呢, 原地址: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对应的就是字符?
至于其他的乱码字符(例如:�Ĵ�ʡ�ɶ��� ����),和上面的差不多,截取的太多或太少,转译出来的就会有变化,至于变成了什么那就要去表里查一下了