如何将wstring转换为string?

271

问题是如何将wstring转换为string?

我有下面的例子:

#include <string>
#include <iostream>

int main()
{
    std::wstring ws = L"Hello";
    std::string s( ws.begin(), ws.end() );

  //std::cout <<"std::string =     "<<s<<std::endl;
    std::wcout<<"std::wstring =    "<<ws<<std::endl;
    std::cout <<"std::string =     "<<s<<std::endl;
}

注释掉的行的输出为:

std::string =     Hello
std::wstring =    Hello
std::string =     Hello

但是去掉之后只剩下:

std::wstring =    Hello

这个例子有什么问题吗?我可以像上面那样进行转换吗?

编辑

新的例子(考虑到一些答案)如下:

#include <string>
#include <iostream>
#include <sstream>
#include <locale>

int main()
{
    setlocale(LC_CTYPE, "");

    const std::wstring ws = L"Hello";
    const std::string s( ws.begin(), ws.end() );

    std::cout<<"std::string =     "<<s<<std::endl;
    std::wcout<<"std::wstring =    "<<ws<<std::endl;

    std::stringstream ss;
    ss << ws.c_str();
    std::cout<<"std::stringstream =     "<<ss.str()<<std::endl;
}

输出结果为:

std::string =     Hello
std::wstring =    Hello
std::stringstream =     0x860283c

所以stringstream不能用于将wstring转换为string。


5
你如何在不指定编码方式的情况下提出这个问题? - David Heffernan
5
为什么要使用std::wstring?http://stackoverflow.com/questions/1049947/should-utf-16-be-considered-harmful - dalle
19
如果你已经有用UTF-16编码的数据,那么UTF-16是否有害就有点无足轻重了。就我所知,任何转换形式都不是有害的;真正有害的是人们认为自己理解Unicode,但实际上并不理解。 - David Heffernan
2
它必须是跨平台的解决方案吗? - ali_bahoo
2
@tenfour相反。http://utf8everywhere.org/ - v.oddou
显示剩余16条评论
19个回答

386

