如何确定 C/C++ 程序中终端使用的字符编码?

如何确定 C/C++ 程序中终端使用的字符编码?

我注意到 SyncTERM 使用与默认 MacOS 终端仿真器不同的字符编码,并且它们彼此不兼容。例如,假设您要打印格式字符串中的块字符。在使用 IBM 扩展 ASCII 字符编码的 SyncTERM 中,您将使用八进制转义序列,例如\261.在 Terminal.app(可能还有 iTerm2)中,这只是打印一个问号。由于这些终端使用 UTF-8,因此您需要使用\uxxxx转义序列。

假设您想要在格式字符串中打印某个非 ASCII 字符,并且希望它能够在所有终端模拟器中工作,而不管字符集如何。我猜您会使用 terminfo 数据库中的条目,但我不太熟悉 terminfo。我这里需要一些指点。

答案1

短的:

  • terminfo 不会带你去那里,不会有帮助
  • 没有可靠的方法来确定终端实际使用的编码
  • 从 Unicode 文字开始是可行的方法,前提是您知道要在终端上使用什么编码
  • 用户必须知道什么区域设置是合适的以及终端可以执行什么编码
  • C 标准具有转换“宽”字符的函数,您可以在任何类 Unix 平台上使用这些函数(例如,参见setlocale,wcrtombwcsrtombs

答案2

使用 a 初始化应用程序的区域设置setlocale(LC_ALL, ""),然后调用nl_langinfo(CODESET)。这将为您提供 LANG、LC_CTYPE、LC_ALL 环境变量的解析值。

这确实不是告诉您终端模拟器实际上是如何工作的,但这几乎是每个应用程序所依赖的。如果这给出了不正确的结果,那么您的系统配置错误,并且几乎所有其他应用程序也将在您的终端模拟器中无法正常工作。作为应用程序开发人员,您的工作不是尝试检测并修复它是否有问题。您可以放心地假设它已为您正确设置。作为系统管理员或发行版开发人员或在系统上进行黑客攻击的用户,您的工作就是确保区域设置变量和终端仿真器的实际行为匹配。

答案3

如果终端仿真器设计良好并配置得当,它将确保环境变量的值LC_CTYPE设置为与其编码一致的值。不幸的是,在实践中,检查LC_CTYPE并不总是可靠的:它可能未设置或错误。 (其他环境变量可能传达区域设置,请参阅我应该将区域设置设置为什么?这样做会产生什么影响?了解详情。)

如果您对可能的字符编码有所了解,您也许能够通过启发式方法确定编码。显示不同编码下具有不同宽度的字节字符串,并找出它使光标移动了多少。这并不在所有情况下都对您有帮助,例如它无法区分单字节编码。但如果对您来说唯一可能的两种可能性是 UTF-8 和一种旧编码,那么效果很好。在我的 shell 启动中,我使用我发布的LC_CTYPE脚本以这种方式设置widthof获取字符串的显示宽度widthof -1显示一个 4 字节字符串,表示 UTF-8 中的 2 个字符,其中只有 3 个字节是可打印的 latin-N 字符。因此,宽度 2 表示 UTF-8(或其他一些多字节编码,这对我来说不太可能),宽度 3 表示 latin-N(无法知道 N),而 4 表示某种单字节编码可打印字符的范围为 128–159。

widthof -1
case $? in
  0) export LC_CTYPE=C;; # 7-bit charset
  2) locale_search .utf8 .UTF-8;; # utf8
  3) locale_search .iso88591 .ISO8859-1 .latin1 '';; # 8-bit with nonprintable 128-159, we assume latin1
  4) locale_search .iso88591 .ISO8859-1 .latin1 '';; # some full 8-bit charset, we assume latin1
  *) export LC_CTYPE=C;; # weird charset
esac

相关内容