【问题标题】:c++ can't get "wcout" to print unicode, and leave "cout" workingc++ 无法让“wcout”打印 unicode,并让“cout”正常工作
【发布时间】:2014-05-21 22:31:49
【问题描述】:

无法让“wcout”在多个代码页中打印 unicode 字符串,同时让“cout”继续工作

请帮我让这 3 行代码一起工作。

std::wcout<<"abc "<<L'\u240d'<<" defg "<<L'א'<<" hijk"<<std::endl;
std::cout<<"hello world from cout! \n";
std::wcout<<"hello world from wcout! \n";

输出:

abc hello world from cout!

我试过了:

#include <io.h> 
#include <fcntl.h>
_setmode(_fileno(stdout), _O_U8TEXT);

问题: “wcout”失败

试过了:

std::locale mylocale("");
std::wcout.imbue(mylocale);

和:

SetConsoleOutputCP(1251);

setlocale(LC_ALL, "");

SetConsoleCP(CP_UTF8)

没有任何效果

【问题讨论】:

  • @Deduplicator:我不明白这仅限于 Windows。这是 C++ 和 C 标准的事实。
  • @LightnessRacesinOrbit:你说得对,对不起。拍摄太快了,因为某些窗口只使用了一些功能。此外,他们对 unicode 有额外的麻烦。
  • 那么,你得到答案了吗?
  • @LightnessRacesinOrbit 不是一个完整的解决方案,但至少不会在打印 Unicode 后冻结 cout\wcout 光标:std::locale mylocale(""); std::wcout.imbue(mylocale);

标签: c++ windows unicode utf-8 cout


【解决方案1】:

C++ 说:

[C++11: 27.4.1/3]: 在相应的宽字符流和窄字符流上混合操作遵循与在FILEs 上混合此类操作相同的语义,如 ISO C 标准的修正 1 中所指定。

the referenced document 说:

流的定义已更改为包含文本流和二进制流的方向概念。在流与文件关联之后,但在对流执行任何操作之前,流是没有方向的。如果将宽字符输入或输出函数应用于没有方向的流,则流将变为宽方向。同样,如果将字节输入或输出操作应用于具有方向的流,则流将变为面向字节的。此后,只有fwide()freopen() 函数可以改变流的方向。

字节输入/输出函数不应应用于面向宽的流,宽字符输入/输出函数不应应用于面向字节的流。

根据我的解释,这意味着,简而言之,不要混用 std::coutstd::wcout

