【问题标题】:printing out a unicode character with printf用 printf 打印出一个 unicode 字符
【发布时间】:2017-01-22 23:08:28
【问题描述】:

我试图通过将对应的十进制值传递给 printf 来打印出 Ș。输出什么都没有。为什么我的代码不起作用?

#include <stdio.h>
int main()
{
    printf("%lc",536);
    return 0;
}

【问题讨论】:

  • 我不知道这是否是一个骗局,但you may need to set the locale on Linux
  • 另外,在 printf 之后尝试 fflush(NULL)。可能会或可能不会改变任何东西,但至少你会确定它正在被打印。
  • 检查返回值。 int cnt = printf("%lc", ( wint_t ) 536); printf("%d\n", cnt); 是 1 还是负值(错误)?错误表明printf() 无法处理该字符。
  • 这取决于您的操作系统、编译器和环境。如果您想从 C 库中获得任何类型的 Unicode 支持,那么在程序的开头调用 setlocale("", LC_ALL) 是一种强制要求。这可能还不够。
  • 我猜 C 库对 Unicode 的支持有点牵强。在某些情况下,您只需发出 UTF-8 而不关心任何内容,让终端对其进行排序,但通常最好的方法是了解操作系统以及您的程序和屏幕之间的任何内容并相应地处理它通过与操作系统而不是 C 对话。

标签: c unicode printf


【解决方案1】:

在带有 GCC 6.3.0 的 macOS Sierra 10.12.2 上,如果我运行这个程序(从 mb37.c 编译成 mb37):

#include <locale.h>
#include <stdio.h>
#include <wchar.h>      /* wint_t */

int main(void)
{
    setlocale(LC_ALL, "");
    printf("%lc\n", (wint_t)536);
    return 0;
}

输出是:

$ ./mb37
Ș
$

我相信,这是期望的输出。如果删除setlocale() 行,则根本不会产生任何输出——甚至不会产生换行符。使用的语言环境是en_US.UTF-8;我的终端也处理 UTF-8。通过捕获并打印来自setlocale()(一个常规字符串)的返回值来找到区域设置名称。

wint_t 演员表是半可选的;碰巧的是,没有强制转换或&lt;wchar.h&gt; 标头的 64 位编译也会产生相同的输出,但有一点巧合,wint_tint 相同。这需要一些跟踪; wint_t 被定义为__darwin_wint_t__darwin_ct_rune_t 被定义为int。为了便于携带,演员表是必要的。在某些系统上,可能没有必要(macOS Sierra 就是这样一种系统)。

printf() 中的换行符不是 100% 必需的,但如果省略,下一个提示符会紧跟在 U+0218 拉丁大写字母 S 后面,下面带有逗号。最好确保输出以换行符结尾。

【讨论】:

  • 我刚发现 "Unicode 码位"​​i> 0x0218 (hexadecimal)"XML entity" 是同一个数字 536 (十进制)。所以如果你喜欢直接打印 Unicode 代码点,你可以像这样使用十六进制值:printf("%lc\n", (wint_t)0x0218); 这更有意义,因为毕竟我们在谈论 UTF-8 和 Unicode……没有人谈论 XML,那为什么要有人使用 XML 十进制数字而不是十六进制 Unicode 吗???
  • 还有一件事。在 Linux 上,我使用了你的程序,但创建了一个 for 循环,打印从 320x10FFFF (就像你做的那样) 的所有小数。我将程序编译了 2x,其中一次我使用了转换 (wchar_t),而一次我没有。然后我执行了两个可执行文件并将输出重定向到两个单独的文件。我在vimdiff 中打开了两个文件,它们说它们是相同的!那么选角有什么意义呢?
【解决方案2】:

应用于c 字段描述符的l 长度说明符指示相应参数的类型为wint_t(在wchar.h 中声明)。在您的代码中,参数的类型为int,可能相同也可能不同。如果它确实不一样,那么行为是未定义的。您可以通过转换获得wint_t ...

    printf("%lc", (wint_t) 536);

;这是表达wint_t 常量的最安全、最便携的方式。

此外,这里还有一个潜在的字符集问题。这是程序运行的环境问题,而不是程序本身。可以想象,您的程序确实以某种编码输出了有问题的字符,但是您正在运行的终端不知道如何处理它,或者可能只是没有字形。您应该能够通过将输出重定向到文件并随后检查文件的内容(可能是二进制文件)来进行测试。

【讨论】:

  • 我完成了您建议的所有更改并将其输出到文件中,但文件为空(0 字节)。当我将数字更改为 126 时,它会打印一个 ~,所以没有其他任何奇怪的事情发生了..
  • @Mike,也许您的 C 实现是旧的或错误的。你用的是哪个?
【解决方案3】:

C 中没有要求打印末尾没有换行符的行。试试“%lc\n”。

【讨论】:

  • 我认为你错了,@gnasher729。如果stdout 是行缓冲(默认)或完全缓冲,则输出可能不会立即显示,但它会在文件被刷新时出现,当文件关闭时会发生这种情况,当文件关闭时会发生这种情况程序调用exit() 函数或从main() 返回,就像OP 的程序一样。
  • @JohnBollinger 你能证明stdout 在程序退出时是关闭的吗?我在标准的第 7.19 节中没有找到任何内容,我记得未完成的行不必写出来。
  • @RolandIllig: 7.22.4.4 exit 函数:¶4 接下来,所有带有未写入缓冲数据的打开流被刷新,所有打开流被关闭,所有文件由 tmpfile 创建函数被删除。和§5.1.2.2.3程序终止:¶1 如果main函数的返回类型是与int兼容的类型,则从初始调用返回main 函数相当于调用exit 函数,将main 函数返回的值作为其参数;…。 (即 C11 — 又名 ISO/IEC 9899:2011。)
  • @RolandIllig,也有7.21.3/5:“如果主函数返回到它的原始调用者,或者如果调用了退出函数,所有打开的文件都会在程序之前关闭(因此所有输出流都被刷新)终止。”所以是的,我可以证明标准需要它(尽管程序在这方面不符合标准)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-10-05
  • 1970-01-01
  • 2018-06-22
  • 2015-04-10
相关资源
最近更新 更多