iostreams - 将 `wchar_t` 或 `charXX_t` 值打印为字符

Posted

技术标签:

【中文标题】iostreams - 将 `wchar_t` 或 `charXX_t` 值打印为字符【英文标题】:iostreams - Print `wchar_t` or `charXX_t` value as a character 【发布时间】:2017-04-27 17:18:30 【问题描述】:

如果您将 wchar_tchar16_tchar32_t 值提供给窄 ostream,它将打印代码点的数值。

#include <iostream>
using std::cout;
int main()

    cout << 'x' << L'x' << u'x' << U'x' << '\n';

打印x120120120。这是因为basic_ostream 与其charT 的特定组合有一个operator&lt;&lt;,但其他字符类型没有类似的运算符,因此它们被静默转换为int 并以这种方式打印。类似地,非窄字符串文字(L"x"u"x"U"X")将被静默转换为void*并打印为指针值,非窄字符串objects(@ 987654338@, u16string, u32string) 甚至无法编译。

所以,问题是:在狭窄的 ostream 上打印wchar_tchar16_tchar32_t 值的最不可怕的方法是,作为字符,而不是作为代码点的数值?它应该正确地将在 ostream 的编码中可表示的 所有 个代码点转换为该编码,并在代码点不可表示时报告错误。 (例如,给定u'…' 和一个UTF-8 ostream,应该将三字节序列0xE2 0x80 0xA6 写入流;但给定u'â' 和一个KOI8-R ostream,应该报告错误。)

同样,如何在窄流上打印非窄 C 字符串或字符串对象,转换为输出编码?

如果这不能在 ISO C++11 中完成,我将采用特定于平台的答案。

(灵感来自this question。)

【问题讨论】:

简而言之,您必须 1) 使用宽 ostream,或 2) 自己将宽字符数据转换为窄编码(这可能是有损转换)。 ostream 无法为您进行转换。查看std::wstring_convert,或使用ICONV 或ICU 之类的库。 【参考方案1】:

正如您所指出的,狭窄的 ostream 没有 operator&lt;&lt;(std::ostream&amp;, const wchar_t)。但是,如果您想使用该语法,您可以教 ostream 如何处理 wchars,以便选择该例程作为比需要先转换为整数的例程更好的重载。

如果您喜欢冒险:

namespace std 
  ostream& operator<< (ostream& os, wchar_t wc) 
    if(unsigned(wc) < 256) // or another upper bound
      return os << (unsigned char)wc;
    else
      throw your_favourite_exception; // or handle the error in some other way
  

否则,请创建一个简单的 struct,它透明地包含 wchar_t 并具有自定义 friend operator&lt;&lt;,并在输出之前将宽字符转换为该字符。

编辑:要在区域设置之间进行即时转换,您可以使用&lt;cwchar&gt; 中的函数,例如:

ostream& operator<< (ostream& os, wchar_t wc) 
    std::mbstate_t state;
    std::string mb(MB_CUR_MAX, '\0');
    size_t ret = std::wcrtomb(&mb[0], wc, &state);
    if(ret == static_cast<std::size_t>(-1))
        deal_with_the_error();
    return os << mb;

不要忘记将您的语言环境设置为系统默认设置:

std::locale::global(std::locale(""));
std::cout << L'ŭ';

【讨论】:

这不会将值转换为窄输出编码。这是必不可少的,也是我不知道该怎么做的部分。 @zwol 如果宽字符在 ASCII 范围内则接受它并拒绝它,您还想如何转换它?然后,您需要具体,例如,删除重音或其他内容。 你的例子使用了一个'x'来传递这个(对于L'x',你需要对其他类型做同样的事情)所以我认为这就是你所追求的。跨度> 当窄输出编码为 UTF-8 时,应该将 L"…" 转换为三字节序列 0xE2 0x80 0xA6。 我认为很明显,我想要能够处理窄输出编码支持的所有个字符,而不仅仅是ASCII。

以上是关于iostreams - 将 `wchar_t` 或 `charXX_t` 值打印为字符的主要内容,如果未能解决你的问题,请参考以下文章

QChar 到 wchar_t

在 Linux 中是不是有任何将 wstring 或 wchar_t* 转换为 UTF-8 的内置函数?

wchar_t 和编码

C++中的WCHAR_T怎么转化成CHAR?

wchar_t*转换string

CString 转换为 wchar_t *