关于编码的一些问题

来源:互联网 发布:局域网扫描软件 编辑:程序博客网 时间:2024/05/16 12:54
一直以来,编码问题都是比较令人纠结的,我自己也是,迷惑了好一段时间。

首先看一下ANSI编码和ASCII 编码,用google搜到这篇文章,挺好的。http://www.differencebetween.net/technology/web-applications/difference-between-ansi-and-ascii/

ASCII: American Standard Code for Information Interchange

ANSI: American National Standard Institute

基本的区别就是:

[plain] view plain copy
  1. Summary:  
  2.   
  3. ANSI has more characters than ASCII  
  4. ASCII uses 7 bits while ANSI uses 8  
  5. ASCII characters are fixed to the code points while ANSI code points may represent different characters  
  6. ASCII is more straightforward to use than ANSI  
  7. ASCII works with Unicode while ANSI compatibility is very limited  

然后又看到这篇文章,也不错,http://blog.csdn.net/zj510/article/details/37877651。建议仔细阅读。

这里,我主要再介绍一下ansi,unicode和utf8

首先看一下ANSI

ANSI编码

首先在C++代码里面敲入一行代码,很简单看下图,运行一下,会发现字符串ansi里面的内存是0xb3, 0xcc, 0xd0, 0xf2, 0xd4, 0xb1.打开记事本,写入“程序员”3个字,然后save,用ultraedit看一下十六进制编码,如下图。

跟VS里面看到的一模一样。我们在看一下C++的源文件。

看16进制的源文件,可以发现源文件里面的“程序员”3个字也是ANSI编码。好像VS对于C++源文件默认的就是ANSI编码,我在VS2013里面看到有个功能可以自动检测UTF-8编码啥的。但是没有仔细研究过。

OK, 现在我们知道,在notepad里面,打入一些字符,然后save,那么默认的就是ANSI编码。在VS里面如果输入一些常量字符串,假如是纯英文的话,那应该也是ANSI编码,如果你的常量字符串里面有中文或者韩文等其他字符的话,就视情况而定了,像vs2013可能会把你的源文件转换成UTF8格式,但我知道早期的VS是不会转的还是用ANSI.所以如果你在IDE,比如VS, Eclipse,甚至XCODE等里面输入常量字符串的话,需要小心。通常如果只是英文的话,一般是没有问题的,但是如果有中文的话,要留个心眼,看你的IDE是怎么编码的。不然恐怕就会出现乱码的情况。当然如果真的有中文等字符的话,也不建议使用ANSI编码,尽可能使用UNICODE吧。

UNICODE

unicode其实有好几种,比如unicode16,unicode32啥的。通常我们在写程序的时候,说起unicode或者宽字符,一般指的就是unicode16.也就是2个字节(wchar_t)来表示一个字符。一个英文字母a使用2个字节的宽字节wchar_t来表示,一个中文也是用一个wchar_t来表示。

notepad的saveas里面有个功能,可以把当前文件保存成unicode。

同样打开notepad输入“程序员”3个字,然后点击save as,在编码那里选择unicode。如图

保存完之后,用ultraedit打开,前面两个字节FFFE,这个其实是notepad的一个标记,不用管它,看后面的6个字节。“程序员”总共是3个中文字符,那么用unicode就是6个字节,也就是0B7A8F5E5854.

OK, 现在C++程序里面敲入一行代码,如下图.从下面的图片里面可以看到unicode字符串的内存是0x7a0b, 0x5e8f, 0x5458.跟上面ultraedit里面比较。好像每两个字节都反过来了,这是何故?

其实这里有个很好玩的事情。我们直接看unicode里面的指向的内存吧。

unicode指向的是地址0x0026fb90,看内存,内存里面的数据是0b, 7a, 8f, 5e, 58, 54。跟上面ultraedit里面显示的是一致的。那么说明unicode里面在内存存放的字节流跟ultraedit里面展示的是一模一样的。那为什么用wchar_t表示的时候就反过来了呢?其实我觉得这应该是windows上面的机制,用wchar_t表示的时候就是反过来的(不知道linux上是不是也是这样?),就好象是int在内存里面存放也是逆序的。

顺便再来看一下C#吧,写几行简单代码,直接看下图:

首先我们可以看到sizeof(char)在C#里面是2.其实在C#里面并没有wchar_t这种东西。只有一个char,而它就是2个字节。这是因为C#里面用的都是unicode。C#的string里面存放的就是unicode格式。好像Java也是这样。每一个char和上面的C++的wchar_t的值一模一样。


ANSI VS Unicode