正如Cubbi在评论中指出的那样,std::wstring_convert(C++11)提供了一个简洁的解决方案(您需要#include <locale><codecvt>):

std::wstring string_to_convert;

//setup converter
using convert_type = std::codecvt_utf8<wchar_t>;
std::wstring_convert<convert_type, wchar_t> converter;

//use converter (.to_bytes: wstr->str, .from_bytes: str->wstr)
std::string converted_str = converter.to_bytes( string_to_convert );

在我发现这个之前,我一直在使用wcstombs和繁琐的内存分配/释放组合。

http://en.cppreference.com/w/cpp/locale/wstring_convert

更新(2013.11.28)

可以将一行代码简述为以下内容(感谢Guss的评论):

std::wstring str = std::wstring_convert<std::codecvt_utf8<wchar_t>>().from_bytes("some string");

包装函数可以这样声明:(感谢ArmanSchwarz的评论)

std::wstring s2ws(const std::string& str)
{
    using convert_typeX = std::codecvt_utf8<wchar_t>;
    std::wstring_convert<convert_typeX, wchar_t> converterX;

    return converterX.from_bytes(str);
}

std::string ws2s(const std::wstring& wstr)
{
    using convert_typeX = std::codecvt_utf8<wchar_t>;
    std::wstring_convert<convert_typeX, wchar_t> converterX;

    return converterX.to_bytes(wstr);
}
注意:关于是否将string/wstring作为引用或文字传递给函数存在一些争议(由于C++11和编译器更新)。我将决定权留给实现人员,但了解这一点很重要。
注意:在上面的代码中,我使用std::codecvt_utf8,但如果您不使用UTF-8,您需要将其更改为您正在使用的适当编码: http://en.cppreference.com/w/cpp/header/codecvt

33
请点赞:这是官方的C++标准字符串转换方式。您还可以使用from_bytes方法进行反向转换。由于我个人喜欢单行代码,这是我的版本:std::wstring str = std::wstring_convert<std::codecvt_utf<wchar_t>>().from_bytes("some string"); - Guss
7
看起来在 g++ 4.8.2 中 http://en.cppreference.com/w/cpp/header/codecvt 不可用。在 Linux 下,s2ws 和 ws2s 两种方法目前无法使用。 - Begui
10
看起来这个已经被废弃了(https://dev59.com/E1gQ5IYBdhLWcg3wUiZS#42946556)。当我尝试运行这段代码时,我的编译器会抛出错误。 - adam_0
18
在C++17中已经不推荐使用...... https://zh.cppreference.com/w/cpp/locale/wstring_convert https://zh.cppreference.com/w/cpp/locale/codecvt_utf8 - Joma
16
对于任何担心C++17及其后向兼容性(由于已弃用功能)的人,请参见:https://dev59.com/bXE85IYBdhLWcg3w3Xhp#18597384。 - Timo
显示剩余9条评论

171

来自较早的解决方案:http://forums.devshed.com/c-programming-42/wstring-to-string-444006.html

std::wstring wide( L"Wide" ); 
std::string str( wide.begin(), wide.end() );

// Will print no problemo!
std::cout << str << std::endl;

更新(2021年):然而,在较新版本的MSVC上,这可能会生成wchar_tchar的截断警告。可以通过在转换函数中使用显式转换,例如使用std::transform来消除警告:

std::wstring wide( L"Wide" );

std::string str;
std::transform(wide.begin(), wide.end(), std::back_inserter(str), [] (wchar_t c) {
    return (char)c;
});

或者如果您更喜欢预分配并且不使用back_inserter

std::string str(wide.length(), 0);
std::transform(wide.begin(), wide.end(), str.begin(), [] (wchar_t c) {
    return (char)c;
});

在这里可以看到不同编译器的示例here


请注意,这里根本没有进行字符集转换。它只是将每个迭代的分配给一个< char > - 一种截断转换。它使用std::string c'tor
template< class InputIt >
basic_string( InputIt first, InputIt last,
              const Allocator& alloc = Allocator() );

如评论所述:

在几乎所有编码中,值为0-127的字符是相同的,因此截断所有小于127的值会得到相同的文本。但如果加入一个中文字符,则会导致失败。

“Windows代码页1252(Windows英语默认)的值为128-255和Unicode的值为128-255大部分是相同的,因此如果你使用的是这个代码页,那么大多数这些字符应该被截断为正确的值。(我完全预料到á和õ会工作,我知道我们工作中的代码依赖于é的这一点,我很快会修复它)”
“请注意,Win1252范围内的代码点在0x80 - 0x9F之间将无法正常工作。这包括€、œ、ž、Ÿ等。”

2
很奇怪,这在Visual Studio 10上可以工作。发生了什么?这应该会导致将原始字符串的所有元素从wchar_t截断赋值给char。 - Pedro Lamarão
6
当涉及到任何非拉丁字符时。 - JavaRunner
10
@PedroLamarão:在几乎所有编码中,值0-127是相同的,因此截断所有小于127的值会导致相同的文本。加入一个中文字符,你就会看到失败了。 - Mooing Duck
3
如果您使用的是Windows英语默认的代码页1252,那么Windows代码页1252中128-255的值和Unicode中128-255的值_大多数情况下_是相同的,因此如果那是您使用的代码页,则大部分这些字符应该被截断为正确的值。(我完全希望á和õ可以工作,我知道我们工作中的代码依赖于这一点,对于é,我很快就会修复) - Mooing Duck
1
这在我升级到MSVC 2019和v142工具集之前是可以工作的 - 现在它会因为一个警告而崩溃(我总是将其视为错误):warning C4244: 'argument': conversion from 'const wchar_t' to 'const _Elem', possible loss of data - YePhIcK
显示剩余8条评论

38

这里是一个基于其他建议的解决方案:

#include <string>
#include <iostream>
#include <clocale>
#include <locale>
#include <vector>

int main() {
  std::setlocale(LC_ALL, "");
  const std::wstring ws = L"ħëłlö";
  const std::locale locale("");
  typedef std::codecvt<wchar_t, char, std::mbstate_t> converter_type;
  const converter_type& converter = std::use_facet<converter_type>(locale);
  std::vector<char> to(ws.length() * converter.max_length());
  std::mbstate_t state;
  const wchar_t* from_next;
  char* to_next;
  const converter_type::result result = converter.out(state, ws.data(), ws.data() + ws.length(), from_next, &to[0], &to[0] + to.size(), to_next);
  if (result == converter_type::ok or result == converter_type::noconv) {
    const std::string s(&to[0], to_next);
    std::cout <<"std::string =     "<<s<<std::endl;
  }
}

这通常适用于Linux,但在Windows上会出现问题。


@Phillip:代码的哪一部分依赖于C语言环境?std::setlocale(LC_ALL, "");是否真的必要? - smerlin
2
使用 std::wcout.imbue(locale) 也可以完成任务,并且它的好处是不会改变任何全局状态。 - smerlin
33
C++11中的std::wstring_convert封装了很多这种繁琐的操作。 - Cubbi
8
@Philipp,你说的“会在Windows上创建问题”是什么意思?会产生哪些问题? - Gili
2
以上代码在 Linux 64 位 (gcc 4.7.3) 上运行时会出现 *** glibc detected *** test: malloc(): smallbin double linked list corrupted: 0x000000000180ea30 *** 的错误提示。还有其他人遇到过这个问题吗? - hogliux
显示剩余6条评论

17

默认编码:

  • Windows UTF-16。
  • Linux UTF-8。
  • MacOS UTF-8。

我的解决方案步骤,包括空字符\0(避免被截断)。在不使用windows.h头文件的函数的情况下:

  1. 添加宏以检测平台。 Windows/Linux 和其他平台
  1. 创建将std::wstring转换为std::string和将std::string转换为std::wstring的函数
  1. 创建打印函数
  1. 打印std::string/ std::wstring

查看原始字符串字面值。原始字符串后缀。

Linux代码。 直接使用std::cout打印std::string,Linux上的默认编码是UTF-8,不需要额外的函数。

在Windows上,如果您需要打印Unicode字符,我们可以使用WriteConsole打印来自std::wstring的Unicode字符。

最后在Windows上。您需要强大而完整的控制台支持Unicode字符。 我推荐使用Windows Terminal

QA

  • 在Microsoft Visual Studio 2019中进行了测试,使用VC++;std=c++17。(Windows项目)
  • 在repl.it上使用Clang编译器进行了测试;std=c++17。

Q.为什么不使用<codecvt>头文件中的函数和类?
A.已废弃的被删除或废弃的功能无法在VC++上构建,但在g ++上没有问题。 我更喜欢0警告和头疼。

Q. std :: wstring跨平台吗?
A.不是。 std :: wstring 使用wchar_t元素。 在Windows上,wchar_t大小为2个字节,每个字符存储在UTF-16单元中,如果字符大于U + FFFF,则该字符以两个UTF-16单元(2个wchar_t元素)表示,称为代理对。 在Linux上,wchar_t大小为4个字节,每个字符存储在一个wchar_t元素中,无需代理对。 请查看UNIX、Linux和Windows上的标准数据类型

Q. std :: string跨平台吗?
A.是的。 std :: string使用char元素。 char类型保证在大多数编译器中具有相同的字节大小。 char类型大小为1字节。 请查看UNIX、Linux和Windows上的标准数据类型


#include <iostream>
#include <set>
#include <string>
#include <locale>

// WINDOWS
#if (_WIN32)
#include <Windows.h>
#include <conio.h>
#define WINDOWS_PLATFORM 1
#define DLLCALL STDCALL
#define DLLIMPORT _declspec(dllimport)
#define DLLEXPORT _declspec(dllexport)
#define DLLPRIVATE
#define NOMINMAX

//EMSCRIPTEN
#elif defined(__EMSCRIPTEN__)
#include <emscripten/emscripten.h>
#include <emscripten/bind.h>
#include <unistd.h>
#include <termios.h>
#define EMSCRIPTEN_PLATFORM 1
#define DLLCALL
#define DLLIMPORT
#define DLLEXPORT __attribute__((visibility("default")))
#define DLLPRIVATE __attribute__((visibility("hidden")))

// LINUX - Ubuntu, Fedora, , Centos, Debian, RedHat
#elif (__LINUX__ || __gnu_linux__ || __linux__ || __linux || linux)
#define LINUX_PLATFORM 1
#include <unistd.h>
#include <termios.h>
#define DLLCALL CDECL
#define DLLIMPORT
#define DLLEXPORT __attribute__((visibility("default")))
#define DLLPRIVATE __attribute__((visibility("hidden")))
#define CoTaskMemAlloc(p) malloc(p)
#define CoTaskMemFree(p) free(p)

//ANDROID
#elif (__ANDROID__ || ANDROID)
#define ANDROID_PLATFORM 1
#define DLLCALL
#define DLLIMPORT
#define DLLEXPORT __attribute__((visibility("default")))
#define DLLPRIVATE __attribute__((visibility("hidden")))

//MACOS
#elif defined(__APPLE__)
#include <unistd.h>
#include <termios.h>
#define DLLCALL
#define DLLIMPORT
#define DLLEXPORT __attribute__((visibility("default")))
#define DLLPRIVATE __attribute__((visibility("hidden")))
#include "TargetConditionals.h"
#if TARGET_OS_IPHONE && TARGET_IPHONE_SIMULATOR
#define IOS_SIMULATOR_PLATFORM 1
#elif TARGET_OS_IPHONE
#define IOS_PLATFORM 1
#elif TARGET_OS_MAC
#define MACOS_PLATFORM 1
#else

#endif

#endif



typedef std::string String;
typedef std::wstring WString;

#define EMPTY_STRING u8""s
#define EMPTY_WSTRING L""s

using namespace std::literals::string_literals;

class Strings
{
public:
    static String WideStringToString(const WString& wstr)
    {
        if (wstr.empty())
        {
            return String();
        }
        size_t pos;
        size_t begin = 0;
        String ret;

#if WINDOWS_PLATFORM
        int size;
        pos = wstr.find(static_cast<wchar_t>(0), begin);
        while (pos != WString::npos && begin < wstr.length())
        {
            WString segment = WString(&wstr[begin], pos - begin);
            size = WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, &segment[0], segment.size(), NULL, 0, NULL, NULL);
            String converted = String(size, 0);
            WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, &segment[0], segment.size(), &converted[0], converted.size(), NULL, NULL);
            ret.append(converted);
            ret.append({ 0 });
            begin = pos + 1;
            pos = wstr.find(static_cast<wchar_t>(0), begin);
        }
        if (begin <= wstr.length())
        {
            WString segment = WString(&wstr[begin], wstr.length() - begin);
            size = WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, &segment[0], segment.size(), NULL, 0, NULL, NULL);
            String converted = String(size, 0);
            WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, &segment[0], segment.size(), &converted[0], converted.size(), NULL, NULL);
            ret.append(converted);
        }
