Ansi,UTF8,Unicode編碼(續)


1.三種編碼的回顧

Ansi字符串我們最熟悉,英文占一個字節,漢字2個字節,以一個\0結尾,常用於txt文本文件。
Unicode字符串,每個字符(漢字、英文字母)都占2個字節;在VC++的世界里,Microsoft比較鼓勵使用Unicode,如wchar_t。
UTF8是Unicode一種壓縮形式,英文A在unicode中表示為0x0041,英語中這種存儲方式太浪費,因為浪費了50%的空間,於是就把英文壓縮成1個字節,成了utf8編碼;但是漢字在utf8中占3個字節,顯然用做中文不如ansi合算,這就是中國的網頁用作ansi編碼而國外的網頁常用utf8的原因。程序中把15.7M大小UTF8格式的txt文件轉化為ANSI后,大小僅為10.8M。

2.轉換函數

一般情況下,可以通過Windows頭文件下的兩個函數實現各個類型之間的轉換。頭文件添加:

#include <Windows.h>

多字節字符集 -> Unicode字符集

int MultiByteToWideChar(
  __in   UINT CodePage, // 標識了與多字節關聯的一個代碼頁值
  __in   DWORD dwFlags, // 允許我們進行額外的控制,它會影響帶變音符號(比如重音)的字符。但是一般情況下不適用,賦為 0 即可。
  __in   LPCSTR lpMultiByteStr, // 參數指定要轉換的字符串
  __in   int cbMultiByte, // 指定要轉換串長度的長度(字節數),如果參數值是-1,函數便可自動判斷源字符串的長度
  __out  LPWSTR lpWideCharStr, // 指定轉換后Unicode版本的字符串內存地址
  __in   int cchWideChar        // 指定 lpWideCharStr 緩沖區的最大長度。
                                // 如果傳入0,函數不會進行轉換,而是返回一個寬字符數(包括終止字符'\0'),
                // 只有當緩沖區能夠容納該數量的寬字符時,轉換才會成功。
);

Unicode字符集 –> 多字節字符集

int WideCharToMultiByte(
  __in   UINT CodePage,   // 標志了要與新轉換的字符串關聯的代碼頁
  __in   DWORD dwFlags,   // 制定額外的轉換控制,一般不需要進行這種程度的控制,而為 dwFlag 傳入 0
  __in   LPCWSTR lpWideCharStr, // 指定要轉換的字符串的內存地址
  __in   int cchWideChar,       // 指出該字符串的長度,如果傳入 -1 ,則由函數來判斷字符串的長度
  __out  LPSTR lpMultiByteStr,  // 轉換后的緩沖區
  __in   int cbMultiByte,       // 指定 lpMultiByteStr 緩沖區的最大大小(字節數),如果傳入 0 ,函數返回該目標緩沖區需要的大小
  __in   LPCSTR lpDefaultChar,  
  __out  LPBOOL lpUsedDefaultChar // 寬字符字符串中,如果至少有一個字符不能轉換為對應的多字節形式,函數就會把這個變量設為 TRUE 。如果所有字符都能成功轉換,就會把這個變量設為 FALSE。 通常將此函數傳入 NULL 值。
);

只有一個字符在 CodePage 制定的代碼頁中沒有對應的表示時,WideCharToMultiByte 才會使用后兩個參數。在遇到一個不能轉換的字符時,函數便使用 lpDefaultChar 參數指向的字符。如果這個參數指向為 NULL ,函數就會使用一個默認的字符。這個默認的值通常是一個問號。這對文件操作是非常危險的,因為問號是一個通配符。

3.程序實現

程序的頭文件:

/*
 *作者:侯凱
 *說明:utf8、unicode、utf8相互轉化
 *日期:2013-6-4
*/
#include <iostream>
#include <string>
#include <fstream>
#include <Windows.h> //Windows頭文件
using std::string;
using namespace std;

ANSI轉Unicode

void AnsiToUnicode() 
{
    char* sAnsi = "ANSI to Unicode, ANSI 轉換到 Unicode";

    //ansi to unicode
    int sLen = MultiByteToWideChar(CP_ACP, NULL, sAnsi, -1, NULL, 0); 
    wchar_t* sUnicode = new wchar_t[sLen];
    //wchar_t* sUnicode = (wchar_t*)malloc(sLen*sizeof(wchar_t));
    MultiByteToWideChar(CP_ACP, NULL, sAnsi, -1, sUnicode, sLen); 

    ofstream rtxt("ansitouni.txt");
    rtxt.write("\xff\xfe",2);//原因參見上一篇——"小尾"字節序方式存儲
    rtxt.write((char*)sUnicode, sLen*sizeof(wchar_t));
    rtxt.close();

    delete[] sUnicode; 
    sUnicode =NULL; 
    //free(sUnicode);
}

Unicode轉ANSI

