我将Lubuntu中Konsole的字符编码设置为UTF-16(不确定Konsole使用UTF-16小端还是UTF-16大端)。
然后我在“运行”窗口中运行以下命令:
然后我按下键盘上的“a”按钮(在 Lubuntu 中选择的键盘布局是英语),我认为这会导致字节61 00
(如果 Konsole 使用 UTF-16 小端)发送到线路纪律,以及线路纪律依次将这些字节回显给 Konsole,Konsole 将显示字符“a”。但我得到了以下结果:
我再次按下“a”按钮,得到以下信息:
我再次按下“a”按钮,得到以下信息:
我再次按下“a”按钮,得到以下信息:
为什么我得到这些奇怪的字符而不是简单地得到字符“aaaa”?
编辑:
这些是 Konsole 中显示的字符:
觉醒`觉醒䁞觉醒`觉醒䁞
答案1
让我们进一步强调这xxd -p
是无关紧要的,我们不是谈论它是输出。由于内核端的行缓冲,它甚至没有看到输入,因此没有产生任何输出。就其价值而言,它也可以是 acat
或 asleep 100000
或其他任何形式。我们正在谈论如何核心(线路规则)回显输入。
如果切换回 UTF-8,然后按 Enter 键xxd -p
,其输出将类似于fffe6100fffe6100
。确认了如此少的字节序(或者可能是架构的本机字节顺序),但令人惊讶的是,每个字符之前都有一个 BOM。让我怀疑 Konsole 开发人员没有正确考虑这一点,他们只是盲目地以 UTF-16(未指定 BE 或 LE)作为目标字符集调用 iconv,对于每个可用的输入块,并且 iconv 放置它在那里。
让我们通过strace
konsole 看看它对连接到的文件描述符做了什么/dev/ptmx
:
write(..., "\377\376a\0", 4) = 4
[...]
read(..., "\377\376a^@", 5) = 5
NUL 字节 (0x00) 以文字形式返回^@
,即 0x5e 后跟 0x40。
与a
(0x61) 一起,这将为您提供 U+5e61,这正是您看到的第一个字形。另外,您将偏离一个字节,也就是说,下一个所谓的低字节将被解释为高字节,反之亦然。
对于字节,0x00
内核只是破坏了它的回显方式。对于其他一些字节,它也执行其他操作。例如,字节0x03
( ^C
) 通常会触发发送到前台进程的中断,0x15
( ^U
) 会清除您到目前为止输入的数据,0x0a
和/或0x0d
(即换行符)将数据刷新到您的应用程序等。所有这些bytes 可以(并且确实)合法地出现在字符的 UTF-16 表示形式中,并且您肯定不希望在键入输入时发生任何这些情况。
为了在门徒行上使用 UTF-16,内核需要为此提供明确的支持,并且需要被告知正在使用这种编码(类似于 an 的方式stty utf16
)。据我所知,这并没有实现(幸运的是——这完全是对开发人员资源的浪费)。内核期望使用与 ASCII 兼容的编码,而 UTF-16 则不然。
即使在内核中为 tty 线路实现了 UTF-16,整个生态系统也必然非常脆弱。终端可以同时从多个源接收数据,并且无法保证所有数据生产者和所有传输者(例如ssh)始终保持字节成对耦合。一旦它丢失了一个字节(如上所示),其余的就无法使用。
我现在更加确定 Konsole 开发人员没有正确考虑这一点。在我看来,UTF-16 应该从他们提供的编码列表中删除,或者至少应该显示一个警告。我已提交Konsole 错误 395171。