ANSI与Unicode编码,TCHAR | LPSTR | LPCSTR | LPWSTR | LPCWSTR | LPTSTR | LPCTSTR 的含义

一个字符可以用1-byte表示,即ANSI编码;

一个字符也可用2-bytes表示,即Unicode编码(Unicode其实还包含了更多内容,不止2-bytes)。

Visual C++支持char和wchar_t作为ANSI和Unicode的原始数据类型。

例如

char cResponse; // ‘Y‘ or ‘N‘
char sUsername[64];
// str* functions

以及

wchar_t cResponse; // ‘Y‘ or ‘N‘
wchar_t sUsername[64];
// wcs* functions

它们可以统一写成

#include<TCHAR.H> // Implicit or explicit include
TCHAR cResponse; // ‘Y‘ or ‘N‘
TCHAR sUsername[64];
// _tcs* functions

TCHAR则是根据选择的字符集决定是翻译成char还是wchar_t,字符集的设置如下:

所以TCHAR的定义如下:

#ifdef _UNICODE
typedef wchar_t TCHAR;
#else
typedef char TCHAR;
#endif

在windows中,一般前缀 代表了它可以自适应不同的字符集。

比如:strcpystrlenstrcat(包括安全后缀_s)代表ANSI版本;

         wcscpywcslenwcscat(包括安全后缀_s),代表Unicode版本,这里WC代表Wide Character;

_tcscpy_tcslen_tcscat则视情况而定:

size_t strlen(const char*); //ANSI
size_t wcslen(const wchar_t* ); //Unicdoe
size_t _tcslen(const TCHAR* ); //ANSI or Unicode


我们知道一个string使用双引号表示,这种表示说明它是一个ANSI-string,每个字符占1-byte,例如:

"This is ANSI String. Each letter takes 1 byte."

要转换成Unicdeo-string需要加前缀:L

[__strong__]L"This is Unicode string. Each letter would take 2 bytes, including spaces."

Unicode编码的字符,每个都占用2-bytes,哪怕是可以用1-byte表示的,比如英文字母,数字,null字符等。所以一个unicode-string占用的字节总是2-bytes的倍数。

结合上面提到的 T 前缀,一种适用于两种字符集的写法是这样的:

"ANSI String"; // ANSI
L"Unicode String"; // Unicode

_T("Either string, depending on compilation"); // ANSI or Unicode

_TTEXT是一个宏定义,它与前缀 T 表示的意思一样,定义如下:

// SIMPLIFIED
#ifdef _UNICODE
 #define _T(c) L##c
 #define TEXT(c) L##c
#else
 #define _T(c) c
 #define TEXT(c) c
#endif

上面的##叫“token-pasting operator”。在Unicode下,_T("Unicode")被翻译成 L"Unicode";在ANSI下,_T("Unicode")被翻译成 “Unicode”。

注意,不能通过_T来转换一个变量(string or character),下面的操作是不允许的:

char c = ‘C‘;
char str[16] = "CodeProject";

_T(c);
_T(str);

如果你是在ANSI(Multi-Byte)下编译,可以顺利通过,_T(c), _T(str)被翻译成c, str;

但是在Unicode下编译,就会报错:

error C2065: ‘Lc‘ : undeclared identifier
error C2065: ‘Lstr‘ : undeclared identifier

结合_T的定义不难弄懂。

在windows中,几乎所有需要传入string或character的API,都有通用的版本,例如: SetWindowTextA/W,就可以统一写成:

BOOL SetWindowText(HWND, const TCHAR*);

但我们知道SetWindowText是一个宏,它代表了以下两种之一:

BOOL SetWindowTextA(HWND, const char*);
BOOL SetWindowTextW(HWND, const wchar_t*);

但其实,在内部实现时,不论ANSI还是Unicode都统一通过Unicode方式实现,当你调用 SetWindowTextA 时(传入ANSI-string),它会先转化成Unicode-string,再调用 SetWindowTextW实现。真正发挥作用的只有Unicode的版本!

所以在写代码时建议是直接调用Unicode版本的api,尽管我们对ANSI版本的string更熟悉。

Note:存在另外一个typedef:WCHAR,它等价于wchar_t



我们知道strlen定义如下:

size_t strlen(const char*);

它也可以写成

size_t strlen(LPCSTR);

所以

// Simplified
typedef const char* LPCSTR;  

它的含义如下

  • LP: Long Pointer
  • C: Constant
  • STR: String

Long Pointer与Pointer意思一样。

举一反三,对于Unicode字符,我们有:

size_t wcslen(const wchar_t* szString); // Or WCHAR*
size_t wcslen(LPCWSTR szString);

这里 LPCWSTR代表

typedef const WCHAR* LPCWSTR;

它的含义如下

  • LP - Pointer
  • C - Constant
  • WSTR - Wide character String

更进一步,有LPCTSTR

  • LP - Pointer
  • C - Constant
  • T = TCHAR
  • STR = String

总结:

  • TCHAR - char / wchar_t (取决于字符集)
  • LPSTR - char*
  • LPCSTR - const char*
  • LPWSTR - wchar_t*
  • LPCWSTR - const wchar_t*
  • LPTSTR - TCHAR*
  • LPCTSTR - const TCHAR*


在编程中有时候会因为选择的字符集不同,而编译出错,如下面的写法在ANSI下没事,但在Unicode下就会报错:

int main()
{
    TCHAR name[] = "Saturn";
    int nLen; // Or size_t

    lLen = strlen(name);
}
  • error C2440: ‘initializing‘ : cannot convert from ‘const char [7]‘ to ‘TCHAR []‘
  • error C2664: ‘strlen‘ : cannot convert parameter 1 from ‘TCHAR []‘ to ‘const char *‘

同样的问题出现在:

nLen = wcslen("Saturn");
// ERROR: cannot convert parameter 1 from ‘const char [7]‘ to ‘const wchar_t *‘

遗憾的是,上面的错误不能通过强制转换的方法修改:

nLen = wcslen((const wchar_t*)"Saturn");

上面的写法会得到错误的结果,往往导致越界。原因是“Saturn”占用7个字节

‘S‘(83) a‘(97) t‘(116) u‘(117) r‘(114) n‘(110) \0‘(0)

但传给wcslen的时候,对于每个字符分配2-bytes。因此头两个字节[83,97]被看作一个字符,value:(97<<8 | 83),是字符‘?‘.后面的以此类推。

所以如果用Unicode的api,需要提前转换:

TCHAR name[] = _T("Saturn");
//或者
wcslen(L"Saturn");

在之前的例子中,strlen(name)中的name在Unicode下编译,每个字符占2-bytes,如果强制转换成ANSI:

lLen = strlen ((const char*)name);

也会出现问题,‘S‘原来表示为[83,0],但在ANSI中第一个字节[83]可以被正确翻译成‘S‘,但接着第二个字节[0]直接被翻译为为‘\0‘,结束了整个字符串。所以strlen得到的结果为1

综上,C语言风格的强制转换在这里是行不通的。



如果需要分配内存,在C++中通过new直接指定字符的个数,不用去管具体分配了多少字节:

LPTSTR pBuffer; // TCHAR* 

pBuffer = new TCHAR[128]; // Allocates 128 or 256 BYTES, depending on compilation.

但如果你是用malloc,LocalAlloc,GlobalAlloc这类api分配空间,就需要指定具体的字节数:

pBuffer = (TCHAR*) malloc (128 * sizeof(TCHAR) );
时间: 2024-10-12 19:42:17

ANSI与Unicode编码,TCHAR | LPSTR | LPCSTR | LPWSTR | LPCWSTR | LPTSTR | LPCTSTR 的含义的相关文章

LPSTR LPCSTR LPWSTR LPCWSTR区别

LPSTR   一个32位的指向字符串的指针    LPCSTR   一个32位的指向字符串常量的指针    LPWSTR   一个32位的指向unicode字符串的指针    LPCWSTR   个32位的指向unicode字符串常量的指针 前面的L代表LONG,P就是指针的意思,C就是constant的意思,W是wide的意思,STR就是string的意思 LPSTR = char *LPCSTR = const char *LPWSTR = wchar_t *LPCWSTR = const

ASCII、ANSI、Unicode编码

3.1 ASCII编码 以下来自"维基百科": ASCII(American Standard Code for Information Interchange,美国信息互换标准代码)是基于拉丁字母的一套电脑编码系统.它主要用于显示现代英语,而其扩展版本EASCII则可以勉强显示其他西欧语言.它是现今最通用的单字节编码系统(但是有被UniCode追上的迹象),并等同于国际标准ISO/IEC 646. ASCII第一次以规范标准的型态发表是在1967年,最后一次更新则是在1986年,至今

【ANSI编码和Unicode编码】

最近在对之前的代码进行优化,主要是把界面全部换成GUI形式的(就是市面上的这种),之前进行数据查询的时候没问题,但是换成GUI的时候,由于输入的的是Unicode编码,而处理的时候又强制转换成ANSI编码,经过一番查阅(刚开始还以为强制转换是万能的....),才发现...看来之前还是低估了它~~~ 以下是相关的介绍 常规的ANSI编码(使用""包裹)和Unicode编码(使用L""包裹),这样对应的就有了两套字符串处理函数,比如:strlen和wcslen,分别用于

unicode,ansi,utf-8,unicode big endian编码的区别

作者:于洋链接:https://www.zhihu.com/question/23374078/answer/69732605来源:知乎著作权归作者所有.商业转载请联系作者获得授权,非商业转载请注明出处. 很久很久以前,有一群人,他们决定用8个可以开合的晶体管来组合成不同的状态,以表示世界上的万物.他们看到8个开关状态是好的,于是他们把这称为"字节".再后来,他们又做了一些可以处理这些字节的机器,机器开动了,可以用字节来组合出很多状态,状态开始变来变去.他们看到这样是好的,于是它们就这

【转】【编码】ANSI,ASCII,Unicode,UTF8

不同的国家和地区制定了不同的标准,由此产生了 GB2312.GBK.GB18030.Big5.Shift_JIS 等各自的编码标准.这些使用多个字节来代表一个字符的各种汉字延伸编码方式,称为 ANSI 编码.在简体中文Windows操作系统中,ANSI 编码代表 GBK 编码:在繁体中文Windows操作系统中,ANSI编码代表Big5:在日文Windows操作系统中,ANSI 编码代表 Shift_JIS 编码.不同 ANSI 编码之间互不兼容,当信息在国际间交流时,无法将属于两种语言的文字,

Native2asciiUtil 文本文件转UNICODE编码文件(支持UTF-8,Unicode,UTF-16BE,ANSI|ASCII,GBK)

package com.ctl.util; import java.io.File; import java.io.IOException; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.Set; /** * * @author Administrator * @Description \u5C06\u6587\u672C\u6587\u4EF6\u8F6C\

Ansi,UTF8,Unicode,ASCII编码的差别

近日须要不同的编码,关于上述编码,一直迷迷糊糊,查了些资料,总算大致了解了,以下全是从网上搜来的: 1.  ASCII和Ansi编码    字符内码(charcter code)指的是用来代表字符的内码.读者在输入和存储文档时都要使用内码,内码分为     单字节内码 -- Single-Byte character sets (SBCS),能够支持256个字符编码.     双字节内码 -- Double-Byte character sets)(DBCS),能够支持65000个字符编码.前者

Ansi,UTF8,Unicode,ASCII编码的区别 ---我看完了 明白了很多

来自:http://blog.csdn.net/xiongxiao/article/details/3741731 ------------------------------------------------------------------------ 近日需要不同的编码,关于上述编码,一直迷迷糊糊,查了些资料,总算大致了解了,下面全是从网上搜来的: 1.  ASCII和Ansi编码    字符内码(charcter code)指的是用来代表字符的内码.读者在输入和存储文档时都要使用内码

ANSI, UTF-8, Unicode, GBK, GB2312 字符编码小结

这两天碰见一个Bug,涉及到字符编码,一通乱搜后,索性研究了下,整理出来,以便今后查阅. ASCII码,0~127,128个,这个就不用多说了,他是计算机文明的基石.但是这里面只有英文字母,其他国家如何把本国的文字输入到电脑中呢?所以基本每种语言都有自己的字符集,如汉字就有GB2312(简体中文),BIG5(繁体中文)等等. 但不同的ANSI编码在不同语言之间是不兼容的,所以对于不同的操作系统之间文件的传输,或者在同样的操作系统下,源文件语言不同于OS的语言文件的传输,需要转换成UTF-8格式.