Emacs、xterm、mousepad、C、Unicode和UTF-8:试图理解这一切

Emacs、xterm、mousepad、C、Unicode和UTF-8:试图理解这一切,c,emacs,unicode,utf-8,xterm,C,Emacs,Unicode,Utf 8,Xterm,免责声明:我对以下所有内容(针对一个简单的问题)表示歉意,但我真诚地认为每一点信息都与问题相关。我很高兴能学到其他东西。我只能希望,如果成功的话,这些问题和答案可以帮助其他疯狂的人。来吧 我已经阅读了所有通常备受推崇的utf8网站,尤其是对我来说非常好的网站,但我也阅读了经典作品,就像SO中其他类似问题中提到的那样。然而,我仍然缺乏关于如何在我的虚拟实验室中集成这一切的知识 ;; Internationalization (prefer-coding-system 'utf-8) (setq l

免责声明:我对以下所有内容(针对一个简单的问题)表示歉意,但我真诚地认为每一点信息都与问题相关。我很高兴能学到其他东西。我只能希望,如果成功的话,这些问题和答案可以帮助其他疯狂的人。来吧

我已经阅读了所有通常备受推崇的utf8网站,尤其是对我来说非常好的网站,但我也阅读了经典作品,就像SO中其他类似问题中提到的那样。然而,我仍然缺乏关于如何在我的虚拟实验室中集成这一切的知识

;; 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)
在my.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;
}
(以防我得到的字符是黑色圆圈内的白色问号)。整数是编码€所需的3个字节的十进制表示形式,但我不确定xterm为什么不能正确显示它们

取而代之的是鼠标垫(如指纹)

Character: â, Integer: 226
Character: ,, Integer: 130 (a comma, standing forU+0082 <control>, why?!)
Character: ¬, Integer: 172
问题:我能问的最普遍的问题是:如何让所有内容都打印相同的字符?但我肯定会有后续行动


再次感谢,并为所有文本表示歉意。

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

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

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


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

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


E-macs是智能的,它知道单个字节对于输出流来说是无效的数据,并用字节的可见转义表示替换它。鼠标垫的输出真的坏了,我搞不懂。Mousepad正在退回到CP1252 Windows代码页,其中单个字节表示字符。“逗号”不是逗号,它是一个逗号。

您发布的第一件事:

Character: � Integer: 226
Character: �, Integer: 130
Character: �, Integer: 172
这是“正确”的答案。当您打印字符226且终端需要utf8时,终端无法执行任何操作,您为其提供了无效数据。序列“226”“空格”是一个错误。这个字符是一种很好的方式,可以向您显示某个地方存在格式错误的数据

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

设想两种功能;解码,采用字符编码和八位字节流并产生字符列表;和encode,它对字符列表进行编码并生成八位字节流。当数据有效时,编码/解码应该是可逆的:编码('utf8',解码('utf8',“…”)==“…”

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

如果有GNU重新编码,请尝试以下操作:

$ recode latin1..utf8
<three-octet representation of the euro character> <control-D>
â¬
如您所见,字符的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编码文件,则需要先将八位字节解码为字符,然后再在应用程序中对其进行处理。

只是为了澄清-您希望看到组成“€”的三个字节,还是希望看到欧元符号的单个多字节呈现-即“字符:€,整数XXX”?组成€的三个字节。但最重要的是,我想确切地知道一般情况下发生了什么。谢谢,戴夫。莫塞帕德可能又回到了标准代码页。在CP1252(Windows)中,十进制数130='、'decimal 226='–',以及十进制数172='。鼠标垫被破坏:Firefox以与鼠标垫相同的方式呈现具有相同字符的文本文件。有什么线索吗?我看到的一件事是,如果文件开头不包含字节顺序标记(BOM),一些程序会将其视为ASCII(并使用当前代码)
$ recode latin1..utf8
<three-octet representation of the euro character> <control-D>
â¬
$ cat | hd
€
00000000  e2 82 ac 0a               |....|
00000004
$ recode latin1..utf8 | hd
€
00000000  c3 a2 c2 82 c2 ac 0a      |.......|
00000007