日韩黑丝制服一区视频播放|日韩欧美人妻丝袜视频在线观看|九九影院一级蜜桃|亚洲中文在线导航|青草草视频在线观看|婷婷五月色伊人网站|日本一区二区在线|国产AV一二三四区毛片|正在播放久草视频|亚洲色图精品一区

分享

C 之編碼問題(Unicode,ASCII,本地默認)

 蘭亭文藝 2019-12-14

本篇文章試圖回答的問題:

1、char* pStr='我aa';這句代碼執(zhí)行后,pStr指向的內存區(qū)域中存儲的字節(jié)到底是根據什么碼表而來的呢?該字符串占幾個字節(jié)?

2、將一個VS2010的Windows程序設置了“使用Unicode字符集”到底意味著什么?

3、現在有一個文件,其存儲內容未知(可能是文本,可能是圖像,可能是視頻),要求是:在文件最前面插入一串Unicode文本,插入完成后以文本程序打開該文件,插入的文本不會顯示為亂碼(該文件本身的內容不考慮)?!绾巫龅??

本人能力、精力有限,所言所感都基于自身的實踐和有限的閱讀、查閱,如有錯誤,歡迎拍磚,敬請賜教——博客園:錢智慧。

一:

用VS2010新建一個win32控制臺應用程序TestChar,代碼如下:

#include <iostream>
using namespace std;
int main()
{
    char* pStr="a";
    cout<<sizeof("a")<<endl;
    cout<<hex<<pStr[0]-0;
    cout<<pStr[1]-0<<endl;
    return 0;
}

打印結果如下:分析:存儲介質(內存、外存等)上存儲的都是二進制數據,而對于字符信息的存儲,先查碼表進行解碼,再把碼以二進制信息存儲,即pStr指向的這段內存中存儲的都是字符們的編碼:一個中文字符,一個英文字母,一個全角字母。要得到”我“的編碼,默認會查找本地碼表,本人是中文Win7系統,查找的是GB2312碼表,而對照GB2312碼表可發(fā)現,'我'的編碼正是ced2,與打印一致(f是符號位,可無視)。另外GB2312是不會對英文字母進行編碼的,因為英文字母屬于半角字符,這類編碼由ASCII碼表負責,GB2312中的任何字符都占用兩個字節(jié),空字符也由ASCII負責編碼,這就是為何上面的字符串占用的字節(jié)數目是6??梢?,上面一句代碼,其實涉及到了兩張碼表:ASCII碼表和GB2312。二:新建一個MFC對話框程序,名為TestUnicodeChar。默認情況下,VS2010建的項目都是基于Unicode的,即打開項目的屬性,在”字符集“設置中都是”使用Unicode字符集“,這到底是什么意思呢?莫非在程序中用的中文都是以Unicode字節(jié)進行存儲的?由上面的TestChar程序可以看出并非如此,pStr中的'我”是以GB2312進行存儲的。又或者,源文件(h文件、cpp文件等)在磁盤上是以Unicode進行編碼存儲的?也不是,源文件存儲默認也是以本地GB2312進行存儲的。驗證方式:用UE編輯器隨便打開一個設置了“使用Unicode字符集”項目的某個源文件,比如打開本項目中的TestUnicodeCharDlg.cpp文件,在UE中以16進制的形式查看該文件內容,可以發(fā)現其前兩個字節(jié)并非FF FE(這是Unicode文件的標識),你隨便找一個中文,對照其16進制找到其編碼,然后跟GB2312碼表中的該中文的編碼對照即可驗證。這是一個GB2312碼表的網頁鏈接:http://tool./gb2312tbl.php 我們知道,在C++中,有char和string,為了支持Unicode字符,還有wchar_t和wstring,我們可以認為string是基于char的,而wstring是基于wchar_t的。為什么要引入Unicode呢,只用char和string難道不能保存含有中文的字符(串)嗎?由TestChar程序,我們知道完全可以,并且采用的是GB2312編碼,問題是你無法確定一個類似pStr的混合串的字符數目,比如:string str='我a',你調用string的length方法不能準確得到str的長度,這給編程帶來了不便。所以你可以選用wstring和wchar:wstring wstr=L'我a',這時你再調用wstring的length方法就能準確得到了,其中L前綴可以使后面緊跟的字符串解釋成寬字符串(Unicode)。因為Unicode對任何字符都采用2個字節(jié)進行編碼,所以length的實現想必也很簡單:每兩個字節(jié)算一個字符,進而可以方便得到字符串的長度,這便是Unicode優(yōu)于多字節(jié)編碼的地方:試想,如果采用多字節(jié)編碼,那么要實現基于這種編碼的字符串類的length方法會非常頭疼。Unicode浪費了存儲空間但帶來了編程上的簡便。(關于這方面的詳細內容可以參考《Windows程序設計 第5版》第1章)在Windows中,有這樣一些宏:_T,TEXT,TCHAR,CString,它們根據不同的設定有不同的含義。先看一下它們的使用:在TestUnicodeCharDlg.cpp的OnPaint方法中加上如下代碼:1 TCHAR * pStr=TEXT('a我');//_T與TEXT的含義是一樣的2 CString str1=TEXT('a我');3 CString str2=L'a我';View Code如果程序定義了UNICODE宏,則_T和TEXT(二者含義和用法完全一樣)便會把括號內的字符串解釋為UNICODE字符串,TCHAR便會替換為wchar_t,CString便會替換為CStringW,否則(即沒有定義UNICODE宏),都會解釋為相應的char版本。而L前綴不是宏,類似強轉:不管有沒有定義UNICODE宏,都把后面的字符串解釋為UNICODE字符串。而設置“使用UNICODE字符集”就相當于定義UNICODE宏,即該設置僅僅是影響了一些宏的行為。若把該項目的“使用Unicode字符集”設置改為“未使用”,則編譯會出錯,因為此時str2就是一個CStringA實例,你不能把一個L前綴的字符串(Unicode字符串)賦值給它。三:第三個問題本質上就是往一個文件中寫Unicode字符串的問題。涉及到編碼問題的文件操作始終牢記一點:以什么編碼寫,就以編碼讀。在TestUnicodeChar程序的OnInitDialog函數中加入如下代碼:1 BOOL CTestUnicodeCharDlg::OnInitDialog() 2 { 3 CDialogEx::OnInitDialog(); 4 5 // 將“關于...”菜單項添加到系統菜單中。 6 7 // IDM_ABOUTBOX 必須在系統命令范圍內。 8 ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX); 9 ASSERT(IDM_ABOUTBOX < 0xF000);10 11 CMenu* pSysMenu = GetSystemMenu(FALSE);12 if (pSysMenu != NULL)13 {14 BOOL bNameValid;15 CString strAboutMenu;16 bNameValid = strAboutMenu.LoadString(IDS_ABOUTBOX);17 ASSERT(bNameValid);18 if (!strAboutMenu.IsEmpty())19 {20 pSysMenu->AppendMenu(MF_SEPARATOR);21 pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);22 }23 }24 25 // 設置此對話框的圖標。當應用程序主窗口不是對話框時,框架將自動26 // 執(zhí)行此操作27 SetIcon(m_hIcon, TRUE); // 設置大圖標28 SetIcon(m_hIcon, FALSE); // 設置小圖標29 30 // TODO: 在此添加額外的初始化代碼31 CFile myFile;32 33 34 if ( myFile.Open( _T('c:\\myfile.txt'), CFile::modeCreate | 35 CFile::modeReadWrite ) )36 {37 38 CString str=TEXT('a我');39 //myFile.Write('\xff\xfe',2);40 myFile.Write( str, str.GetLength()*sizeof(TCHAR) ); 41 myFile.Flush();42 }43 44 45 return TRUE; // 除非將焦點設置到控件,否則返回 TRUE46 }View Code運行程序,然后用寫字板、記事本、NotePad++、UE(UEdit)分別打開mfile.txt發(fā)現,有的能正常顯示,有的則亂碼。要知道,你往myfile.txt寫進去的是 兩個字符的Unicode編碼,用某文本程序去打開myfile.txt,倘若該程序默認情況下讀取文本時是按Unicode來解析的,則不會亂碼,否則就亂碼。我們把注釋的那行代碼的注釋拿掉,用任何支持Unicode的文本程序去打開myfile.txt就不會出錯了,因為一個文本中的前兩個字節(jié)FF FE便向試圖打開該文本的程序表明該文本應該用Unicode進行解析(你可以用NotePad++新建幾個Unicode格式的文本,隨便保存幾個字符,然后用UE以16進制格式查看便可知Unicode文本的前兩個字節(jié)都是FF FE)。另外,經常遇到有人問這樣的問題:CString如何轉換為char*?問這個問題之前,最好問下自己:我的目的是什么,為何要進行這樣的轉換,當前項目有沒有設置Unicode。要知道,如果設置了Unicode,則CString存儲的是Unicode字符串,轉換為char*后,你如果直接顯示這個char*或者寫到文件中(沒有把FF FE寫到文件開始處)然后打開,則會(假如打開文件的程序默認不以Unicode進行解析)出現亂碼,所以,這種情況下,轉換為char*的意義不大——這不是說不能把Unicode串轉為char*,這完全是可行的,本質上這只是在把一個Unicode字符串的內存內容'活生生”取出來而已。不管怎樣,下面的代碼重新修改了OnInitDialog函數,演示了幾種情況: 1 BOOL CTestUnicodeCharMFCDlg::OnInitDialog() 2 { 3 CDialogEx::OnInitDialog(); 4 5 // 將“關于...”菜單項添加到系統菜單中。 6 7 // IDM_ABOUTBOX 必須在系統命令范圍內。 8 ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX); 9 ASSERT(IDM_ABOUTBOX < 0xF000);10 11 CMenu* pSysMenu = GetSystemMenu(FALSE);12 if (pSysMenu != NULL)13 {14 BOOL bNameValid;15 CString strAboutMenu;16 bNameValid = strAboutMenu.LoadString(IDS_ABOUTBOX);17 ASSERT(bNameValid);18 if (!strAboutMenu.IsEmpty())19 {20 pSysMenu->AppendMenu(MF_SEPARATOR);21 pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);22 }23 }24 25 // 設置此對話框的圖標。當應用程序主窗口不是對話框時,框架將自動26 // 執(zhí)行此操作27 SetIcon(m_hIcon, TRUE); // 設置大圖標28 SetIcon(m_hIcon, FALSE); // 設置小圖標29 30 // TODO: 在此添加額外的初始化代碼31 CFile myFileW,myFileA,myFileCharArrow,myFileWOrA;32 33 34 if ( myFileW.Open( _T('c:\\myfileW.txt'), CFile::modeCreate | 35 CFile::modeReadWrite ) && 36 myFileA.Open( _T('c:\\myfileA.txt'), CFile::modeCreate | 37 CFile::modeReadWrite ) &&38 myFileCharArrow.Open( _T('c:\\myfileCharArrow.txt'), CFile::modeCreate | 39 CFile::modeReadWrite ) &&40 myFileWOrA.Open( _T('c:\\myFileWOrA.txt'), CFile::modeCreate | 41 CFile::modeReadWrite ))42 {43 44 CString strW=TEXT('a我');//因為本項目設置了Unicode字符集,所以我們知道CString會被替換為CStringW45 46 CStringA strA(strW);47 48 49 myFileW.Write( strW, strW.GetLength()*2); 50 myFileW.Flush();51 52 myFileA.Write(strA,strA.GetLength());53 myFileA.Flush();54 //CString的GetString返回的const類型指針,要么在右邊強轉,要么左邊用const類型的char*去接55 //注意指針命名:p是pointer,c是const,如果有t則是TEXT,w是wide,l是long56 char* pstr=(char*)strA.GetString();57 /*下面這行代碼若不注掉,會報錯,因為本程序是Unicode程序,58 strW會是一個CStringW類型的字符串,它的GetString返回的是LPCWSTR類型指針59 當然不能賦值給LPCSTR類型指針了,一個是const wchar_t*,另一個是const char*60 */61 //const char* pcstr1=strW.GetString();62 63 myFileCharArrow.Write(pstr,strlen(pstr));64 myFileCharArrow.Flush();65 66 //假設我們在編程中,不知道有沒有使用Unicode設置,為了通用,我們可以盡量使用宏及通用版本的相關函數(如_tcslen)67 CString strWOrA=TEXT('a我');68 //注意這里的TCHAR不一定就是wchar_t,這取決于程序是否設置了Unicode69 const TCHAR* pctstr=strWOrA.GetString();//CString的GetString返回的是const指針70 myFileWOrA.Write(ptstr,_tcslen(pctstr)*sizeof(TCHAR));71 myFileWOrA.Flush();72 73 }74 75 76 return TRUE; // 除非將焦點設置到控件,否則返回 TRUE77 }View Code用UE察看幾個文件的內容,如圖:其中,CED2是”我“的GB2312編碼,6211(注意字節(jié)高低次序)是”我“的Unicode編碼,我們可知,CStringA strA(strW)這行代碼,一定進行了碼表間的轉換。結合代碼,文件內容應該不難理解。還有些讓人容易頭暈的字符串指針宏,下面列舉出來:關于char*的:LPCSTR: long pointer const string,可看成const char*,與PCSTR相似LPSTR:可看成char*,與PSTR相似關于wchar_t*的:LPCWSTR,PCWSTR,LPWSTR,PWSTR通用版本(根據是否配置了Unicode有不同的宏替換):TCHAR*LPTSTR,LPCTSTR (T有點類似TEXT宏的意思)

    本站是提供個人知識管理的網絡存儲空間,所有內容均由用戶發(fā)布,不代表本站觀點。請注意甄別內容中的聯系方式、誘導購買等信息,謹防詐騙。如發(fā)現有害或侵權內容,請點擊一鍵舉報。
    轉藏 分享 獻花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多