【讨论】:

    【解决方案2】:

    wcoutwcin 可以工作之前,Microsoft 需要对_setmode() 进行一些非标准设置。这个例子在样板文件上相当繁重,所以不是很清楚,但它可以在 clang++、g++ 和 MSVC++ 上运行:

    #include <iostream>
    #include <locale>
    #include <locale.h>
    #include <stdlib.h>
    
    #ifndef MS_STDLIB_BUGS // Allow overriding the autodetection.
    /* The Microsoft C and C++ runtime libraries that ship with Visual Studio, as
     * of 2017, have a bug that neither stdio, iostreams or wide iostreams can
     * handle Unicode input or output.  Windows needs some non-standard magic to
     * work around that.  This includes programs compiled with MinGW and Clang
     * for the win32 and win64 targets.
     */
    #  if ( _MSC_VER || __MINGW32__ || __MSVCRT__ )
        /* This code is being compiled either on MS Visual C++, or MinGW, or
         * clang++ in compatibility mode for either, or is being linked to the
         * msvcrt.dll runtime.
         */
    #    define MS_STDLIB_BUGS 1
    #  else
    #    define MS_STDLIB_BUGS 0
    #  endif
    #endif
    
    #if MS_STDLIB_BUGS
    #  include <io.h>
    #  include <fcntl.h>
    #endif
    
    #if !HAS_APP17_FILESYSTEM && !HAS_TS_FILESYSTEM && __has_include(<filesystem>)
    #  include <filesystem> /* MSVC has this header, but not the standard API. */
    #  if __cpp_lib_filesystem >= 201703
    #    define HAS_CPP17_FILESYSTEM 1
    #  endif
    #endif
    
    #if !HAS_CPP17_FILESYSTEM && __has_include(<experimental/filesystem>)
    #  include <experimental/filesystem>
    /* Microsoft screws this one up, too, by not defining the feature-test
     * macro specified by the standard.
     */
    #  if __cpp_lib_experimental_filesystem >= 201406 || MS_STDLIB_BUGS
    #    define HAS_TS_FILESYSTEM 1
    /* With g++6, this requires -lstdc++fs, AFTER this source file on the
     * command line.
     */
    #  endif
    #endif
    
    #if HAS_CPP17_FILESYSTEM
      using std::filesystem::absolute;
      using std::filesystem::current_path;
      using std::filesystem::directory_entry;
      using std::filesystem::directory_iterator;
      using std::filesystem::is_directory;
      using std::filesystem::exists;
      using std::filesystem::path;
    #elif HAS_TS_FILESYSTEM
      using std::experimental::filesystem::absolute;
      using std::experimental::filesystem::current_path;
      using std::experimental::filesystem::directory_entry;
      using std::experimental::filesystem::directory_iterator;
      using std::experimental::filesystem::is_directory;
      using std::experimental::filesystem::exists;
      using std::experimental::filesystem::path;
    #else
    #  error "This library has neither <filesystem> nor <experimental/filesystem>."
    #endif
    
    void init_locale(void)
    // Does magic so that wcout can work.
    {
    #if MS_STDLIB_BUGS
      // Windows needs a little non-standard magic.
      constexpr char cp_utf16le[] = ".1200"; // UTF-16 little-endian locale.
      setlocale( LC_ALL, cp_utf16le );
      _setmode( _fileno(stdout), _O_WTEXT );
      /* Repeat for _fileno(stdin), if needed. */
    #else
      // The correct locale name may vary by OS, e.g., "en_US.utf8".
      constexpr char locale_name[] = "";
      setlocale( LC_ALL, locale_name );
      std::locale::global(std::locale(locale_name));
      std::wcin.imbue(std::locale())
      std::wcout.imbue(std::locale());
    #endif
    }
    
    using std::endl;
    
    int main( const int argc, const char * const argv[] )
    {
      init_locale();
    
      const path cwd = (argc > 1) ? absolute(path( argv[1], std::locale() ))
                                  : absolute(current_path());
    
      if (exists(cwd)) {
        std::wcout << cwd.wstring() << endl;
      } else {
        std::wcerr << "Path does not exist.\n";
        return EXIT_FAILURE;
      }
    
      if (is_directory(cwd)) {
        for ( const directory_entry &f : directory_iterator(cwd) )
          std::wcout << f.path().filename().wstring() << endl;
      }
    
      return EXIT_SUCCESS;
    }
    

    这可能比实际需要的复杂得多:std::filesystem 自 2018 年起不受支持,但 &lt;experimental/filesystem&gt; 永远不会被删除。

    这是一个简化版本,其中仅包含使 wcout 工作的样板:

    #include <iostream>
    #include <locale>
    #include <locale.h>
    
    #ifndef MS_STDLIB_BUGS
    #  if ( _MSC_VER || __MINGW32__ || __MSVCRT__ )
    #    define MS_STDLIB_BUGS 1
    #  else
    #    define MS_STDLIB_BUGS 0
    #  endif
    #endif
    
    #if MS_STDLIB_BUGS
    #  include <io.h>
    #  include <fcntl.h>
    #endif
    
    void init_locale(void)
    {
    #if MS_STDLIB_BUGS
      constexpr char cp_utf16le[] = ".1200";
      setlocale( LC_ALL, cp_utf16le );
      _setmode( _fileno(stdout), _O_WTEXT );
    #else
      // The correct locale name may vary by OS, e.g., "en_US.utf8".
      constexpr char locale_name[] = "";
      setlocale( LC_ALL, locale_name );
      std::locale::global(std::locale(locale_name));
      std::wcin.imbue(std::locale())
      std::wcout.imbue(std::locale());
    #endif
    }
    

    【讨论】:

      【解决方案3】:

      这是因为 Unicode 在代码页中无法表示,导致 wcout 失败。

      std::wcout<<"abc "<<L'\u240d'<<" defg "<<L'א'<<" hijk"<<std::endl;
      if(std::wcout.fail()){
          std::cout<<"\nConversion didn't succeed\n";
          std::wcout << "This statement has no effect on the console";
          std::wcout.clear();
          std::wcout<<"hello world from wcout! \n";
      }
      std::cout<<"hello world from cout! \n";
      std::wcout<<"hello world from wcout again! \n";
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2012-09-05
        • 2017-11-06
        • 2011-09-06
        • 1970-01-01
        • 2023-03-10
        • 2020-10-16
        • 2014-01-15
        相关资源
        最近更新 更多