#elif LINUX_PLATFORM || MACOS_PLATFORM || EMSCRIPTEN_PLATFORM
        size_t size;
        pos = wstr.find(static_cast<wchar_t>(0), begin);
        while (pos != WString::npos && begin < wstr.length())
        {
            WString segment = WString(&wstr[begin], pos - begin);
            size = wcstombs(nullptr, segment.c_str(), 0);
            String converted = String(size, 0);
            wcstombs(&converted[0], segment.c_str(), converted.size());
            ret.append(converted);
            ret.append({ 0 });
            begin = pos + 1;
            pos = wstr.find(static_cast<wchar_t>(0), begin);
        }
        if (begin <= wstr.length())
        {
            WString segment = WString(&wstr[begin], wstr.length() - begin);
            size = wcstombs(nullptr, segment.c_str(), 0);
            String converted = String(size, 0);
            wcstombs(&converted[0], segment.c_str(), converted.size());
            ret.append(converted);
        }
#else
        static_assert(false, "Unknown Platform");
#endif
        return ret;
    }

    static WString StringToWideString(const String& str)
    {
        if (str.empty())
        {
            return WString();
        }

        size_t pos;
        size_t begin = 0;
        WString ret;
#ifdef WINDOWS_PLATFORM
        int size = 0;
        pos = str.find(static_cast<char>(0), begin);
        while (pos != std::string::npos) {
            std::string segment = std::string(&str[begin], pos - begin);
            std::wstring converted = std::wstring(segment.size() + 1, 0);
            size = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, &segment[0], segment.size(), &converted[0], converted.length());
            converted.resize(size);
            ret.append(converted);
            ret.append({ 0 });
            begin = pos + 1;
            pos = str.find(static_cast<char>(0), begin);
        }
        if (begin < str.length()) {
            std::string segment = std::string(&str[begin], str.length() - begin);
            std::wstring converted = std::wstring(segment.size() + 1, 0);
            size = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, segment.c_str(), segment.size(), &converted[0], converted.length());
            converted.resize(size);
            ret.append(converted);
        }

