一 编码的历史
单字节编码
2.1.1 ASCII 0-127 7位表示2.1.2 ASCII扩展码 0-255 8位表示
代码页:通过代码页来切换对应的
多字节编码
2.1.3 双字节字符集 DBCS使用一个或两个字节表示字符.
"A中B国"
12 1 2
A: 0x41 中:0x8051
B: 0x42 国:0x8253
1 2 3 4 5 6
0x41 0x80 0x51 0x42 0x82 0x53
A 中 B 国
这样既包含多字节又包含单字节的成为多字节编码
宽字节编码
2.1.4 Unicode全部使用2个字节表示字符
"A 中 B 国"
2 2 2 2
A: 0x0041 中:0x8051
B: 0x0042 国:0x8253
1 2 3 4 5 6 7 8
41 00 51 80 42 00 53 82
内存/硬盘等资源占用变大.
对编程支持度.
二 C语言和编码
1 单字节的字符和字符串
char cText = 'A';char * pszText = "ABCD";
2 宽字节的字符
wchar_t cText = 'A'wchar_t * pszText = L"ABCD";
3 相关函数
单字字符的函数,对应有多.宽字节的函数.strlen wcslen mbslen
printf wprintf
4 TCHAR
为了程序中可以方便的支持的Unicode和多字节字符等,所以使用TCHAR来定义字符和字符串.根据_UNICODE宏开关,会将TCHAR编译成不同字符类型.
#ifndef _UNICODE
typedef char TCHAR
#define __T(x) x
#else
typedef wchar_t TCHAR
#define __T(x) L##x
#endif
使用时,要增加TCHAR.H头文件支持,使用
_UNICODE 宏开关进行编译
CL window.c /D_UNICODE
#define _UNICODE
#include "tchar.h"
定义方式:
TCAHR * pszText = __T("ABCDEF");
代码使用:使用UNICODE宏开关,通知
编译器选择编译的代码.
#ifndef _UNICODE
int nLen = strlen( pszText );
#else
int nLen = wcslen( pszText );
#endif
2.5 Unicode的控制台打印
BOOL WriteConsole(
HANDLE hConsoleOutput, //控制台输出流的句柄
CONST VOID *lpBuffer,//输出的字符串的指针
DWORD nNumberOfCharsToWrite,//输出的字符串的长度
LPDWORD lpNumberOfCharsWritten,
// 返回已输出字符的数量
LPVOID lpReserved ); // 保留值
代码实例:
#include "stdafx.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <windows.h>
#include <tchar.h> //TCHAR
void ascii()
{
for(int i=0;i<255;i++)
{
printf("%c ",i);
}
printf("\n");
//因为现在是中文字符集 而中文是双字节的 所以 128以后的都变成问号了
}
void codePage(int nCodePage)
{
/*
BOOL SetConsoleCP(
UINT wCodePageID // code page to set
);
437 美国 936 中国
*/
SetConsoleOutputCP(nCodePage);
for(int i=0;i<255;i++)
{
printf("%c ",i);
}
printf("\n");
}
void c_char()
{
char *pszText = "hello world";
int nLen = strlen(pszText);
printf("%d,%s\n",nLen,pszText);
}
void c_wchar()
{
SetConsoleOutputCP(936);
wchar_t cText = 'A'; //char 可以指直接转换为wchar_t
// wchar_t *pszText = "ABCD"; 错误 无法从const char[5] 转换成wchar_t *
wchar_t *pszText = L"ABCD"; //L告诉编译器 字符串是双字节字符串
int nLen = wcslen(pszText); // 这里是4 和以前一样 表示有4个双字节
//printf("%s\n",pszText); //发现只打印了A 因为A的aci是41 变成双字节后 在小端模式内存中是 41 00 所以按单字节打印时只显示A了 查看内存方法 调试-窗口-内存
wprintf(L"%d,%s\n",nLen,pszText);// 格式化输出之前要写个L
//宽字节字符串
wchar_t *pszChs = L"我是程序员";
nLen = wcslen(pszChs);
wprintf(L"%d,%s\n",nLen,pszChs); //5 ???? printf 对unicode的支持不高 所以汉字打印不出来
//多字节字符串
char * ptr = "我是程序员";
nLen = strlen(ptr);
printf("%d,%s\n",nLen,ptr); //10 我是程序员
}
void tchar()
{
TCHAR *pszText = __T("我是程序员"); // __T 根据是否是unicode在前面加L
#ifndef _UNICODE
int len = strlen(pszText);
#else
int len = wcslen(pszText);
#endif
printf("%d\n",len);
}
void printUnicode(wchar_t *pszStr)
{
//如何打印UNICODE WINDOWS API
/*
BOOL WriteConsole(
HANDLE hConsoleOutput, // handle to a console screen buffer
CONST VOID *lpBuffer, // pointer to buffer to write from
DWORD nNumberOfCharsToWrite, // number of characters to write
LPDWORD lpNumberOfCharsWritten, // pointer to number of characters written
LPVOID lpReserved // reserved
);
*/
HANDLE hout = GetStdHandle(STD_OUTPUT_HANDLE); //得到console的句柄
int nLen = wcslen(pszStr);
WriteConsole(hout,pszStr,nLen,NULL,NULL);
/*
//将所有的unicode打印出来
wchar_t szText[2] = {0};
for(BYTE nHigh=0;nHigh<0xFF;nHigh++)
{
for(BYTE nLow=0;nLow<0xFF;nLow++)
{
szText[0] = MAKEWORD(nLow,nHigh); //宏 MAKEWORD 构成一个字
WriteConsole(hout,szText,wcslen(szText),NULL,NULL);
}
}
*/
}
int _tmain(int argc, _TCHAR* argv[])
{
printf("hello world\n");
c_char();
ascii();
printf("----------------------\n");
codePage(437);
c_wchar();
tchar();
printUnicode(L"我是程序员\n");
system("pause");
return 0;
}
三 Win32程序与编码
1 Win32 API的定义
每个API对多字节字符和UNICODE分别有不同的版本.
MessageBox
MessageBoxA 多字节字符
MessageBoxW UNICODE字符
2 字符的定义,使用TEXT, 由Winnt.h提供定义
#ifdef UNICODE#define __TEXT(quote) L##quote
#else /* UNICODE */
#define __TEXT(quote) quote
#endif /* UNICODE */
TCHAR * pszText = TEXT( "ABCD" );
3 字符转换
宽字节到多字节
UINT CodePage, //代码页 CP_ACP ascii码
DWORD dwFlags, //转换方式 0
LPCWSTR lpWideCharStr, //需要被转换WCHAR地址
int cchWideChar, //需要被转换WCHAR的长度
LPSTR lpMultiByteStr,//用于存放转换后的结果BUFF
int cchMultiByte, //BUFF的长度
LPCSTR lpDefaultChar,//使用的缺省字符串的地址 NULL
LPBOOL lpUsedDefaultChar //缺省字符串被使用的标识 0
);
多字节到宽字节
int MultiByteToWideChar(
UINT CodePage,// 代码页
DWORD dwFlags,// 转换方式
LPCSTR lpMultiByteStr, // 需要被转换CHAR地址
int cchMultiByte,//需要被转换CHAR的长度
LPWSTR lpWideCharStr,//用于存放转换后的结果BUFF
int cchWideChar );//BUFF的长度
使用方法:
1 将要转换的字符串,传递给函数(cchWideChar 或者 cchMultiByte = 0),从 返回值中获取转换后字符串的元素个数。
2 分配字符串空间
3 再次调用函数,并将分配的空间传递给函数,获取结果.
4 例子
void MyMessageBox()
{
MessageBox(NULL,TEXT("helloWide"),TEXT("宽的"),MB_OK);
}
void Multi2Wide()
{
CHAR * pszText = "MultiByte";
//先测算一次长度
int nLen = MultiByteToWideChar(CP_ACP,0,pszText,strlen(pszText),NULL,0);
// 拿到长度后分配空间
wchar_t *pwszText = (wchar_t *)malloc((nLen+1)*sizeof(wchar_t)); //多一个wchar空间来存放\0\0
memset(pwszText,0,(nLen+1)*sizeof(wchar_t));
//再进行转换
MultiByteToWideChar(CP_ACP,0,pszText,strlen(pszText),pwszText,nLen);
MessageBoxW(NULL,pwszText,TEXT("Multi2Wide"),MB_OK);
free(pwszText);
};
void Wide2Multi()
{
WCHAR *pwszText = TEXT("Widebyte");
int nLen = WideCharToMultiByte(CP_ACP,0,pwszText,wcslen(pwszText),NULL,0,NULL,0);
char *pszText = (char*)malloc((nLen+1)*sizeof(char));
memset(pszText,0,(nLen+1)*sizeof(char));
WideCharToMultiByte(CP_ACP,0,pwszText,wcslen(pwszText),pszText,nLen,NULL,0);
MessageBoxA(NULL,pszText,"Wide2Multi",MB_OK);
};