那么我们到底应该使用ANSI还是Unicode呢?我建议使用Unicode,特殊是在写windows程序的时候,因为windows的API都支持ANSI和Unicode两种,也就是我们常说的多字节和宽字节。比如CreateFileA()支持ANSI编码, CreateFileW()支持Unicode编码。如果使用Ansi的话,万一你的字符串里面含有中文,就会比较麻烦一些,比如有些时候为了换行,很难计算,以为ANSI里面英文占一个自己,中文占2个字节。如果把一个中文给劈开了,那就显示乱码了。如果使用Unicode就很方便了,因为中文,英文都是占2个字节。当然如果你的程序确定只支持英文,那用ANSI也行,毕竟用Unicode的话,会多占内存。假如一个英文字符串有100个字符,用ANSI就占用100字节,用UNICODE就是200字节。Windows的大多数API都支持ANSI和UNICODE两种,我个人比较喜欢用UNICODE版本,毕竟现在的内存动不动都4g,8g的,多耗些内存也不是什么大问题。


utf8

最后再介绍一种非常常用的编码utf8. 什么是utf-8, 看定义, UTF-8 stands for Unicode Transformation Format-8. It is an octet (8-bit) lossless encoding of Unicode characters

至于utf8的具体细节和好处,网上一搜一大堆。基本上现在的网页用的都是utf-8编码,数据库里面用的很多也是utf-8,网络传输也是很多使用utf8。反正我觉得utf8的使用率是非常高的。比如我们想把一个字符串存入本地文件的时候也可以使用utf8编码,当然也使用ANSI或者UNICODE. 这里就简单介绍一下C++和Java里面的utf8使用吧。在C++里面,现在可以使用C++11了,简单的一个例子,把一个unicode字符串转换成utf8,再把utf8转换成unicode

[cpp] view plain copy
  1. #ifndef UNICODE  
  2. #define UNICODE  
  3. #endif  
  4.   
  5. #include "stdafx.h"  
  6. #include <windows.h>  
  7. #include <stdio.h>  
  8. #include <locale>  
  9. #include <codecvt>  
  10. #include <fstream>  
  11.   
  12. #include <string>  
  13.   
  14. using namespace std;  
  15.   
  16. // convert UTF-8 string to wstring  
  17. std::wstring utf8_to_wstring (const std::string& str)  
  18. {  
  19.     std::wstring_convert<std::codecvt_utf8<wchar_t>> myconv;  
  20.     return myconv.from_bytes(str);  
  21. }  
  22.   
  23. // convert wstring to UTF-8 string  
  24. std::string wstring_to_utf8 (const std::wstring& str)  
  25. {  
  26.     std::wstring_convert<std::codecvt_utf8<wchar_t>> myconv;  
  27.     return myconv.to_bytes(str);  
  28. }  
  29.   
  30. int main()  
  31. {  
  32.     // UTF-8 narrow multibyte encoding  
  33.     std::string ansi = "程序员";  
  34.                
  35.     std::wstring unicode(L"程序员");  
  36.     const wchar_t* p = unicode.c_str();  
  37.       
  38.     std::string utf8 = wstring_to_utf8(unicode);  
  39.      
  40.     std::wstring unicode2 = utf8_to_wstring(utf8);  
  41. }  
运行一下,可以看到utf8的字节流,然后再把utf8字节流转换成UNICODE得到unicode2,unicode2显示的也是“程序员”。

如果用C#就更简单了。我们可以看到utf8编码的字节流跟C++的一模一样。

最后看一下c#源文件的编码,我这里的c#源文件编码用的是UNICODE, 看图:(好像如果有中文的话,VS会转换成utf8)


尽量不要在源代码文件里面hardcode需要显示在UI上的字符串

比如类似的代码:

[cpp] view plain copy
  1. string title = "hellow world";  
  2.   
  3. SetWindowText(title);  

这个真的不是好习惯。如果你的程序要支持其他语言的话,你就不得不修改源文件而且重新编译了。而且万一需要支持中文的话,那么就得在字符串常量里面写中文,可是有些时候又不知道源文件是用什么格式来编码的。比如源文件是用utf8编码的话,文件就来的,SetWindowText()是不支持utf8编码显示的,我们不得不先把utf8编码转换成ANSI或者UNICODE,再调用windows API。还有其他问题,比如你用VS UNICODE编码来写程序的,而另外一个人用Xcode来写程序,那么你的中文在他的Xcode里面肯定显示乱码。总之很麻烦。我觉得我们的字符串常量应该尽可能放在资源文件里面,比如C++的resource文件,C#的资源文件。Android程序也是有资源文件。这样比较好,如果要改字符串的话,改资源文件就可以了。不需要跟源代码。当然如果是一些log之类的字符串,写在源代码文件里面倒也问题不大,因为这些log通常都是英文,而且没有localization的问题。


其他编码

除了ANIS, UNICODE, UTF8之后还有很多其他的编码,但是我觉得这三种是最常用的。而且一旦掌握了这三种以后,再看其他的编码应该也是很容易了。



1 0