#elif LINUX_PLATFORM || MACOS_PLATFORM || EMSCRIPTEN_PLATFORM
        size_t size;
        pos = str.find(static_cast<char>(0), begin);
        while (pos != String::npos)
        {
            String segment = String(&str[begin], pos - begin);
            WString converted = WString(segment.size(), 0);
            size = mbstowcs(&converted[0], &segment[0], converted.size());
            converted.resize(size);
            ret.append(converted);
            ret.append({ 0 });
            begin = pos + 1;
            pos = str.find(static_cast<char>(0), begin);
        }
        if (begin < str.length())
        {
            String segment = String(&str[begin], str.length() - begin);
            WString converted = WString(segment.size(), 0);
            size = mbstowcs(&converted[0], &segment[0], converted.size());
            converted.resize(size);
            ret.append(converted);
        }
#else
        static_assert(false, "Unknown Platform");
#endif
        return ret;
    }
};

enum class ConsoleTextStyle
{
    DEFAULT = 0,
    BOLD = 1,
    FAINT = 2,
    ITALIC = 3,
    UNDERLINE = 4,
    SLOW_BLINK = 5,
    RAPID_BLINK = 6,
    REVERSE = 7,
};

enum class ConsoleForeground
{
    DEFAULT = 39,
    BLACK = 30,
    DARK_RED = 31,
    DARK_GREEN = 32,
    DARK_YELLOW = 33,
    DARK_BLUE = 34,
    DARK_MAGENTA = 35,
    DARK_CYAN = 36,
    GRAY = 37,
    DARK_GRAY = 90,
    RED = 91,
    GREEN = 92,
    YELLOW = 93,
    BLUE = 94,
    MAGENTA = 95,
    CYAN = 96,
    WHITE = 97
};

enum class ConsoleBackground
{
    DEFAULT = 49,
    BLACK = 40,
    DARK_RED = 41,
    DARK_GREEN = 42,
    DARK_YELLOW = 43,
    DARK_BLUE = 44,
    DARK_MAGENTA = 45,
    DARK_CYAN = 46,
    GRAY = 47,
    DARK_GRAY = 100,
    RED = 101,
    GREEN = 102,
    YELLOW = 103,
    BLUE = 104,
    MAGENTA = 105,
    CYAN = 106,
    WHITE = 107
};

class Console
{
private:
    static void EnableVirtualTermimalProcessing()
    {
#if defined WINDOWS_PLATFORM
        HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
        DWORD dwMode = 0;
        GetConsoleMode(hOut, &dwMode);
        if (!(dwMode & ENABLE_VIRTUAL_TERMINAL_PROCESSING))
        {
            dwMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
            SetConsoleMode(hOut, dwMode);
        }
#endif
    }

    static void ResetTerminalFormat()
    {
        std::cout << u8"\033[0m";
    }

    static void SetVirtualTerminalFormat(ConsoleForeground foreground, ConsoleBackground background, std::set<ConsoleTextStyle> styles)
    {
        String format = u8"\033[";
        format.append(std::to_string(static_cast<int>(foreground)));
        format.append(u8";");
        format.append(std::to_string(static_cast<int>(background)));
        if (styles.size() > 0)
        {
            for (auto it = styles.begin(); it != styles.end(); ++it)
            {
                format.append(u8";");
                format.append(std::to_string(static_cast<int>(*it)));
            }
        }
        format.append(u8"m");
        std::cout << format;
    }
public:
    static void Clear()
    {

#ifdef WINDOWS_PLATFORM
        std::system(u8"cls");
#elif LINUX_PLATFORM || defined MACOS_PLATFORM
        std::system(u8"clear");
#elif EMSCRIPTEN_PLATFORM
        emscripten::val::global()["console"].call<void>(u8"clear");
#else
        static_assert(false, "Unknown Platform");
#endif
    }

