Unicode字符集和多字节字符集关系
在计算机中字符通常并不是保存为图像,每个字符都是使用一个编码来表示的,而每个字符究竟使用哪个编码代表,要取决于使用哪个字符集(charset)。在最初的时候,Internet上只有一种字符集——ANSI的ASCII字符集,它使用7 bits来表示一个字符,总共表示128个字符,其中包括了英文字母、数字、标点符号等常用字符。之后,又进行扩展,使用8 bits表示一个字符,可以表示256个字符,主要在原来的7 bits字符集的基础上加入了一些特殊符号例如制表符。
后来,由于各国语言的加入,ASCII已经不能满足信息交流的需要,因此,为了能够表示其它国家的文字,各国在ASCII的基础上制定了自己的字符集,这些从ANSI标准派生的字符集被习惯的统称为ANSI字符集,它们正式的名称应该是MBCS(Multi-Byte Chactacter System,即多字节字符系统)。这些派生字符集的特点是以ASCII 127 bits为基础,兼容ASCII 127,他们使用大于128的编码作为一个Leading Byte,紧跟在Leading Byte后的第二(甚至第三)个字符与Leading Byte一起作为实际的编码。这样的字符集有很多,我们常见的GB-2312就是其中之一。
例如在GB-2312字符集中,“连通”的编码为C1 AC CD A8,其中C1和CD就是Leading Byte。前127个编码为标准ASCII保留,例如“0”的编码是30H(30H表示十六进制的30)。软件在读取时,如果看到30H,知道它小于128就是标准ASCII,表示“0”,看到C1大于128就知道它后面有一个另外的编码,因此C1 AC一同构成一个整个的编码,在GB-2312字符集中表示“连”。
由于每种语言都制定了自己的字符集,导致最后存在的各种字符集实在太多,在国际交流中要经常转换字符集非常不便。因此,提出了Unicode字符集,它固定使用16 bits(两个字节、一个字)来表示一个字符,共可以表示65536个字符。将世界上几乎所有语言的常用字符收录其中,方便了信息交流。标准的Unicode称为UTF-16。后来为了双字节的Unicode能够在现存的处理单字节的系统上正确传输,出现了UTF-8,使用类似MBCS的方式对Unicode进行编码。注意UTF-8是编码,它属于Unicode字符集。Unicode字符集有多种编码形式,而ASCII只有一种,大多数MBCS(包括GB-2312)也只有一种。Unicode的最初目标,是用1个16位的编码来为超过65000字符提供映射。但这还不够,它不能覆盖全部历史上的文字,也不能解决传输的问题 (implantation head-ache's),尤其在那些基于网络的应用中。已有的软件必须做大量的工作来程序16位的数据。因此,Unicode用一些基本的保留字符制定了三套编码方式。它们分别是UTF-8,UTF-16和UTF-32。正如名字所示,在UTF-8中,字符是以8位序列来编码的,用一个或几个字节来表示一个字符。这种方式的最大好处,是UTF-8保留了ASCII字符的编码做为它的一部分,例如,在UTF-8和ASCII中,“A”的编码都是0x41.UTF-16和UTF-32分别是Unicode的16位和32位编码方式。考虑到最初的目的,通常说的Unicode就是指UTF-16。
例如“连通”两个字的Unicode标准编码UTF-16 (big endian)为:DE 8F 1A 90
而其UTF-8编码为:E8 BF 9E E9 80 9A
最后,当一个软件打开一个文本时,它要做的第一件事是决定这个文本究竟是使用哪种字符集的哪种编码保存的。软件有三种途径来决定文本的字符集和编码:
最标准的途径是检测文本最开头的几个字节,如下表:
开头字节 Charset/encoding
EF BB BF UTF-8
FE FF UTF-16/UCS-2, little endian
FF FE UTF-16/UCS-2, big endian
FF FE 00 00 UTF-32/UCS-4, little endian.
00 00 FE FF UTF-32/UCS-4, big-endian.例如插入标记后,连通”两个字的UTF-16 (big endian)和UTF-8码分别为:
FF FE DE 8F 1A 90
EF BB BF E8 BF 9E E9 80 9A
但是MBCS文本没有这些位于开头的字符集标记,更不幸的是,一些早期的和一些设计不良的软件在保存Unicode文本时不插入这些位于开头的字符集标记。因此,软件不能依赖于这种途径。这时,软件可以采取一种比较安全的方式来决定字符集及其编码,那就是弹出一个对话框来请示用户,例如将那个“连通”文件拖到MS Word中,Word就会弹出一个对话框。
如果软件不想麻烦用户,或者它不方便向用户请示,那它只能采取自己“猜”的方法,软件可以根据整个文本的特征来猜测它可能属于哪个charset,这就很可能不准了。使用记事本打开那个“连通”文件就属于这种情况。
我们可以证明这一点:在记事本中键入“连通”后,选择“Save As”,会看到最后一个下拉框中显示有“ANSI”,这时保存。当再当打开“连通”文件出现乱码后,再点击“File”->“Save As”,会看到最后一个下拉框中显示有“UTF-8”,这说明记事本认为当前打开的这个文本是一个UTF-8编码的文本。而我们刚才保存时是用ANSI字符集保存的。这说明,记事本猜测了“连通”文件的字符集,认为它更像一个UTF-8编码文本。这是因为“连通”两个字的GB-2312编码看起来更像UTF-8编码导致的,这是一个巧合,不是所有文字都这样。可以使用记事本的打开功能,在打开“连通”文件时在最后一个下拉框中选择ANSI,就能正常显示了。反过来,如果之前保存时保存为UTF-8编码,则直接打开也不会出现问题。
如果将“连通”文件放入MS Word中,Word也会认为它是一个UTF-8编码的文件,但它不能确定,因此会弹出一个对话框询问用户,这时选择“简体中文(GB2312)”,就能正常打开了。记事本在这一点上做得比较简化罢了,这与这个程序的定位是一致的。
需要提醒大家的是,部分Windows 2000字型无法显示所有的Unicode字符。如果发现文件中缺少了某些字符,只需将其变更为其它字型即可。
big endian和little endian
big endian和little endian是CPU处理多字节数的不同方式。例如“汉”字的Unicode编码是6C49。那么写到文件里时,究竟是将6C写在前面,还是将49写在前面?如果将6C写在前面,就是big endian。还是将49写在前面,就是little endian。
“endian”这个词出自《格列佛游记》。小人国的内战就源于吃鸡蛋时是究竟从大头(Big-Endian)敲开还是从小头(Little-Endian)敲开,由此曾发生过六次叛乱,其中一个皇帝送了命,另一个丢了王位。
我们一般将endian翻译成“字节序”,将big endian和little endian称作“大尾”和“小尾”。
Unicode big endian:在Big-endian处理器(如苹果Macintosh电脑)上建立的Unicode文件中的文字位元组(存放单位)排列顺序,与在Intel处理器上建立的文件的文字位元组排列顺序相反。最重要的位元组拥有最低的地址,且会先储存文字中较大的一端。为使这类电脑的用户能够存取你的文件,可选择Unicode big-endian格式。
************************************************************************************
关于char,wchar_t, TCHAR, _T(),L,宏 _T、TEXT,_TEXT、L
http://www.cnblogs.com/txwsh1/archive/2008/03/06/1093335.html
L"HELLO"
_T("HELLO")
************************************************************************************
CString 和 LPCTSTR 之间的转换 及 LPSTR、LPWSTR、LPCSTR、LPCWSTR、LPTSTR、LPCTSTR的区分与转化(转)
LPSTR:即 char *,指向以'\0'结尾的8位(单字节)ANSI字符数组指针
LPWSTR:即wchar_t *,指向'\0'结尾的16位(双字节)Unicode字符数组指针
LPCSTR:即const char *
LPCWSTR:即const wchar_t *
LPTSTR:LPSTR、LPWSTR两者二选一,取决于是否宏定义了UNICODE或ANSI
LPCTSTR: LPCSTR、LPCWSTR两者二选一,取决于是否宏定义了UNICODE或ANSI,如下是从MFC库中拷来的:
#ifdef UNICODE
typedef LPWSTR LPTSTR;
typedef LPCWSTR LPCTSTR;
#else
typedef LPSTR LPTSTR;
typedef LPCSTR LPCTSTR;
#endif
相互转换方法:
LPWSTR->LPTSTR: W2T();
LPTSTR->LPWSTR: T2W();
LPCWSTR->LPCSTR: W2CT();
LPCSTR->LPCWSTR: T2CW();
ANSI->UNICODE: A2W();
UNICODE->ANSI: W2A();
另外,CString转为CStringW方法(通过一个wchar_t数组来转)
CString str;
CStringW strw;
wchar_t *text = new wchar_t[sizeof(wchar_t) * str.GetLength()];
MultiByteToWideChar(CP_ACP,0,str,-1,text,str.GetLength());
strw = text;
vc2008
c肽
gb2312编码:0x63,0xebc4
unicode编码:0x6300,0xbd80
utf-8编码:0x63,0xe882bd
void Ctestt3Dlg::OnBnClickedButton1()
{
//多字节编码即本地编码gb2312
char *p;
LPSTR lp="c肽";//char* lp="c肽"
p=lp;
for (int i=0;i<10;i++)
{
TRACE("p=%x\n",*p);
p++;
}
TRACE("\n");
//宽字节编码unicode
LPWSTR lpw=L"c肽";//wchar_t* lpw=L"c肽"
p=(char*)lpw;
for (int i=0;i<10;i++)
{
TRACE("p=%x\n",*p);
p++;
}
TRACE("\n");
//工程属性里已选择使用多字节编码,则cstring使用gb2312编码保存字符串
CString str1="c肽";
for (int i=0;i<str1.GetLength();i++)
{
TRACE("p=%x\n",str1.GetAt(i));
}
}
//输出为
p=63
p=ffffffeb
p=ffffffc4
p=0
p=49
p=6e
p=76
p=61
p=6c
p=69
p=63
p=0
p=ffffffbd
p=ffffff80
p=0
p=0
p=0
p=0
p=a
p=0
p=63
p=ffffffeb
p=ffffffc4
工程属性里如果选择使用unicode编码,则cstring使用unicode编码保存字符串
比如上面的
CString str2=L"c肽";
for (int i=0;i<str2.GetLength();i++)
{
TRACE("p=%x\n",str2.GetAt(i));
}
会输出
p=63
p=80bd
//构造unicode码
wchar_t uni[]={0x0063,0x80bd,0x0};
char *p=(char*)uni;
for (int i=0;i<10;i++)
{
TRACE("p=%x\n",*p);
p++;
}
MessageBox(uni);//c肽
//输出
p=63
p=0
p=ffffffbd
p=ffffff80
p=0
p=0
p=ffffffcc
p=ffffffcc
p=ffffffcc
p=ffffffcc
***********************************************
unicode-->utf8
[字符集]Unicode和UTF-8之间的转换详解
utf8-->unicode
vc2008
// testf2Dlg.cpp : 实现文件
//
#include "stdafx.h"
#include "testf2.h"
#include "testf2Dlg.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
// 用于应用程序“关于”菜单项的 CAboutDlg 对话框
class CAboutDlg : public CDialog
{
public:
CAboutDlg();
// 对话框数据
enum { IDD = IDD_ABOUTBOX };
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV 支持
// 实现
protected:
DECLARE_MESSAGE_MAP()
};
CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD)
{
}
void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
}
BEGIN_MESSAGE_MAP(CAboutDlg, CDialog)
END_MESSAGE_MAP()
// Ctestf2Dlg 对话框
Ctestf2Dlg::Ctestf2Dlg(CWnd* pParent /*=NULL*/)
: CDialog(Ctestf2Dlg::IDD, pParent)
{
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}
void Ctestf2Dlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
}
BEGIN_MESSAGE_MAP(Ctestf2Dlg, CDialog)
ON_WM_SYSCOMMAND()
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
//}}AFX_MSG_MAP
ON_BN_CLICKED(IDC_BUTTON1, &Ctestf2Dlg::OnBnClickedButton1)
ON_BN_CLICKED(IDC_BUTTON2, &Ctestf2Dlg::OnBnClickedButton2)
END_MESSAGE_MAP()
// Ctestf2Dlg 消息处理程序
BOOL Ctestf2Dlg::OnInitDialog()
{
CDialog::OnInitDialog();
// 将“关于...”菜单项添加到系统菜单中。
// IDM_ABOUTBOX 必须在系统命令范围内。
ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
ASSERT(IDM_ABOUTBOX < 0xF000);
CMenu* pSysMenu = GetSystemMenu(FALSE);
if (pSysMenu != NULL)
{
CString strAboutMenu;
strAboutMenu.LoadString(IDS_ABOUTBOX);
if (!strAboutMenu.IsEmpty())
{
pSysMenu->AppendMenu(MF_SEPARATOR);
pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
}
}
// 设置此对话框的图标。当应用程序主窗口不是对话框时,框架将自动
// 执行此操作
SetIcon(m_hIcon, TRUE); // 设置大图标
SetIcon(m_hIcon, FALSE); // 设置小图标
// TODO: 在此添加额外的初始化代码
return TRUE; // 除非将焦点设置到控件,否则返回 TRUE
}
void Ctestf2Dlg::OnSysCommand(UINT nID, LPARAM lParam)
{
if ((nID & 0xFFF0) == IDM_ABOUTBOX)
{
CAboutDlg dlgAbout;
dlgAbout.DoModal();
}
else
{
CDialog::OnSysCommand(nID, lParam);
}
}
// 如果向对话框添加最小化按钮,则需要下面的代码
// 来绘制该图标。对于使用文档/视图模型的 MFC 应用程序,
// 这将由框架自动完成。
void Ctestf2Dlg::OnPaint()
{
if (IsIconic())
{
CPaintDC dc(this); // 用于绘制的设备上下文
SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);
// 使图标在工作区矩形中居中
int cxIcon = GetSystemMetrics(SM_CXICON);
int cyIcon = GetSystemMetrics(SM_CYICON);
CRect rect;
GetClientRect(&rect);
int x = (rect.Width() - cxIcon + 1) / 2;
int y = (rect.Height() - cyIcon + 1) / 2;
// 绘制图标
dc.DrawIcon(x, y, m_hIcon);
}
else
{
CDialog::OnPaint();
}
}
//当用户拖动最小化窗口时系统调用此函数取得光标
//显示。
HCURSOR Ctestf2Dlg::OnQueryDragIcon()
{
return static_cast<HCURSOR>(m_hIcon);
}
void Ctestf2Dlg::OnBnClickedButton1()//u-8
{
// TODO: 在此添加控件通知处理程序代码
wchar_t uni[20]=L"c肽";
char utf_8[40]={0};
char* p=NULL;
p=(char*)uni;
for(int i=0;i<10;i++)
{
TRACE("unicode p=%x\n",*p);
p++;
}
int len=UCS2UTF8(uni,utf_8);
uni[len]=0;
p=(char*)utf_8;
for(int i=0;i<10;i++)
{
TRACE("utf8 p=%x\n",*p);
p++;
}
}
void Ctestf2Dlg::OnBnClickedButton2()//8-u
{
// TODO: 在此添加控件通知处理程序代码
wchar_t uni[20]={0};
char utf_8[40]={0x63,0xe8,0x82,0xbd,0};//最后一个0是字符串结束符,c肽
char* p=NULL;
p=(char*)utf_8;
for(int i=0;i<10;i++)
{
TRACE("utf8 p=%x\n",*p);
p++;
}
int len=UTF82UCS(utf_8,strlen(utf_8),uni,40);//unicode字符的个数,2
TRACE("strlen(utf_8)=%d\n",strlen(utf_8));//4
TRACE("len=%d\n",len);
AfxMessageBox(uni);//c肽
p=(char*)uni;
for(int i=0;i<10;i++)
{
TRACE("unicode p=%x\n",*p);
p++;
}
}
int Ctestf2Dlg::UCS2UTF8(const wchar_t* pUCS,char* pUTF8)
{
int UCSlen = 0, UTF8len = 0, i;
char* pTempUTF8 = NULL;
UCSlen = wcslen(pUCS);
if(pUCS == NULL || pUTF8 == NULL)
return -1;
pTempUTF8 = pUTF8;
for(i = 0; i < UCSlen; i++)
{
if(pUCS[i] <= 0x007F)//1 byte 0xxxxxxx
{
*(pTempUTF8++) = LOBYTE(pUCS[i]);
UTF8len++;
}
else if(pUCS[i] <=0x07FF)//2 bytes 110xxxxx 10xxxxxx
{
*(pTempUTF8++) = HIBYTE(pUCS[i] << 2) & 0x3F | 0xC0;
*(pTempUTF8++) = LOBYTE(pUCS[i] & 0x3f) | 0x80;
UTF8len += 2;
}
else//3 bytes 1110xxxx 10xxxxxx 10xxxxxx
{
*(pTempUTF8++) = HIBYTE(pUCS[i] >> 4) | 0xe0;
*(pTempUTF8++) = HIBYTE(pUCS[i] << 2) & 0x3F | 0x80;
*(pTempUTF8++) = LOBYTE(pUCS[i]) & 0x3F | 0x80;
UTF8len += 3;
}
}
return UTF8len;
}
int Ctestf2Dlg::UTF82UCS( const char* UTF8String, int UTF8StringLength, wchar_t* OutUnicodeString, int UnicodeStringBufferSize )
{
int UTF8Index = 0;
int UniIndex = 0;
while ( UTF8Index < UTF8StringLength )
{
unsigned char UTF8Char = UTF8String[UTF8Index];
if ( UnicodeStringBufferSize != 0 && UniIndex >= UnicodeStringBufferSize )
break;
if ((UTF8Char & 0x80) == 0)
{
const int cUTF8CharRequire = 1;
// UTF8字码不足
if ( UTF8Index + cUTF8CharRequire > UTF8StringLength )
break;
if ( OutUnicodeString )
{
wchar_t& WideChar = OutUnicodeString[UniIndex];
WideChar = UTF8Char;
}
UTF8Index++;
}
else if((UTF8Char & 0xE0) == 0xC0) ///< 110x-xxxx 10xx-xxxx
{
const int cUTF8CharRequire = 2;
// UTF8字码不足
if ( UTF8Index + cUTF8CharRequire > UTF8StringLength )
break;
if ( OutUnicodeString )
{
wchar_t& WideChar = OutUnicodeString[UniIndex];
WideChar = (UTF8String[UTF8Index + 0] & 0x3F) << 6;
WideChar |= (UTF8String[UTF8Index + 1] & 0x3F);
}
UTF8Index += cUTF8CharRequire;
}
else if((UTF8Char & 0xF0) == 0xE0) ///< 1110-xxxx 10xx-xxxx 10xx-xxxx
{
const int cUTF8CharRequire = 3;
// UTF8字码不足
if ( UTF8Index + cUTF8CharRequire > UTF8StringLength )
break;
if ( OutUnicodeString )
{
wchar_t& WideChar = OutUnicodeString[UniIndex];
WideChar = (UTF8String[UTF8Index + 0] & 0x1F) << 12;
WideChar |= (UTF8String[UTF8Index + 1] & 0x3F) << 6;
WideChar |= (UTF8String[UTF8Index + 2] & 0x3F);
}
UTF8Index += cUTF8CharRequire;
}
else if((UTF8Char & 0xF8) == 0xF0) ///< 1111-0xxx 10xx-xxxx 10xx-xxxx 10xx-xxxx
{
const int cUTF8CharRequire = 4;
// UTF8字码不足
if ( UTF8Index + cUTF8CharRequire > UTF8StringLength )
break;
if ( OutUnicodeString )
{
wchar_t& WideChar = OutUnicodeString[UniIndex];
WideChar = (UTF8String[UTF8Index + 0] & 0x0F) << 18;
WideChar = (UTF8String[UTF8Index + 1] & 0x3F) << 12;
WideChar |= (UTF8String[UTF8Index + 2] & 0x3F) << 6;
WideChar |= (UTF8String[UTF8Index + 3] & 0x3F);
}
UTF8Index += cUTF8CharRequire;
}
else ///< 1111-10xx 10xx-xxxx 10xx-xxxx 10xx-xxxx 10xx-xxxx
{
const int cUTF8CharRequire = 5;
// UTF8字码不足
if ( UTF8Index + cUTF8CharRequire > UTF8StringLength )
break;
if ( OutUnicodeString )
{
wchar_t& WideChar = OutUnicodeString[UniIndex];
WideChar = (UTF8String[UTF8Index + 0] & 0x07) << 24;
WideChar = (UTF8String[UTF8Index + 1] & 0x3F) << 18;
WideChar = (UTF8String[UTF8Index + 2] & 0x3F) << 12;
WideChar |= (UTF8String[UTF8Index + 3] & 0x3F) << 6;
WideChar |= (UTF8String[UTF8Index + 4] & 0x3F);
}
UTF8Index += cUTF8CharRequire;
}
UniIndex++;
}
return UniIndex;
}