void UnicodeToAnsi() 
{
    wchar_t *sUnicode = L"Convert Unicode to ANSI, Unicode 轉換為 ANSI";

    //unicode to ansi
    int sLen = WideCharToMultiByte(CP_ACP, NULL, sUnicode, -1, NULL, 0, NULL, NULL); 
    char* sAnsi = new char[sLen];
    //char* sAnsi = (char*)malloc(sLen);
    WideCharToMultiByte(CP_ACP, NULL, sUnicode, -1, sAnsi, sLen, NULL, NULL); 

    ofstream rtxt("unitoansi.txt");
    rtxt.write(sAnsi, sLen);
    rtxt.close();

    delete[] sAnsi; 
    sAnsi =NULL; 
    //free(sAnsi);
}

Unicode轉UTF8

void UnicodeToUtf8()
{
    wchar_t *sUnicode = L"Convert Unicode to UTF8, Unicode 轉換為 UTF8"; 
    // unicode to UTF8 
    int sLen = WideCharToMultiByte(CP_UTF8, NULL, sUnicode, -1, NULL, 0, NULL, NULL); 
    //UTF8雖然是Unicode的壓縮形式,但也是多字節字符串,所以可以以char的形式保存 
    char* sUtf8 = new char[sLen];  
    //unicode版對應的strlen是wcslen 
    WideCharToMultiByte(CP_UTF8, NULL, sUnicode, -1, sUtf8, sLen, NULL, NULL); 

    ofstream rtxt("unitoutf8.txt");
    rtxt.write("\xef\xbb\xbf", 3);//原因參見上一篇
    rtxt.write(sUtf8, sLen);
    rtxt.close();

    delete[] sUtf8; 
    sUtf8 =NULL; 
}

UTF8轉Unicode

void Utf8ToUnicode()
{    
    //UTF8 Convert to Unicode, UTF8 轉換為 Unicode,用UE十六進制打開“轉化為”直接復制過來亂碼,用16進制表示
    char* sUtf8 = "UTF8 Convert to Unicode, UTF8 \xe8\xbd\xac\xe6\x8d\xa2\xe4\xb8\xba Unicode"; 
    //UTF8 to Unicode 
    int sLen = MultiByteToWideChar(CP_UTF8, NULL, sUtf8, -1, NULL, 0); 
    wchar_t* sUnicode = new wchar_t[sLen]; 
    MultiByteToWideChar(CP_UTF8, NULL, sUtf8, -1, sUnicode, sLen);

    ofstream rtxt("utf8touni.txt");
    rtxt.write("\xff\xfe",2);
    rtxt.write((char*)sUnicode, sLen*sizeof(wchar_t));
    rtxt.close();

    delete[] sUnicode; 
    sUnicode =NULL;  
}

Ansi轉換utf8和utf8轉換Ansi就是上面2個的結合,把unicode作為中間量,進行2次轉換即可。

4.UTF8轉ANSI

在網絡傳輸中,我們常常使用UTF8編碼,但在程序處理時,我們習慣於ANSI編碼,至少目前的VS2010對UTF8碼的顯示是亂碼的。以下函數綜合上述程序,實現了txt文件UTF8編碼向ANSI編碼的轉化。

//changeTxtEncoding修改字符串的編碼  

char* changeTxtEncoding(char* szU8)
{  
    int wcsLen = ::MultiByteToWideChar(CP_UTF8, NULL, szU8, -1, NULL, 0);  
    wchar_t* wszString = new wchar_t[wcsLen];
    ::MultiByteToWideChar(CP_UTF8, NULL, szU8, -1, wszString, wcsLen);
    cout<<wszString<<endl;

    int ansiLen = ::WideCharToMultiByte(CP_ACP, NULL, wszString, -1, NULL, 0, NULL, NULL);  //wcslen(wszString)
    char* szAnsi = new char[ansiLen];  
    ::WideCharToMultiByte(CP_ACP, NULL, wszString, -1, szAnsi, ansiLen, NULL, NULL); 
    delete[] wszString;
    return szAnsi;  
}

void changeTextFromUtf8ToAnsi(const char* filename)  
{  
    ifstream infile;
    string strLine="";
    string strResult="";  
    infile.open(filename);
    infile.seekg(3, ios::beg);
    if (infile)  
    {  
        while(!infile.eof())
        {  
            getline(infile,strLine);  
            strResult+=strLine+"\n";
        }  
    }
    infile.close();
    char* changeTemp=new char[strResult.length()+1];
    changeTemp[strResult.length()]='\0'; //問題記錄
    strcpy(changeTemp, strResult.c_str()); //const char*轉化char*的方法
    char* changeResult=changeTxtEncoding(changeTemp); 
    strResult=changeResult;  

    ofstream outfile;  
    outfile.open("ANSI.txt");  
    outfile.write(strResult.c_str(), strResult.length());  
    outfile.flush();  
    outfile.close();
    delete[] changeResult;
    delete[] changeTemp;
}

問題記錄:
關於字符串的長度a.String類型的length()和size()函數都返回字符串的真實大小,不包括'\0‘ ;
b.char*類型的strlen()函數也是返回字符串的真實大小,不包括'\0‘ ;
c.注意,sizeof()函數包含'\0‘ ,如char str[] = “Hello” ;則sizeof (str ) = 6。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM