首先,我要说您最好不要将TCHAR
用于新的Windows项目,而应直接使用Unicode。关于实际答案:
字符集
我们需要了解的第一件事是字符集如何在Visual Studio中工作。项目属性页面具有一个选择使用的字符集的选项:
未设置使用Unicode字符集使用多字节字符集根据您选择的三个选项中的哪一个,许多定义都会更改以适应所选的字符集。主要有三个类:字符串,
tchar.h
中的字符串例程和API函数:
“未设置”与使用ANSI编码的TCHAR = char
对应,在此您将系统的标准8位代码页用于字符串。所有tchar.h
字符串例程都使用基本的char
版本。所有与字符串一起使用的API函数都将使用API函数的“A”版本。 'Unicode'对应于使用UTF-16编码的TCHAR = wchar_t
。所有tchar.h
字符串例程都使用wchar_t
版本。所有与字符串一起使用的API函数都将使用API函数的“W”版本。 使用某些多字节编码方案,“多字节”对应于TCHAR = char
。所有tchar.h
字符串例程都使用多字节字符集版本。所有与字符串一起使用的API函数都将使用API函数的“A”版本。 相关阅读:
About the "Character set" option in visual studio 2010TCHAR.h header
tchar.h
header 是用于对字符串的C字符串操作使用通用名称的帮助程序,这些名称针对给定的字符集切换到正确的函数。例如,
_tcscat
将切换为
strcat
(未设置),
wcscat
(unicode)或
_mbscat
(mbcs)。
_tcslen
将切换为
strlen
(未设置),
wcslen
(unicode)或
strlen
(mbcs)。
通过将所有
_txxx
符号定义为可评估正确函数的宏来进行切换,具体取决于编译器的切换。
其背后的想法是,您可以使用
TCHAR
中的与编码无关的类型
_TCHAR
(或
tchar.h
)和对它们起作用的与编码无关的函数,而不是
string.h
中的常规字符串函数。
同样,
_tmain
定义为
main
或
wmain
。另请参阅:
What is the difference between _tmain() and main() in C++?定义了一个辅助宏
_T(..)
来获取正确类型的字符串文字,即
"regular literals"
或
L"wchar_t literals"
。
请参阅此处提到的警告:
Is TCHAR still relevant? -- dan04's answer_tmain
示例
对于问题中的main的示例,以下代码将所有作为命令行参数传递的字符串连接在一起。
int _tmain(int argc, _TCHAR *argv[])
{
TCHAR szCommandLine[1024];
if (argc < 2) return 0;
_tcscpy(szCommandLine, argv[1]);
for (int i = 2; i < argc; ++i)
{
_tcscat(szCommandLine, _T(" "));
_tcscat(szCommandLine, argv[i]);
}
/* szCommandLine now contains the command line arguments */
return 0;
}
(省略了错误检查)此代码适用于字符集的所有三种情况,因为在各处我们都使用
TCHAR
,
tchar.h
字符串函数和
_T
作为字符串文字。编写此类
_T(..)
-programs时,忘记用
TCHAR
包围字符串文字是编译器错误的常见原因。
如果我们还没有做完所有这些事情,那么切换字符集将导致代码在运行时无法编译,或者更糟糕的是编译但行为异常。
Windows API函数
在字符串上起作用的Windows API函数(例如
CreateFile
和
GetCurrentDirectory
)在Windows header 中以宏的形式实现,与
tchar.h
宏的宏一样,切换为“A”版本或“W”版本。例如,
CreateFile
是一个宏,为ANSI和MBCS定义为
CreateFileA
,为Unicode定义为
CreateFileW
。
每当您在代码中使用平面形式(不带“A”或“W”)时,实际调用的函数将根据所选字符集进行切换。您可以通过使用显式的“A”或“W”名称来强制使用特定版本。
结论是,除非应始终引用特定版本,否则应始终使用非限定名称,而与字符集选项无关。
对于问题中的示例,我们要在其中打开第一个参数给出的文件:
int _tmain(int argc, _TCHAR *argv[])
{
if (argc < 2) return 1;
HANDLE hFile = CreateFile(argv[1], GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL);
/* Read from file and do other stuff */
...
CloseHandle(hFile);
return 0;
}
(省略了错误检查)请注意,在此示例中,我们不需要在任何地方使用任何
TCHAR
特定的东西,因为宏定义已经为我们解决了这一问题。
使用C++字符串
我们已经看到了如何使用
tchar.h
例程来使用C样式字符串操作来与
TCHAR
一起使用,但是如果我们可以利用C++
string
来进行此操作,那将是很好的。
我的建议最重要的是不要使用
TCHAR
而是直接使用Unicode,请参阅“结论”部分,但是如果要使用
TCHAR
,可以执行以下操作。
要使用
TCHAR
,我们想要的是一个使用
std::basic_string
的
TCHAR
实例。您可以通过
typedef
自己的
tstring
来做到这一点:
typedef std::basic_string<TCHAR> tstring;
对于字符串文字,请不要忘记使用
_T
。
您还需要使用正确的
cin
和
cout
版本。您可以使用引用来实现
tcin
和
tcout
:
#if defined(_UNICODE)
std::wistream &tcin = wcin;
std::wostream &tcout = wcout;
#else
std::istream &tcin = cin;
std::ostream &tcout = cout;
#end
这应该使您几乎可以做任何事情。偶尔会有异常(exception),例如
std::to_string
和
std::to_wstring
,您可以找到类似的解决方法。
结论
这个答案(希望如此)详细说明了
TCHAR
是什么以及如何将其与Visual Studio和Windows header 结合使用。但是,我们还应该怀疑是否要使用它。
我的建议是直接对所有新的Windows程序使用Unicode,而根本不使用TCHAR
! 其他提供相同建议的人:
Is TCHAR still relevant?要在创建新项目后使用Unicode,请首先确保将字符集设置为Unicode。然后,从您的源文件(或从
#include <tchar.h>
)中删除
stdafx.h
。将
TCHAR
或
_TCHAR
修复为
wchar_t
,将
_tmain
修复为
wmain
:
int wmain(int argc, wchar_t *argv[])
对于非控制台项目,Windows应用程序的入口点是
WinMain
,并将在
TCHAR
-jargon中显示为
int APIENTRY _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow)
并且应该成为
int APIENTRY wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int nCmdShow)
此后,仅使用
wchar_t
字符串和/或
std::wstring
。
进一步警告
使用sizeof(szMyString)
数组(字符串)时,在编写TCHAR
时要小心,因为对于ANSI,这是字符和字节的大小,对于Unicode,这只是字节的大小,字符数最多为一半,对于MBCS这是字节大小,字符数可以相等或可以不相等。 Unicode和MBCS都可以使用多个TCHAR
来编码单个字符。 将TCHAR
内容与固定的char
或wchar_t
混合使用非常烦人;您必须使用正确的代码页将字符串从一个转换为另一个!在一般情况下,简单的副本将不起作用。 _UNICODE
和UNICODE
之间有细微的差别,如果要有条件地定义自己的函数,则相关。参见Why both UNICODE and _UNICODE? 一个很好的补充答案是:
Difference between MBCS and UTF-8 on Windows