自动为带引号的字符串添加 _T 宏以及其他多字节-Unicode 转换
在VC++中移植到Unicode配置时,为引用的字符串添加_T宏。
引言
本文将介绍如何将Visual Studio C++项目从多字节配置移植到Unicode,并特别强调
- 自动为引用的字符串添加
_T("")
宏。 - 将
std::string
、std::ostringstream
和std::ofstream
移植到兼容Unicode的版本。 - 在
std::string
中存储Unicode值。
背景
很久以前,我开始了一个Visual Studio 6 C++项目,我写的第一行代码是
AfxMessageBox("Hello World!");
当我点击“构建”时,直到我将项目配置从默认的Unicode更改为多字节,它才能编译。
从那一刻起,我就知道我最好在每个字符串前面加上_T
宏,但过了一段时间,我停止这样做了。
一年后,当项目变成一个20万行代码的庞然大物时,我被要求将程序翻译成其他语言,如俄语和中文。将项目配置改回Unicode后,出现了成千上万的错误,主要是因为引用的文本没有添加_T
宏。
本文将介绍如何使用Visual Studio的宏资源管理器,自动为引用的字符串添加_T("")
宏。
自动为引用的字符串添加_T("")宏
使用代码
- 在Visual Studio中打开你的项目。
- 在顶部主菜单中,选择“工具 -> 宏 -> 宏资源管理器”。宏资源管理器面板应该会出现在屏幕的右侧。
- 右键单击“MyMacros”并选择“新建模块”。
- 精确地输入以下名称:“AutoT”。
- 右键单击新创建的模块并选择“编辑”。
- 将以下文本粘贴进去,覆盖自动生成的代码中已有的几行。
- 保存并关闭宏。
请注意,尽管以下代码是用VBScript编写的,但它是为C++程序设计的。
Imports System
Imports EnvDTE
Imports EnvDTE80
Imports System.Diagnostics
Public Module AutoT
Sub ReplaceXWithY(ByVal X As String, ByVal Y As String, _
Optional ByVal MatchCase As Boolean = False, _
Optional ByVal PatternSyntax As _
EnvDTE.vsFindPatternSyntax = _
vsFindPatternSyntax.vsFindPatternSyntaxLiteral)
DTE.Find.Action = vsFindAction.vsFindActionReplace
DTE.Find.FindWhat = X
DTE.Find.ReplaceWith = Y
DTE.Find.Target = vsFindTarget.vsFindTargetOpenDocuments
DTE.Find.MatchCase = MatchCase
DTE.Find.MatchWholeWord = False
DTE.Find.Backwards = False
DTE.Find.MatchInHiddenText = False
DTE.Find.PatternSyntax = PatternSyntax
If (DTE.Find.Execute() = vsFindResult.vsFindResultNotFound) Then
Throw New System.Exception("vsFindResultNotFound")
End If
End Sub
Sub QuotedTextTo_T()
ReplaceXWithY("{:q}", "_T(\1)", True, _
vsFindPatternSyntax.vsFindPatternSyntaxRegExpr)
End Sub
End Module
当你回到你的项目时,你会在右侧的宏资源管理器面板中看到一个名为“AutoT
”的宏。
每次双击该宏时,它将标记当前打开的C++文件中下一个引用的文本。再点击一次宏,它就会用_T
宏包装文本。
AfxMessageBox("Hello World!");
将被改为
AfxMessageBox(_T("Hello World!"));
并可以在多字节和Unicode配置中进行编译!
建议为宏添加键盘快捷键
- 在Visual Studio的顶部主菜单中,选择“工具 -> 选项”
- 点击左侧“环境”旁边的+号
- 点击“键盘”
- 在右侧窗格中,在“显示包含命令的:”编辑框中键入“AutoT”,以找到我们新的宏。
- 点击宏,并为其分配一个快捷键(我选择了Ctrl-Alt-Num0)。
- 单击“确定”
现在,每次按下该键盘组合时,宏就会被执行。
请注意:不要被诱惑而盲目地让脚本完成所有工作。需要人工验证。脚本会尝试将_T
宏添加到像这样的行中
#include "StdAfx.h"
为了让脚本跳过这样的行,只需按键盘上的“右”箭头键。
该脚本也不够智能,无法识别文本中的引号,比如
AfxMessageBox(_T("Hello \"World!\" "));
它也无法跳过已经被_T宏包装的引用的文本,但好消息是它永远不会跳过一个引用的文本:)
请注意,您还必须将所有类似strcmp
的出现重命名为TCHAR.H例程,如_tcscmp
。
将std::string、std::ostringstream、std::ofstream移植到兼容Unicode的版本
如果在你的多字节项目中广泛使用了std::string
、std::ostringstream
或std::ofstream
,这些在Unicode编译中表现不佳。
最简单的方法是定义以下内容,并将你的程序中所有std::string
的出现重命名为tstring
,例如。
#include <string>
typedef std::basic_string<TCHAR> tstring;
typedef std::basic_ostringstream<TCHAR> tostringstream;
typedef std::basic_ofstream<TCHAR> tofstream;
此外,将代码中的所有char替换为TCHAR
。
在std::string中存储Unicode值
要存储Unicode值,可以使用std::wstring
,但当必须将Unicode值存储在标准的std::string
或char数组中时,可以将其存储为UTF-8格式,TinyXML等也使用了这种格式。
以下辅助函数可能有助于您进行转换
std::string CStringToString(const CString& cs)
{
// Convert a TCHAR string to a LPCSTR
CT2CA pszConvertedAnsiString (cs);
// construct a std::string using the LPCSTR input
//std::string strStd (pszConvertedAnsiString);
return pszConvertedAnsiString;
}
tstring CStringTo_tstring(const CString& cs)
{
std::basic_string <TCHAR> bsConverted (cs);
return bsConverted;
}
std::string tstringTo_stdString(const tstring& ts)
{
return CStringToString(ts.c_str()).c_str();
}
tstring UTF8charTo_tstring( const char* strIn )
{
wchar_t buffer[2048];//!!H
MultiByteToWideChar(CP_UTF8, 0, strIn, -1, buffer, 2048 );//!!H
tstring ts1 = CString(buffer);
return ts1;
}
std::string tstringToUTF8string( tstring tsIn )
{
char buffer[2048];//!!H
WideCharToMultiByte( CP_UTF8, 0, tsIn.c_str() , -1, buffer, 2048, NULL, NULL );
std::string s1 = buffer;
return s1;
}
bool HasUnicodeChars( tstring tsIn )
{
std::string sNarrow = tstringTo_stdString(tsIn);
tstring tsFromNarrow = CString(sNarrow.c_str());
if ( tsFromNarrow != tsIn )
return true;
else
return false;
}
将tstring
转换为char*
CStringToString(sName.c_str()).c_str()
将std::string
转换为tstring
tstring ts30 = CString(stdS1.c_str());
注意:UTF-8字符串类似于char字符串,但Unicode字母可能需要两个字符,使字符串变长。
tstring ts1;
ts1 = _T("Some foreign language text");
int nLen = ts1.length();
int nSize = ts1.size();
//Convert wide string to std string
std::string s2 = tstringToUTF8string(ts1);
int nLen2 = s2.length();
int nSize2 = s2.size();
//Convert std string back to wide string
tstring ts5 = UTF8charTo_tstring(s2.c_str());