C++基础——字符、字符集、字符串相关

基本概念:

字节(Byte):是计算机信息技术用于计量存储容量的一种计量单位,也表示一些计算机编程语言中的数据类型和语言字符。Byte是从0-255的无符号类型,所以不能表示负数 [3]  。

字符:和字节不同,任何一个文字或符号都是一个字符,但不同的编码格式使一个字符所占的内存不同(比如:GBK编码中汉字占2个字节,UTF-8 编码中一个汉字占 3个字节)。

编码规范:为了显示字符,国际组织就制定了编码规范,希望用不同的二进制数来表示不同的字符。

字库表:一个相当于存储了编码规范中能显示的所有字符的数据库,计算机能根据不同的二进制数从字库表中找到对应的字符

编码字符集:在一个字库表中,每一个字符都有一个对应的二进制地址,而字符集就是这些地址的集合。

字符编码:有字库表和编码字符集后,就可以直接使用二进制地址来得到字符了,但直接使用字符对应的二进制地址来显示文字是十分浪费的(如:Unicode 编码规范中包括了几百万个字符,想要包括几百万个不同的字符,起码需要3个字节的容量,为了方便将来扩展,Unicode还保留了更多未使用的空间,最多可以存储4个字节的容量),因此为了区分每个字符,哪怕是00000000 00000000 00000000 00001111这种其实只占了1个字节的字符,我们也要为他分配4个字节的空间,这是极其浪费的做法;于是就需要制定不同的算法来节省空间,而每种不同的算法就被成为字符编码。

常见编码规范:

ASCII码

ASCII码,是最早产生的编码规范,一共包含00000000~01111111共128个字符,可以表示阿拉伯数字和大小写英文字母,以及一些简单的符号。可以看出ASCII码只需要1个字节的存储空间,最高位为0。后被称为(American Standard Code for Information Interchange,美国信息交换标准代码)。它没有特定的编码方式,直接使用地址对应的二进制数来表示,非要说那就叫他ASCII 编码方式。

GBK

GBK全称《汉字内码扩展规范》,支持国际标准ISO/IEC10646-1和国家标准GB13000-1中的全部中日韩汉字。GBK字符集中所有字符占2个字节,不论中文英文都是2个字节。 没有特殊的编码方式,习惯称呼GBK 编码。一般在国内,汉字较多时使用。

ISO-8859-1

ISO-8859-1收录的字符除ASCII收录的字符外,还包括西欧语言、希腊语、泰语、阿拉伯语、希伯来语对应的文字符号。因为ISO-8859-1编码范围使用了单字节内的所有空间,在支持ISO-8859-1的系统中传输和存储其他任何编码的字节流都不会被抛弃。换言之,把其他任何编码的字节流当作ISO-8859-1编码看待都没有问题。这是个很重要的特性,MySQL数据库默认编码是Latin1就是利用了这个特性。ASCII编码是一个7位的容器,ISO-8859-1编码是一个8位的容器。由此可见,ISO-8859-1只占1个字节,且MySQL数据库默认编码就是ISO-8859-1,有时,tomcat服务器默认也是使用ISO-8859-1编码,然而ISO-8859-1是不支持中文的,有时这就是在浏览器上显示乱码的原因。

Unicode

从以上几种编码规范可以看出,各种编码规范互不兼容,且只能表示自己需要的字符,于是,国际标准化组织(ISO)决定制定一套全世界通用的编码规范,这就是Unicode。

Unicode包含了全世界所有的字符。Unicode最多可以保存4个字节容量的字符。也就是说,要区分每个字符,每个字符的地址需要4个字节。这是十分浪费存储空间的,于是,程序员就设计了几种字符编码方式,比如:UTF-8,UTF-16,UTF-32。最广为程序员使用的就是UTF-8,UTF-8是一种变长字符编码,注意:UTF-8不是编码规范,而是字符编码的一种方式。

Window的字符串

字符类型

在Windows中主要包含两种字符,一种是ANSI字符,另一种是Unicode字符(使用UTF16编码,即每个字符编码为2个字节)。C++中表示ANSI字符的关键字是char,然后每个字符的长度是1;而表示Unicode字符的类型是wchar_t,每个字符的长度是2。而为了和语言有一些区别,Windows又定义了自己的数据类型:

typedef  char  CHAR; //8位字符

typedef  wchar_t  WCHAR;//16位字符

字符串

Windows定义了非常多的字符指针或字符串指针:

// 8位字符指针

typedef CHAR*  PCHAR;

typedef CHAR*  PSTR;

typedef CONST CHAR *PCSTR

// 16位字符指针

typedef WCHAR  *PWCHAR;

typedef WCHAR  *PWSTR;

typedef CONSTWCHAR   *PCWSTR;

Windows为了保证使用ANSI或Unicode都能通过编译,又定义了下面的类型:

#ifdef  UNICODE                     // r_winnt

#ifndef _TCHAR_DEFINED
typedef WCHAR TCHAR, *PTCHAR;
typedef WCHAR TBYTE , *PTBYTE ;
#define _TCHAR_DEFINED
#endif /* !_TCHAR_DEFINED */

