为啥 wprintf 在 Linux 上将 Unicode 中的俄语文本音译成拉丁语?

Posted

技术标签:

【中文标题】为啥 wprintf 在 Linux 上将 Unicode 中的俄语文本音译成拉丁语?【英文标题】:Why does wprintf transliterate Russian text in Unicode into Latin on Linux?为什么 wprintf 在 Linux 上将 Unicode 中的俄语文本音译成拉丁语? 【发布时间】:2021-04-06 05:11:18 【问题描述】:

为什么下面的程序

#include <stdio.h>
#include <wchar.h>

int main() 
  wprintf(L"Привет, мир!");

print "女贞,先生!"在 Linux 上?具体来说,为什么它将 Unicode 中的俄语文本音译为拉丁语,而不是将其转码为 UTF-8 或使用替换字符?

在 Godbolt 上演示此行为:https://godbolt.org/z/36zEcG

非宽版本 printf("Привет, мир!") 按预期打印此文本(“Привет, мир!”)。

【问题讨论】:

出于好奇,为什么还要在 Linux 上使用wchar 没有理由使用wchar_t,因为它是不可移植的。我在回答另一个 SO 问题时遇到了这种“有趣”的行为:***.com/a/65480111/471164, 在我的系统中,它只打印??????, ???!。你能检查一下/usr/share/i18n/locales/C,看看里面有没有以translit开头的规则吗? @Heinzi,如果有兴趣,您可以在 Godbolt 上查看语言环境 - 问题中有一个链接。 【参考方案1】:

因为宽字符的转换是根据当前设置的语言环境完成的。默认情况下,C 程序总是以“C”语言环境开始,它只支持 ASCII 字符。

您必须先切换到任何俄语或 UTF-8 语言环境:

setlocale(LC_ALL, "ru_RU.utf8"); // Russian Unicode
setlocale(LC_ALL, "en_US.utf8"); // English US Unicode

或当前系统区域设置(这可能是您需要的):

setlocale(LC_ALL, "");

完整的程序将是:

#include <stdio.h>
#include <wchar.h>
#include <locale.h>

int main() 
  setlocale(LC_ALL, "ru_RU.utf8");
  wprintf(L"Привет, мир!\n");

至于您的代码在其他机器上按原样运行 - 这是由于 libc 在那里的运行方式。一些实现(如 musl)不支持非 Unicode 语言环境,因此可以无条件地将宽字符转换为 UTF-8 序列。

【讨论】:

当我在有或没有setlocale(LC_ALL, "ru_RU.utf8")setlocale(LC_ALL, "")的godbolt上运行它时,它会逐字打印Privet, mir! 但是为什么要音译呢?它是否记录在某处? @Jabberwocky 您的计算机上是否安装了“ru_RU.utf8”语言环境?如果没有,那么设置将失败。使用""(默认语言环境),它可能是 UTF-8。任何 unicode 语言环境都可以。 @vitaut 我不确定,但我认为在没有语言环境的情况下输出这些字符是非法的,libc 可能可以做任何它想做的事情。音译是产生有效且仍然可读的输出的好方法。 @Jabberwocky 那么您使用的是什么语言环境?如果您在美国,请尝试“en_US.utf8”。【参考方案2】:

为什么它将 Unicode 中的俄语文本音译为拉丁语,而不是将其转码为 UTF-8 或使用替换字符?

因为您的程序的起始语言环境是默认语言环境,即C 语言环境。所以它将宽字符串转换为C 语言环境。 C 语言环境不处理 UTF-8 或任何 unicode,因此您的标准库最好将宽字符转换为 C 语言环境中使用的一些基本字符集。

您可以将语言环境更改为任何 UTF-8 语言环境,程序应输出 UTF-8 字符串。

注意:(在我知道的实现中)FILE 流的编码已确定并保存在时间选择流方向(宽与正常)。请记住在使用stdout (即this 与this)进行任何操作之前设置语言环境

【讨论】:

以上是关于为啥 wprintf 在 Linux 上将 Unicode 中的俄语文本音译成拉丁语?的主要内容,如果未能解决你的问题,请参考以下文章

Linux 上的 wprintf UTF16(应该是 UTF8)?

wprintf: %p 带 NULL 指针

为啥 Microsoft.Build.Evaluation 在 64 位 PC 上将 $(ProgramFiles) 评估为“c:\program files”?

uni-app引用图片为啥不显示?

拼命尝试使用 wprintf 在 64 位 NASM x86 程序集中打印 unicode

无法在命令行上将 Netbeans 生成的 .java 文件编译成 .class