浅谈Python和VC中的编码问题(转)
以前編碼問題總能讓自己湊或蒙過去,最近要做一個項目服務器端用python寫,客戶端用c++,工程編譯的字符集使用UNICODE。之間通過socket進行通信,通信過程中編碼轉換問題把我搞得暈頭轉向,逼著我將編碼問題好好研究一番。
?
?
首先先談談VC中的編碼問題,首先編碼我們大致可以分為兩類:文件編碼和內存編碼。文件編碼即源代碼文件的編碼,gbk,UTF-8等。內存編碼即源代碼編譯成為二進制文件的時候采用的編碼。
比如,在VC 2008中我們寫下如下的代碼:
char* a = “a中”;
讓我們分別來看一下文件編碼和內存編碼的區別。
用UntraEdit打開該cpp文件,進入十六進制模式:我們可以看到”a中”對應的編碼為:0×61 0xD6 0xD0,0×61是ASCII碼中的a,0xD6 0xD0是GBK里”中”的編碼。說明,在中文windows環境下,VC創建的文件編碼默認是GBK的。那么我們將文件修改為UTF-8的編碼格式,重新用UE以十六進制模式進入。我們看到”a中”的編碼變為了0×61 0xE4 0xB8 0xAD,0×61仍然是a的編碼,而中的編碼變成了,0xE4 0xB8 0xAD。
從上面的試驗我們還可以知道,GBK中中文為2個字節,UTF-8是3個字節。
?
下面我們研究一下內存編碼,內存編碼在VC中只有3個選項:Not Set,Use Multi-Byte Character Set和Use Unicode Character Set。這個屬性的修改可以在工程的屬性欄中找到:
?
?
讓我們繼續觀察上面的例子。來查看一下什么是內存編碼。我們在程序中設置斷點,調試程序來觀察在程序運行過程中,三種工程屬性下,char* a所指向的那篇內存空間的值是什么,是不是如我們所認為的那樣:
| 工程屬性 | Not Set | Use Multi-Byte Character Set | Use Unicode Character Set |
| 內存 | 0×61 0xd6 0xd0 | 0×61 0xd6 0xd0 | 0×61 0xd6 0xd0 |
?
?
如上表所示,無論工程屬性如何設置,”中”在內存中都是gbk編碼。這樣的程序拿到非中文的操作系統中執行是會出現亂碼的,那么怎么才能將其用UNICODE進行編碼呢?我們需要用到wchar_t這個類型,wchar_t是一種寬字類型,寬字類型的變量使用方法如下wchar_t* a = L”a中”;即在”a中”前面加上L。
現在,調試一下可以看到,a在內存中的值為0×61 0×00 0×2d 0×4e。wchar_t類型的變量會將用兩個字節存儲英文變量,即在原有ASCII的基礎上補上兩個0.而后面的0×2d 0×4e就是”中”的編碼。
這樣,變量a就在多種語言平臺上都可以正確顯示了。那么,工程屬性中的三種字符集選項有什么作用呢?
讓我們引用MSDN中的原文:
| Generic-text data type name | SBCS (_UNICODE, _MBCS not defined) | _MBCS defined | _UNICODE defined |
| _TCHAR | char | char | wchar_t |
| _tfinddata_t | _finddata_t | _finddata_t | _wfinddata_t |
| _tfinddata64_t | __finddata64_t | __finddata64_t | __wfinddata64_t |
| _tfinddatai64_t | _finddatai64_t | _finddatai64_t | _wfinddatai64_t |
| _TINT | int | int | wint_t |
| _TSCHAR | signed char | signed char | wchar_t |
| _TUCHAR | unsigned char | unsigned char | wchar_t |
| _TXCHAR | char | unsigned char | wchar_t |
| _T or _TEXT | No effect (removed by preprocessor) | No effect (removed by preprocessor) | L (converts following character or string to its Unicode counterpart) |
?
?
SBCS、_MBCS和_UNICODE是三個宏,會分別在設置了Not Set、Use Multi-Byte Character Set、Use Unicode Character Set的時候定義。為了程序的可移植性,我們通常不會直接使用wchar_t和L,而是使用TCHAR和T()。這樣,在工程屬性設置不同的情況下,TCHAR和T()會被解釋為不同的內容。
同樣,我們以前測試字符串長度的函數strlen也不要直接使用,為了可移植性,我們通常使用_tcslen或者_tcsclen。
?
?
| TCHAR.H routine | _UNICODE & _MBCS not defined | _MBCS defined | _UNICODE defined |
| _tcslen | strlen | strlen | wcslen |
| _tcsclen | strlen | _mbslen | wcslen |
| _tcsclen_l | strlen_l | _mbslen_l | wcslen_l |
?
?
那么現在的問題是,我想要把數據經過網絡傳輸,現在是否可以直接傳輸wchar_t*的變量呢,答案是不行的,因為wchar_t*的字符串的英文字符表示中存在00,中文英文都用兩個byte。這樣既浪費空間又可能會導致網絡的錯誤。因此我們需要先將其轉換成char*的那種類型,即,英文用一個byte,中文用兩個byte。怎么轉換呢?要使用MultiByteToWideChar和WideCharToMultiByte來互相進行轉換。轉換完了之后再通過網絡傳輸。
好了,現在數據到了Python端,想想看,現在Python端收到的是什么編碼?對,中文是gbk的編碼。然后我們為了在python端實現國際化,要將gbk轉化成unicode。方法很簡單,首先先用
s = struct.unpack(’s’,mysocket.recv(2))來接收一個中文字,然后調用s.decode(”gbk”)來將編碼轉化成為unicode編碼。這樣,編碼問題就完全解決了~
?
最后,我們可以通過python來觀察一下中的各種編碼數據:
可以很明顯的看出gbk、utf-8、unicode中英文1個byte,且都和ASCII的編碼方式一一致,gbk和unicode中中文2個byte,utf-8中中文3個byte。Utf-16不在本文進行討論。
到此,已經基本討論完了常用的編碼問題。記得以前玩臺灣游戲是亂碼,估計就是沒有國際化轉化成unicode的原因。如果用unicode,我們就能夠看到正確的繁體文字了。需要注意的是UNICODE編碼僅僅在程序執行的時候有用到,并不會作為數據交換的時候的數據傳輸的文字編碼或者作為文件的文字編碼。
轉載于:https://www.cnblogs.com/duzouzhe/archive/2009/10/21/1587664.html
總結
以上是生活随笔為你收集整理的浅谈Python和VC中的编码问题(转)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: MySQL怎么给表简明_科学网—mySQ
- 下一篇: ID3DXMesh的数据导出和导入