typedef LPWCH LPTCH, PTCH;
typedef LPCWCH LPCTCH, PCTCH;
typedef LPWSTR PTSTR, LPTSTR;
typedef LPCWSTR PCTSTR, LPCTSTR;
typedef LPUWSTR PUTSTR, LPUTSTR;
typedef LPCUWSTR PCUTSTR, LPCUTSTR;
typedef LPWSTR LP;
typedef PZZWSTR PZZTSTR;
typedef PCZZWSTR PCZZTSTR;
typedef PUZZWSTR PUZZTSTR;
typedef PCUZZWSTR PCUZZTSTR;
typedef PZPWSTR PZPTSTR;
typedef PNZWCH PNZTCH;
typedef PCNZWCH PCNZTCH;
typedef PUNZWCH PUNZTCH;
typedef PCUNZWCH PCUNZTCH;
#define __TEXT(quote) L##quote      // r_winnt

#else   /* UNICODE */               // r_winnt

#ifndef _TCHAR_DEFINED
typedef char TCHAR, *PTCHAR;
typedef unsigned char TBYTE , *PTBYTE ;
#define _TCHAR_DEFINED
#endif /* !_TCHAR_DEFINED */

typedef LPCH LPTCH, PTCH;
typedef LPCCH LPCTCH, PCTCH;
typedef LPSTR PTSTR, LPTSTR, PUTSTR, LPUTSTR;
typedef LPCSTR PCTSTR, LPCTSTR, PCUTSTR, LPCUTSTR;
typedef PZZSTR PZZTSTR, PUZZTSTR;
typedef PCZZSTR PCZZTSTR, PCUZZTSTR;
typedef PZPSTR PZPTSTR;
typedef PNZCH PNZTCH, PUNZTCH;
typedef PCNZCH PCNZTCH, PCUNZTCH;
#define __TEXT(quote) quote         // r_winnt

#endif /* UNICODE */                // r_winnt

上面可以看出,定义了UNICODE 宏, TCHAR等同于WCHAR ,而如果没有定义UNICODE ,则 TCHAR等同于CHAR。其他的一些表示:P代表Point(指针),W代码Unicode,C代表Const,__TEXT则能自动将字符串转换为ANSI类型字符串或UNICODE类型字符串而L加字符串则自动表示为Unicode类型字符串,L是对应于16位OS而已代表long,也就是长指针,而现在32位的OS上所有前面加L的类型和不加L的类型已经没有任何区别了。

UNICODE 与 ANSI 之间的转换

可以通过windows的API实现,MultiByteToWideChar表示将ANSI字符串转换为Unicode字符串,而WideCharToMultiByte则是将Unicode字符串转换为ANSI字符串。

int
WINAPI
MultiByteToWideChar(
    _In_ UINT CodePage,
    _In_ DWORD dwFlags,
    _In_NLS_string_(cbMultiByte) LPCCH lpMultiByteStr,
    _In_ int cbMultiByte,
    _Out_writes_to_opt_(cchWideChar, return) LPWSTR lpWideCharStr,
    _In_ int cchWideChar
    );

int
WINAPI
WideCharToMultiByte(
    _In_ UINT CodePage,
    _In_ DWORD dwFlags,
    _In_NLS_string_(cchWideChar) LPCWCH lpWideCharStr,
    _In_ int cchWideChar,
    _Out_writes_bytes_to_opt_(cbMultiByte, return) LPSTR lpMultiByteStr,
    _In_ int cbMultiByte,
    _In_opt_ LPCCH lpDefaultChar,
    _Out_opt_ LPBOOL lpUsedDefaultChar
    );

CodePage:多字符所对应的的字符集,一般是CP_ACP 

dwFlags:一些标准位,一般不需要使用,置0即可

lpMultiByteStr:多字符的地址

cchMultiBytes:多字符的字符串长度,相当于字符串的字节数,如果是1,则由函数判断长度

lpWideCharStr:宽字符的地址

cchWideChar:宽字符的字符长度,相当于字符串的个数

内部实现(TODO)

UTF-8 与 UTF-16 之间的转换

(TODO)

UTF-16 与 UTF-32 之间的转换

#include <Windows.h>
using namespace std;

void utf16Tutf32(DWORD* pDes, int iDesLength, const WORD* pSrc, int iSrcLength)
{
	WORD h;
	WORD l;
	int iLen = 0;
	for (int i = 0; i<iSrcLength; i++)
	{
		if (pSrc[i]<0xD800 || pSrc[i]>0xDFFF)
		{
			pDes[i] = (DWORD)pSrc[i];
		}
		else
		{
			h = pSrc[i] - 0xD800;
			l = pSrc[i] - 0xDC00;
			pDes[i] = ((h << 10) | l) + 0x10000;
		}
		iLen = i;
	}
}
void utf32Tutf16(WORD* pDes, int iDesLength, const DWORD* pSrc, int iSrcLength)
{
	int tmplen = 0;
	for (int i = 0; i<iSrcLength; i++)
	{
		if (pSrc[i]>0xFFFF)
		{
			DWORD tmp = 0x0010000UL;
			pDes[tmplen] = 0xD800 | (tmp >> 10);
			tmplen++;
			pDes[tmplen] = 0xDC00 | (tmp & 0x03FF);
			tmplen++;
		}
		else
		{
			pDes[tmplen] = pSrc[i];
			tmplen++;
		}
	}
}

 

以上参考:


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值