5

免责声明:对于下面的所有文字(针对一个简单的问题),我深表歉意,但我真诚地认为每一点信息都与问题相关。我很乐意学习其他方式。我只能希望,如果成功,问题和答案可以帮助其他人在 Unicode 疯狂。开始。

我已经阅读了所有通常备受推崇的关于 utf8 的网站,尤其是这个非常适合我的目的,但我也阅读了经典,就像 SO 中其他类似问题中提到的那些。但是,我仍然缺乏如何将其全部集成到我的虚拟实验室中的知识。我使用 Emacs

;; Internationalization
(prefer-coding-system 'utf-8)
(setq locale-coding-system 'utf-8)
(set-terminal-coding-system 'utf-8)
(set-keyboard-coding-system 'utf-8)
(set-selection-coding-system 'utf-8)

在我的 .emacs 中,xterm 以

 LC_CTYPE=en_US.UTF-8 xterm -geometry 91x58\
-fn '-Misc-Fixed-Medium-R-SemiCondensed--13-120-75-75-C-60-ISO10646-1'

我的语言环境如下:

LANG=en_US.UTF-8
LC_CTYPE=en_US.UTF-8
LC_NUMERIC="en_US.UTF-8"
LC_TIME="en_US.UTF-8"
LC_COLLATE="en_US.UTF-8"
LC_MONETARY="en_US.UTF-8"
LC_MESSAGES="en_US.UTF-8"
LC_PAPER="en_US.UTF-8"
LC_NAME="en_US.UTF-8"
LC_ADDRESS="en_US.UTF-8"
LC_TELEPHONE="en_US.UTF-8"
LC_MEASUREMENT="en_US.UTF-8"
LC_IDENTIFICATION="en_US.UTF-8"
LC_ALL=

我的问题如下(一些答案可能是应用程序的预期行为,但我仍然需要理解它,所以请耐心等待):

假设以下 C 程序:

#include <stdio.h>

int main(void) {
  int c;
  while((c=getc(stdin))!=EOF) {
    if(c!='\n') {
      printf("Character: %c, Integer: %d\n", c, c);
    }
  }
  return 0;
}

如果我在我的 xterm 中运行它,我会得到:

€
Character: � Integer: 226
Character: �, Integer: 130
Character: �, Integer: 172

(以防万一我得到的字符是黑色圆圈内的白色问号)。整数是编码 € 所需的 3 个字节的十进制表示,但我不确定为什么 xterm 不能正确显示它们。

相反,鼠标垫,例如,打印

Character: â, Integer: 226
Character: ,, Integer: 130 (a comma, standing forU+0082 <control>, why?!)
Character: ¬, Integer: 172

同时,Emacs 显示

Character: \342, Integer: 226
Character: \202, Integer: 130
Character: \254, Integer: 172

问题:我能问的最普遍的问题是:如何让所有内容都打印相同的字符?但我确信会有后续行动。

再次感谢,并为所有文字道歉。

4

3 回答 3

7

好的,所以您的问题是由于混合了老式 C 库调用(getc,printf %c)和 UTF-8。您的代码正确读取了组成“€”的三个字节 - 226、130 和 172 作为十进制 - 但这些值单独不是有效的 UTF-8 编码字形。

如果您查看UTF-8 encoding,整数值 0..127 是原始 US-ASCII 字符集的编码。但是 128..255 (即所有字节)是多字节 UTF-8 字符的一部分,因此不对应于单独的有效 UTF-8 字符。

换句话说,单字节“226”本身并不意味着任何东西(因为它是 3 字节字符的前缀 - 正如预期的那样)。该printf调用将其打印为单个字节,这对于 UTF-8 编码是无效的,因此每个不同的程序以不同的方式处理无效值。

假设您只想“查看” UTF-8 字符由哪些字节组成,我建议您坚持使用已有的整数输出(或者如果更明智,可以使用十六进制) - 因为您的 >127 字节无效unicode 您不太可能在不同的程序中获得一致的结果。

于 2009-07-17T22:22:52.650 回答
3

UTF-8 编码表示字符串中的三个字节一起形成欧元符号或“€”。但是单字节,比如你的 C 程序产生的字节,在 UTF-8 流中没有意义。这就是为什么它们被替换为 U+FFFD“替换字符”或“�”。

E-macs 很聪明,它知道单个字节对于输出流来说是无效数据,并将其替换为字节的可见转义表示。鼠标垫输出真的坏了,我无法理解。鼠标垫回退到 CP1252 Windows 代码页,其中单个字节代表字符。“逗号”不是逗号,它是一个低弯引号

于 2009-07-17T22:24:45.643 回答
1

您发布的第一件事:

Character: � Integer: 226
Character: �, Integer: 130
Character: �, Integer: 172

是“正确”的答案。当您打印字符 226 并且终端需要 utf8 时,终端无能为力,您给了它无效的数据。序列“226”“空格”是一个错误。这 ?character 是向您展示某处存在格式错误数据的好方法。

如果要复制第二个示例,则需要正确编码字符。

想象两个功能;decode,它接受一个字符编码和一个八位字节流并产生一个字符列表;和encode,它对一个字符列表进行编码并产生一个八位字节流。当您的数据有效时,编码/解码应该是可逆的:编码('utf8',解码('utf8',“...”))==“...”。

无论如何,在第二个示例中,应用程序(“mousepad?”)将欧元字符的三个八位字节表示中的每个八位字节视为单独的 latin1 字符。它获取八位字节,将其从 latin-1 解码为“字符”(不是八位字节或字节)的某种内部表示,然后将该字符编码为 utf8 并将其写入终端。这就是它起作用的原因。

如果你有 GNU Recode,试试这个:

$ recode latin1..utf8
<three-octet representation of the euro character> <control-D>
â¬

这样做是将 utf-8 表示的每个八位字节视为 latin1 字符,然后将这些字符中的每一个转换为您的终端可以理解的内容。也许通过 hd 运行它会更清楚:

$ cat | hd
€
00000000  e2 82 ac 0a               |....|
00000004

如您所见,字符的 utf-8 表示形式是 3 个八位字节,然后是换行符。

通过重新编码运行:

$ recode latin1..utf8 | hd
€
00000000  c3 a2 c2 82 c2 ac 0a      |.......|
00000007

这是“latin1”输入字符串的 utf-8 表示;你的终端可以显示的东西。这个想法是,如果你输出到你的终端,你会看到欧元符号。如果你输出,你什么也得不到,那是无效的。最后,如果您输出 ,您将得到“垃圾”,即字符的“utf-8 表示”。

如果这看起来令人困惑,那就是。你永远不应该担心这样的内部表示;如果您正在使用字符并且需要将它们打印到 utf-8 终端,则必须始终编码为 utf-8。如果您正在读取 utf-8 编码文件,则需要先将八位字节解码为字符,然后再在应用程序中处理它们。

于 2009-07-18T00:22:48.190 回答