将_TCHAR*转换为char*

6

我正在尝试在Windows上使用C++使一个简单的OpenCV示例运行起来,但我的C++已经非常生疏了。

这个示例相当简单:

#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <iostream>

using namespace cv;
using namespace std;

int main( int argc, char** argv )
{
    if( argc != 2)
    {
        cout <<" Usage: display_image ImageToLoadAndDisplay" << endl;
        return -1;
    }
    Mat image;
    image = imread(argv[1], IMREAD_COLOR); // Read the file
    if(! image.data )                      // Check for invalid input
    {
        cout <<  "Could not open or find the image" << std::endl ;
        return -1;
    }
    namedWindow( "Display window", WINDOW_AUTOSIZE ); // Create a window for display.
    imshow( "Display window", image );                // Show our image inside it.
    waitKey(0); // Wait for a keystroke in the window
    return 0;
}

当我在Visual Studio 2012中创建一个新的简单C++控制台应用程序(使用ATL),我会得到一个不同的main模板:
int _tmain( int argc, _TCHAR* argv[] )

在将文件名发送到OpenCV的imread函数之前,我需要将_TCHAR* arg[1]转换为char*。使用一个简单的文件名'opencv-logo.jpg',从内存窗口中可以看到_TCHAR每个字符占用两个字节。

o.p.e.n.c.v.-.l.o.g.o...j.p.g...
6f 00 70 00 65 00 6e 00 63 00 76 00 2d 00 6c 00 6f 00 67 00 6f 00 2e 00 6a 00 70 00 67 00 00 00

另一个回答中推荐的转换建议后,我正在尝试使用ATL 7.0 String Conversion Classes and Macros,通过插入以下代码:

char* filename = CT2A(argv[1]);

但是生成的内存是一团糟,肯定不是作为ASCII字符串的 'opencv-logo.jpg'。
fe fe fe fe fe fe fe fe fe fe ...
þþþþþþþþþþ ...

我应该使用哪种转换技术、函数或宏?
(注:这里可能是一个相关的问题,但我不知道如何在这里应用答案。)

您的项目模板正在尝试创建一个支持Unicode的C++项目。我不记得具体的项目/编译器选项,但应该有一些选项可以指定是否使用Unicode。 - OldProgrammer
在使用转换宏的每个函数顶部,您需要一个USES_CONVERSION;语句吗? - Duncan Smith
它在通用项目属性页面中。 - user786653
3个回答

21

最快的解决方法是将签名更改为标准签名。将以下内容替换:

int _tmain( int argc, _TCHAR* argv[] )

随着

int main( int argc, char *argv[] )

在Windows上,这意味着命令行参数会被转换为系统的区域编码,由于Windows不支持UTF-8,因此并非所有内容都能正确转换。但是,除非您实际上需要国际化,否则可能没有必要再做更多的工作。


我尝试了这个 - 我会编辑问题来解释发生了什么(我认为)。 - dumbledad
@dumbledad 好的。当你编辑完问题后,请在此处发表评论,这样我就能收到通知。 - bames53
2
@bames 这个确实有效。上次我使用了签名 int _tmain( int argc, char** argv ),结果 char* 变成了 6f 00 70 00 65 00 6e 00 63 00 76 00 2d 00 6c 00 6f 00 67 00 6f 00 2e 00 6a 00 70 00 67 00 00 00,这是字符 c 后面跟着 \0 加上更多的字节,即字符串 "c"!将 _tmain 替换为 main 就有了很大的区别,谢谢。 - dumbledad

7
_TCHAR,即TCHAR是一种取决于您项目设置的类型。它可以是wchar_t(当您使用Unicode时),也可以是char(当您使用多字节时)。您可以在“项目属性”-“常规”中找到此设置,其中有一个“字符集”设置。
可能最简单的事情就是使用多字节选项,并将_TCHAR*类型视为简单的char*,并尽快使用它来构造std::string对象:
std::string filename(argv[1]);

但是如果你需要大量使用特殊字符,那么我认为更合理的做法是使用Unicode,并尽可能使用std::wstring对象来保存字符串。如果是这种情况,那么只需使用std::wstring的构造函数即可:

std::wstring filename(argv[1]);

如果您需要使用宽字符串,有时您需要在宽字符串和多字节字符串之间进行转换,这些帮助程序可能会对您有所帮助:

// multi byte to wide char:
std::wstring s2ws(const std::string& str)
{
    int size_needed = MultiByteToWideChar(CP_UTF8, 0, &str[0], (int)str.size(), NULL, 0);
    std::wstring wstrTo(size_needed, 0);
    MultiByteToWideChar(CP_UTF8, 0, &str[0], (int)str.size(), &wstrTo[0], size_needed);
    return wstrTo;
}

// wide char to multi byte:
std::string ws2s(const std::wstring& wstr)
{
    int size_needed = WideCharToMultiByte(CP_ACP, 0, wstr.c_str(), int(wstr.length() + 1), 0, 0, 0, 0); 
    std::string strTo(size_needed, 0);
    WideCharToMultiByte(CP_ACP, 0, wstr.c_str(), int(wstr.length() + 1), &strTo[0], size_needed, 0, 0); 
    return strTo;
}

我建议您使用Unicode。当然,使用Unicode并不排除使用char*。通常我将Unicode数据编码为UTF-8,并坚持使用char* - bames53
@bames53:这取决于程序的上下文以及他将要使用的API,但我承认在这里使用多字节似乎是更好的选择。 - LihO
1
@bames53:没错,但不幸的是在Windows上,Unicode或多或少意味着UTF-16和wchar_t,因为这是Win32 API使用的。 - user786653
1
@dumbledad:你确定你正在使用多字节字符集吗? - LihO
很好地发现了@LihO。我已经在属性更改的常规部分中将字符集从“使用Unicode字符集”更改为“使用多字节字符集”,现在您的std :: string filename(argv [1]);filename.c_str()可以正常工作。谢谢! - dumbledad
显示剩余3条评论

-1

除了阅读基本没有变化的26,000页cpp 2003 VS手册之外...std::string test(print); test = "";

int _tmain( int argc, _TCHAR* argv[] )  

int main( int argc, char *argv[] ) 

除非您使用了某些安全功能,否则它们将起到相同的作用...并且不能在字符集属性中使用Unicode...如果您要在文件管理函数中使用CreateFile函数,除非以某种方式进行多线程处理,否则它也无法正常工作。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接