代码之家  ›  专栏  ›  技术社区  ›  Evan Carslake

C++问题,将wchar_t*转换为字符串

  •  0
  • Evan Carslake  · 技术社区  · 10 年前

    我这里有问题。这是Unicode格式。我有一个字符串表,其中包含值,用 ; 。我整天都在做这件事,结果总是立即出现运行时错误。

    Stringtable看起来像:

    `blah;blah;foo;bar;car;star`
    

    然后代码:

    // More than enough size for this
    const int bufferSize = 2048;
    
    // Resource ID to a StringTable
    int resid = IDS_MAP;
    wchar_t readMap[bufferSize];            
    resid = LoadString(NULL, resid, readMap, bufferSize);  
    
    wchar_t* line;
    line = wcstok(readMap,L";");
    
    while (line != NULL) {
    
        line = wcstok(NULL,L";");
        wstring wstr(line); // Problem
        string str(wstr.begin(), wstr.end()); // Problem
    
        MessageBox(0,line,0,0) // No problem
    }
    

    问题是当我试图转换 wchar_t* line wstring string 。若我取消注释这两行,它运行正常,消息框显示正常。

    有什么想法吗?在这里问这个问题是我最后的办法。谢谢

    1 回复  |  直到 10 年前
        1
  •  1
  •   Remy Lebeau    10 年前

    本声明:

    line = wcstok(readMap,L";");
    

    读取第一个分隔符 line 在缓冲区中。好啊

    但是,在循环中,以下语句:

    line = wcstok(NULL,L";");
    

    位于 顶部 因此 扔掉 第一次迭代的第一行,然后读取 下一个 定界的 线 。最终,循环将到达缓冲区的末端 wcstok() 将返回NULL,但在使用之前没有检查该条件 线 :

    line = wcstok(readMap,L";"); // <-- reads the first line
    
    while (line != NULL) {
    
        line = wcstok(NULL,L";"); // <-- 1st iteration throws away the first line
        wstring wstr(line); // <-- line will be NULL on last iteration
    
        //...
    }
    

    这个 line = wcstok(NULL,L";"); 语句需要移动到 底部 而不是:

    wchar_t* line = wcstok(readMap, L";");
    
    while (line != NULL)
    {
        // use line as needed...
    
        line = wcstok(NULL, L";");
    }
    

    我建议更改 while 循环到 for 循环以强制执行:

    for (wchar_t* line = wcstok(readMap, L";"); (line != NULL); line = wcstok(NULL, L";"))
    {
        // use line as needed...
    }
    

    另一方面,由于您使用的是C++,因此应该考虑使用 std:wistringstream std:getline() 而不是 wcstok() :

    #include <string>
    #include <sstream>
    
    // after LoadString() exits, resid contains the
    // number of character copied into readMap...
    std::wistringstream iss(std::wstring(readMap, resid));
    
    std::wstring line;
    while (std::getline(iss, line, L';'))
    {
        // use line as needed...
    }
    

    但无论如何,这种说法都是错误的:

    string str(wstr.begin(), wstr.end()); // Problem
    

    这句话行得通 正确地 仅当 std::wstring 包含#0-#127范围内的ASCII字符。对于非ASCII字符 而是执行数据转换以避免Unicode字符的数据丢失>U+00FF。

    由于您在Windows上运行,因此可以使用Win32 API WideCharToMultiByte() 功能:

    std::wstring line;
    while (std::getline(iss, line, L';'))
    {
        std::string str;
    
        // optionally substitute CP_UTF8 with any ANSI codepage you want...
        int len = WideCharToMultiByte(CP_UTF8, 0, line.c_str(), line.length(), NULL, 0, NULL, NULL);
        if (len > 0)
        {
            str.resize(len);
            WideCharToMultiByte(CP_UTF8, 0, line.c_str(), line.length(), &str[0], len, NULL, NULL);
        }
    
        // use str as needed...
        MessageBoxW(0, line.c_str(), L"line", 0);
        MessageBoxA(0, str.c_str(), "str", 0);
    }
    

    或者,如果您使用的是C++11或更高版本,则可以使用 std::wstring_convert 类(但仅适用于UTF/16/32转换):

    #include <locale> 
    
    std::wstring line;
    while (std::getline(iss, line, L';'))
    {
        std::wstring_convert<std::codecvt_utf8<wchar_t>, wchar_t> conv;
        std::string str = conv.to_bytes(line);
    
        // use str as needed...
        MessageBoxW(0, line.c_str(), L"line", 0);
        MessageBoxA(0, str.c_str(), "str", 0);
    }