    static void Write(const String& s, ConsoleForeground foreground = ConsoleForeground::DEFAULT, ConsoleBackground background = ConsoleBackground::DEFAULT, std::set<ConsoleTextStyle> styles = {})
    {
#ifndef EMSCRIPTEN_PLATFORM
        EnableVirtualTermimalProcessing();
        SetVirtualTerminalFormat(foreground, background, styles);
#endif
        String str = s;
#ifdef WINDOWS_PLATFORM
        WString unicode = Strings::StringToWideString(str);
        WriteConsole(GetStdHandle(STD_OUTPUT_HANDLE), unicode.c_str(), static_cast<DWORD>(unicode.length()), nullptr, nullptr);
#elif defined LINUX_PLATFORM || defined MACOS_PLATFORM || EMSCRIPTEN_PLATFORM
        std::cout << str;
#else
        static_assert(false, "Unknown Platform");
#endif

#ifndef EMSCRIPTEN_PLATFORM
        ResetTerminalFormat();
#endif
    }

    static void WriteLine(const String& s, ConsoleForeground foreground = ConsoleForeground::DEFAULT, ConsoleBackground background = ConsoleBackground::DEFAULT, std::set<ConsoleTextStyle> styles = {})
    {
        Write(s, foreground, background, styles);
        std::cout << std::endl;
    }

    static void Write(const WString& s, ConsoleForeground foreground = ConsoleForeground::DEFAULT, ConsoleBackground background = ConsoleBackground::DEFAULT, std::set<ConsoleTextStyle> styles = {})
    {
#ifndef EMSCRIPTEN_PLATFORM
        EnableVirtualTermimalProcessing();
        SetVirtualTerminalFormat(foreground, background, styles);
#endif
        WString str = s;

#ifdef WINDOWS_PLATFORM
        WriteConsole(GetStdHandle(STD_OUTPUT_HANDLE), str.c_str(), static_cast<DWORD>(str.length()), nullptr, nullptr);
#elif LINUX_PLATFORM || MACOS_PLATFORM || EMSCRIPTEN_PLATFORM
        std::cout << Strings::WideStringToString(str);
#else
        static_assert(false, "Unknown Platform");
#endif

#ifndef EMSCRIPTEN_PLATFORM
        ResetTerminalFormat();
#endif
    }

    static void WriteLine(const WString& s, ConsoleForeground foreground = ConsoleForeground::DEFAULT, ConsoleBackground background = ConsoleBackground::DEFAULT, std::set<ConsoleTextStyle> styles = {})
    {
        Write(s, foreground, background, styles);
        std::cout << std::endl;
    }

    static void WriteLine()
    {
        std::cout << std::endl;
    }

    static void Pause()
    {
        char c;
        do
        {
            c = getchar();
            std::cout << "Press Key " << std::endl;
        } while (c != 64);
        std::cout << "KeyPressed" << std::endl;
    }

    static int PauseAny(bool printWhenPressed = false, ConsoleForeground foreground = ConsoleForeground::DEFAULT, ConsoleBackground background = ConsoleBackground::DEFAULT, std::set<ConsoleTextStyle> styles = {})
    {
        int ch;
#ifdef WINDOWS_PLATFORM
        ch = _getch();
#elif LINUX_PLATFORM || MACOS_PLATFORM || EMSCRIPTEN_PLATFORM
        struct termios oldt, newt;
        tcgetattr(STDIN_FILENO, &oldt);
        newt = oldt;
        newt.c_lflag &= ~(ICANON | ECHO);
        tcsetattr(STDIN_FILENO, TCSANOW, &newt);
        ch = getchar();
        tcsetattr(STDIN_FILENO, TCSANOW, &oldt);
#else
        static_assert(false, "Unknown Platform");
#endif
        if (printWhenPressed)
        {
            Console::Write(String(1, ch), foreground, background, styles);
        }
        return ch;
    }
};



int main()
{
    std::locale::global(std::locale(u8"en_US.UTF8"));
    auto str = u8"\0Hello\0123456789也不是可运行的程序123456789日本"s;//
    WString wstr = L"\0Hello\0123456789也不是可运行的程序123456789日本"s;
    WString wstrResult = Strings::StringToWideString(str);
    String strResult = Strings::WideStringToString(wstr);
    bool equals1 = wstr == wstrResult;
    bool equals2 = str == strResult;

    Console::WriteLine(u8"█ Converted Strings printed with Console::WriteLine"s, ConsoleForeground::GREEN);
    Console::WriteLine(wstrResult, ConsoleForeground::BLUE);//Printed OK on Windows/Linux.
    Console::WriteLine(strResult, ConsoleForeground::BLUE);//Printed OK on Windows/Linux.
    
    Console::WriteLine(u8"█ Converted Strings printed with std::cout/std::wcout"s, ConsoleForeground::GREEN);
    std::cout << strResult << std::endl;//Printed OK on Linux. BAD on Windows.
    std::wcout << wstrResult << std::endl; //Printed BAD on Windows/Linux.
    Console::WriteLine();
    Console::WriteLine(u8"Press any key to exit"s, ConsoleForeground::DARK_GRAY);
    Console::PauseAny();

}

你无法在https://repl.it/@JomaCorpFX/StringToWideStringToString#main.cpp上测试此代码。


**屏幕截图**

使用 Windows Terminal WindowsTerminal

使用 cmd/powershell enter image description here

Repl.it 捕获
enter image description here


13

如果你确定你的字符串可以转换,那么不必包含区域设置和所有花哨的东西,只需这样做:

#include <iostream>
#include <string>

using namespace std;

int main()
{
  wstring w(L"bla");
  string result;
  for(char x : w)
    result += x;

  cout << result << '\n';
}

实时示例 这里


2
+1 是因为它是一个简单的解决方案,适用于某些场景(我必须补充一下,这里的“适用”定义比较宽泛)。 - raven
2
几乎与namar0x0309的解决方案相同,但在我看来更加优雅。但这只是我的个人意见。 - onitake
我对你的代码进行了一些改进,只需最少的修改就可以使其正常工作;-) - rubenvb
12
如果你有一个wstring,那么你很可能正在处理多字节字符。如果你知道该字符串可以轻松转换,那么你不应该首先处理wstring。更有可能的是,你正在处理另一个库,该库希望你正确地处理wstring。截断wchars只会引发难以跟踪的错误。此外,如果你打算这样做,应该使用“string result(w.begin(), w.end())”,以避免循环触发多次重新分配内存。 - Kian
@Kian,简单明了,非常适合某些有限的使用情况。 - Devolus

8
我相信官方的方法仍然是通过codecvt facets(你需要某种区域设置感知翻译),例如:
resultCode = use_facet<codecvt<char, wchar_t, ConversionState> >(locale).
  in(stateVar, scratchbuffer, scratchbufferEnd, from, to, toLimit, curPtr);

