如何将const WCHAR *转换为const char *

24
CString output ;
const WCHAR* wc = L"Hellow World" ;
if( wc != NULL )
{   
     output.Append(wc);
}
printf( "output: %s\n",output.GetBuffer(0) );

1
请添加解释,因为纯代码并不能说明问题。 - Kao
你不需要使用 GetBuffer。CString 有一个 LPCTSTR 运算符,可以访问内部缓冲区。 - MikMik
1
如果 wcпривет мир,输出应该是什么?你是否关心代码页或者这只是宽到窄的转换,所有宽字符都是 ANSI 字符? - Zdeslav Vojkovic
7个回答

35

你也可以尝试这样做:

#include <comdef.h>  // you will need this
const WCHAR* wc = L"Hello World" ;
_bstr_t b(wc);
const char* c = b;
printf("Output: %s\n", c);

_bstr_t 实现了以下转换运算符,我发现这非常方便:

operator const wchar_t*( ) const throw( ); 
operator wchar_t*( ) const throw( ); 
operator const char*( ) const; 
operator char*( ) const;

编辑:针对答案评论的澄清:行 const char* c = b; 会创建并由 _bstr_t 实例管理的窄字符副本,该实例在销毁时将释放它。运算符只返回指向此副本的指针。因此,无需复制此字符串。此外,在问题中,CString::GetBuffer 返回 LPTSTR(即 TCHAR*),而不是 LPCTSTR(即 const TCHAR*)。

另一种选择是使用转换宏:

USES_CONVERSION;
const WCHAR* wc = L"Hello World" ;
const char* c = W2A(wc);

这种方法的问题在于转换后的字符串内存是在堆栈上分配的,因此字符串的长度受到限制。然而,这个转换宏族允许你选择用于转换的代码页,如果宽字符串包含非ANSI字符,则经常需要这样做。


为什么需要复制它?你的代码只是展示了你需要在 printf 中使用它。_bstr_t 会负责释放内存。如果你需要保留一份副本并将字符串传递出去,请使用 _bstr_t 实例,而不是 const char* - 在这个意义上,_bstr_t 类似于 CString。当多个对象的副本被使用时,它会正确地处理复制字符串数据的问题(尽管它不使用 写时复制)。 - Zdeslav Vojkovic
const WCHAR* wc = L"你好,世界"; c = _bstr_t(wc);printf( "输出: %s\n",c ); - jack
以下是关于编程的相关内容。仅返回翻译后的文本:输出如下:ε■ε■ε■ε■ε■ε■ε■ε■ε■ε■ε■ε■ε■ε■ε■ε■ε■ε■ε■ε■ε■ε■ε■ε■ε■ε■ε■ε■ε■ε■ε■ε■ε■ε■ε■ - jack
我已经尝试了与我的答案完全相同的代码,它可以正常工作。 - Zdeslav Vojkovic
抱歉,我写得太快了!请查看更新后的代码示例。你是完全正确的:原始代码创建了一个临时的bstr_t,在分配给c之后立即被销毁。我打字比思考更快,对于造成的混淆感到抱歉。 - Zdeslav Vojkovic
显示剩余2条评论

13
你可以使用sprintf来实现这个目的:
const char output[256];
const WCHAR* wc = L"Hellow World" ;
sprintf(output, "%ws", wc );

10
我认为你不能将 "output" 声明为 "const"。 - CinCout

6

我在Linux上的代码

// Debian GNU/Linux 8 "Jessie" (amd64)

#include <locale.h>
#include <stdlib.h>
#include <stdio.h>

// Use wcstombs(3) to convert Unicode-string (wchar_t *) to UTF-8 (char *)
// http://man7.org/linux/man-pages/man3/wcstombs.3.html

int f(const wchar_t *wcs) {
        setlocale(LC_ALL,"ru_RU.UTF-8");
        printf("Sizeof wchar_t: %d\n", sizeof(wchar_t));
        // on Windows, UTF-16 is internal Unicode encoding (UCS2 before WinXP)
        // on Linux, UCS4 is internal Unicode encoding
        for (int i = 0; wcs[i] > 0; i++) printf("%2d %08X\n",i,wcs[i]);
        char s[256];
        size_t len = wcstombs(s,wcs,sizeof(s));
        if (len > 0) {
                s[len] = '\0';
                printf("mbs: %s\n",s);
                for (int i = 0; i < len; i++)
                        printf("%2d %02X\n",i,(unsigned char)s[i]);
                printf("Size of mbs, in bytes: %d\n",len);
                return 0;
        }
        else return -1;
}

