【问题标题】:How to print Unicode codepoints as characters in C?如何将Unicode代码点打印为C中的字符?
【发布时间】:2016-11-24 06:54:06
【问题描述】:

我有一个 uint32_t 元素数组,每个元素都存储一个非拉丁 Unicode 字符的代码点。如何在控制台上打印它们或将它们作为 UTF-8 编码字符存储在文件中?我知道它们可能无法在控制台上正确呈现,但如果我在兼容的编辑器中打开它们,它们应该可以正常显示。

我曾尝试使用wprintf(L"%lc", UINT32_T_VARIABLE)fwprintf(FILE_STREAM, L"%lc", UINT32_T_VARIABLE),但无济于事。

【问题讨论】:

  • 您确定stdout 不是面向字节的吗?
  • @EOF:实际上stdout 必须是面向字节的,并且必须为此工作选择正确的语言环境。
  • @chqrlie:如果你想像 OP 那样使用wprintf(),那就不行了。
  • @EOF:OP 尝试使用 wprintf() 来达到他的目的,但他不一定想使用宽字符串版本,他只是想将 Unicode 代码点转换为 UTF-8。

标签: c file-io unicode utf-8


【解决方案1】:

最好在可用时使用现有代码。

将自己的 Unicode 代码点滚动到 UTF8 很简单,但很容易搞砸。答案需要 2 次编辑才能修复。 @Jonathan Leffler@chqrlie,因此建议对任何自编码解决方案进行严格测试。以下是经过简单测试的代码,用于将代码点转换为数组。
请注意,结果不是 字符串

// Populate utf8 with 0-4 bytes
// Return length used in utf8[]
// 0 implies bad codepoint
unsigned Unicode_CodepointToUTF8(uint8_t *utf8, uint32_t codepoint) {
  if (codepoint <= 0x7F) {
    utf8[0] = codepoint;
    return 1;
  }
  if (codepoint <= 0x7FF) {
    utf8[0] = 0xC0 | (codepoint >> 6);
    utf8[1] = 0x80 | (codepoint & 0x3F);
    return 2;
  }
  if (codepoint <= 0xFFFF) {
    // detect surrogates
    if (codepoint >= 0xD800 && codepoint <= 0xDFFF) return 0;
    utf8[0] = 0xE0 | (codepoint >> 12);
    utf8[1] = 0x80 | ((codepoint >> 6) & 0x3F);
    utf8[2] = 0x80 | (codepoint & 0x3F);
    return 3;
  }
  if (codepoint <= 0x10FFFF) {
    utf8[0] = 0xF0 | (codepoint >> 18);
    utf8[1] = 0x80 | ((codepoint >> 12) & 0x3F);
    utf8[2] = 0x80 | ((codepoint >> 6) & 0x3F);
    utf8[3] = 0x80 | (codepoint & 0x3F);
    return 4;
  }
  return 0;
}

// Sample usage
uint32_t cp = foo();
uint8_t utf8[4];
unsigned len = Unicode_CodepointToUTF8(utf8, cp);
if (len == 0) Handle_BadCodePoint();
size_t y = fwrite(utf8, 1, len, stream_opened_in_binary_mode);

【讨论】:

  • 高低代理的正确范围是 U+D800 .. U+DFFF (分为高代理 U+D800 .. U+DBFF 和低代理 U+DC00 .. U+DFFF ) 而不是 U+D000 .. U+DFFF 作为答案中代码的一个版本。 U+AC00 .. U+D7AF 的图表涵盖了韩文音节。正如你所说,很容易搞砸。
  • 3 和 4 字节情况下的中间序列字节的代码不正确:您必须在移位 codepoint 后屏蔽掉 0x40 位... 简单,但容易确实搞砸了
  • @chqrlie 是的,需要清除位(在 2 字节的情况下也是如此)- 修改了代码。
【解决方案2】:

您必须首先选择正确的语言环境:

#include <locale.h>

setlocale(LC_ALL, "C.UTF-8");

setlocale(LC_ALL, "en_US.UTF-8");

然后将printffprintf%lc 格式一起使用:

printf("%lc", UINT32_T_VARIABLE);

这仅适用于小到足以容纳wchar_t 的 Unicode 代码点。要获得更完整和便携的解决方案,您可能需要自己实现 Unicode 到 UTF-8 的转换,这不是很困难。

【讨论】:

  • 设置语言环境没有帮助。 fprintf()wprintf() 仅打印/存储空格,fwprintf() 在文件中存储不正确的字符。
  • 你的平台和编译器是什么?
  • Win10、gcc 5.3、Cygwin
  • 对语言环境和多字节编码的支持在 C 库中。在 Windows 上,您可以使用从 Glibc 派生的 MSVC 运行时库或 Cygwin 特定的 C 库,我相信它有更好的支持。为避免此问题,请使用您自己的 UTF-8 转换器。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-08-31
  • 2015-03-05
  • 2011-08-04
  • 2021-10-27
  • 2017-08-03
  • 2021-12-26
相关资源
最近更新 更多