或者类似的东西,我没有可用的工作代码。但是我不确定现在有多少人使用那种机制,有多少人只是请求指向内存的指针,并让 ICU 或其他库处理细节。


8

代码存在两个问题:

  1. The conversion in const std::string s( ws.begin(), ws.end() ); is not required to correctly map the wide characters to their narrow counterpart. Most likely, each wide character will just be typecast to char.
    The resolution to this problem is already given in the answer by kem and involves the narrow function of the locale's ctype facet.

  2. You are writing output to both std::cout and std::wcout in the same program. Both cout and wcout are associated with the same stream (stdout) and the results of using the same stream both as a byte-oriented stream (as cout does) and a wide-oriented stream (as wcout does) are not defined.
    The best option is to avoid mixing narrow and wide output to the same (underlying) stream. For stdout/cout/wcout, you can try switching the orientation of stdout when switching between wide and narrow output (or vice versa):

    #include <iostream>
    #include <stdio.h>
    #include <wchar.h>
    
    int main() {
        std::cout << "narrow" << std::endl;
        fwide(stdout, 1); // switch to wide
        std::wcout << L"wide" << std::endl;
        fwide(stdout, -1); // switch to narrow
        std::cout << "narrow" << std::endl;
        fwide(stdout, 1); // switch to wide
        std::wcout << L"wide" << std::endl;
    }
    

是的,这解决了使用cout和wcout的问题。 - BЈовић

8
除了转换数据类型,你还应该注意字符串的实际格式。
在编译 多字节字符集 时,Visual Studio 和 Win API 假定使用 UTF8 编码(实际上是 Windows 编码,即 Windows-28591)。
在编译 Unicode 字符集 时,Visual Studio 和 Win API 假定使用 UTF16 编码。
所以,你必须将字符串从 UTF16 转换为 UTF8 格式,并且不仅仅是转换为 std::string。当使用一些非拉丁语系的多字符格式时,这将变得必要。
思路是决定让 std::wstring 总是代表 UTF16,而 std::string 总是代表 UTF8
这不是由编译器强制执行的,而更像是一个好的方针。请注意,我在定义 UTF16(L)和 UTF8(u8)时使用的字符串前缀。
要在两种类型之间进行转换,应该使用:std::codecvt_utf8_utf16< wchar_t>
#include <string>

#include <codecvt>

int main()
{

    std::string original8 = u8"הלו";

    std::wstring original16 = L"הלו";

    //C++11 format converter
    std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> convert;

    //convert to UTF8 and std::string
    std::string utf8NativeString = convert.to_bytes(original16);

    std::wstring utf16NativeString = convert.from_bytes(original8);

    assert(utf8NativeString == original8);
    assert(utf16NativeString == original16);

    return 0;
}

7

如果你正在处理文件路径(就像我经常需要进行wstring-to-string转换时),你可以使用filesystem::path(自C++17版本以来可用):

#include <filesystem>

const std::wstring wPath = GetPath(); // some function that returns wstring
const std::string path = std::filesystem::path(wPath).string();

这正是我正在寻找的。谢谢。 - Antidisestablishmentarianism

7
在撰写本回答时,“将string转换为wstring”的谷歌搜索排名第一的结果会跳转至此页面。我的回答展示了如何将string转换为wstring,尽管这并不是实际问题,我应该删除此回答,但这被认为是不好的行为。您可能想跳转到此StackOverflow答案,该答案现在比本页面排名更高。
以下是一种将string、wstring和混合字符串常量组合成wstring的方法。使用wstringstream类。
#include <sstream>

std::string narrow = "narrow";
std::wstring wide = "wide";

std::wstringstream cls;
cls << " abc " << narrow.c_str() << L" def " << wide.c_str();
std::wstring total= cls.str();

14
这不是从宽字符串到字符串的转换。 - poitroae
1
@Michael,你能解释一下吗?这里有什么不正确的地方吗?如果没有更多细节,你的评论并不是很有帮助。 - Nate
1
这是一个字符串到宽字符串的转换,即与问题相反。 - Jeff McClintock

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