int main() {
        f(L"Привет"); // 6 symbols
        return 0;
}

如何构建

#!/bin/sh
NAME=`basename $0 .sh`
CC=/usr/bin/g++-4.9
INCS="-I."
LIBS="-L."
$CC ${NAME}.c -o _${NAME} $INCS $LIBS

输出

$ ./_test 
Sizeof wchar_t: 4
 0 0000041F
 1 00000440
 2 00000438
 3 00000432
 4 00000435
 5 00000442
mbs: Привет
 0 D0
 1 9F
 2 D1
 3 80
 4 D0
 5 B8
 6 D0
 7 B2
 8 D0
 9 B5
10 D1
11 82
Size of mbs, in bytes: 12

1

你可以这样做,或者你可以做一些更简洁的事情:

std::wcout << L"output: " << output.GetString() << std::endl;

1
为什么要使用 GetBuffer() 函数?这里有官方提供的 GetString() 函数,可以用来获取 C-字符串。 - Rost
@Rost 复制粘贴 :D 不需要大喊大叫 :D - Luchian Grigore
3
复制粘贴是邪恶的!!!真正的开发人员总是逐字重新输入!你不知道吗?!? :-D - Rost

1

这很容易,因为CString只是CStringT的typedef,而您还可以访问CStringACStringW(您应该阅读有关差异的文档)。

CStringW myString = L"Hello World";
CString myConvertedString = myString;

是的,我意识到了,但它是这样编写的,以更接近他的示例代码。 - Mark Ingram
这个转换会对没有匹配的窄字符的宽字符做什么处理? - M.M

1
你可以使用 std::wcsrtombs 函数。
以下是 C++17 版本的重载集合,用于转换:
#include <iostream> // not required for the conversion function

// required for conversion
#include <cuchar>
#include <cwchar>
#include <stdexcept>
#include <string>
#include <string_view> // for std::wstring_view overload

std::string to_string(wchar_t const* wcstr){
    auto s = std::mbstate_t();
    auto const target_char_count = std::wcsrtombs(nullptr, &wcstr, 0, &s);
    if(target_char_count == static_cast<std::size_t>(-1)){
        throw std::logic_error("Illegal byte sequence");
    }

    // +1 because std::string adds a null terminator which isn't part of size
    auto str = std::string(target_char_count, '\0');
    std::wcsrtombs(str.data(), &wcstr, str.size() + 1, &s);
    return str;
}

std::string to_string(std::wstring const& wstr){
    return to_string(wstr.c_str());
}

std::string to_string(std::wstring_view const& view){
    // wstring because wstring_view is not required to be null-terminated!
    return to_string(std::wstring(view));
}

int main(){
    using namespace std::literals;

    std::cout
        << to_string(L"wchar_t const*") << "\n"
        << to_string(L"std::wstring"s) << "\n"
        << to_string(L"std::wstring_view"sv) << "\n";
}

如果你使用的是C++17之前的版本,你应该紧急更新你的编译器!;-)

如果真的不可能更新编译器,这里有一个C++11版本:

#include <iostream> // not required for the conversion function

// required for conversion
#include <cwchar>
#include <stdexcept>
#include <string>

std::string to_string(wchar_t const* wcstr){
    auto s = std::mbstate_t();
    auto const target_char_count = std::wcsrtombs(nullptr, &wcstr, 0, &s);
    if(target_char_count == static_cast<std::size_t>(-1)){
        throw std::logic_error("Illegal byte sequence");
    }

    // +1 because std::string adds a null terminator which isn't part of size
    auto str = std::string(target_char_count, '\0');
    std::wcsrtombs(const_cast<char*>(str.data()), &wcstr, str.size() + 1, &s);
    return str;
}

std::string to_string(std::wstring const& wstr){
    return to_string(wstr.c_str());
}

int main(){
    std::cout
        << to_string(L"wchar_t const*") << "\n"
        << to_string(std::wstring(L"std::wstring")) << "\n";
}

0
您可以使用`sprintf`来实现此目的,正如@l0pan所提到的(但我使用了`%ls`而不是`%ws`):
char output[256];
const WCHAR* wc = L"Hello World" ;
sprintf(output, "%ws", wc ); // did not work for me (Windows, C++ Builder)
sprintf(output, "%ls", wc ); // works

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