1

我正在使用键盘将多语言文本输入到 Web 浏览器显示的表单中的字段中。在与 O/S 和浏览器无关的级别上,我认为会发生以下事件(如果我错了,请纠正我,因为我认为我是):

  1. 在每次按键时,都会有一个中断指示按键被按下
  2. O/S(或键盘驱动程序?)确定键码并将其转换为某种键盘事件(字符、修饰符等)。
  3. O/S 的窗口管理器查找当前聚焦的窗口(浏览器)并将键盘事件传递给它
  4. 浏览器的 GUI 工具包查找当前聚焦的元素(在本例中为我正在输入的字段)并将键盘事件传递给它
  5. 该字段会自行更新以包含新字符
  6. 发送表单时,浏览器在将输入的文本发送到表单目标之前对其进行编码(什么编码?)

在我继续之前,这是实际发生的事情吗?我是否错过或掩盖了任何重要的事情?

接下来,我想问一下:在上面的每个步骤中,字符是如何表示的?在第 1 步,密钥代码可以是特定于设备的幻数。在第 2 步,键盘驱动程序可以将其转换为操作系统可以理解的内容(例如,USB HID 规范:http ://en.wikipedia.org/wiki/USB_human_interface_device_class )。接下来的步骤呢?我认为第 3 步和第 4 步的编码分别取决于操作系统和应用程序(浏览器)。它们会不会有所不同,如果可以,这个问题是如何解决的?

我问的原因是我遇到了一个特定于我最近开始使用的网站的问题:

在此处输入图像描述

事情似乎一直在进行,直到上面的第 6 步,提交了输入文本的表单,之后文本被破坏得面目全非。虽然很明显该网站没有正确处理 Unicode 输入,但这一事件让我质疑我自己对事情如何运作的理解,现在我在这里。

4

2 回答 2

1

你的描述或多或少是正确的。

但是,了解该站点的问题并不重要。

问号而不是字符表示发生了编码之间的转换,而不是编码的错误表示(这可能会导致乱码。)

用来表示字母的字符可以用不同的方式编码。例如,ASCII 中的“a”是 0x61,而 EBCDIC 中的“a”是 0x81。这您可能知道,人们往往会忘记 ASCII 是仅包含英文字符的 7 位代码。由于 PC 计算机使用字节作为它们的存储单元,因此早期 ASCII 码中未使用的前 128 个位置用于表示其他字母表中的字母,但是哪一个呢?西里尔?希腊语?等等。DOS 使用代码页号来指定使用哪些符号。大多数(全部?) DOS 代码页保持低 128 个符号不变,因此无论使用什么代码页,英语看起来都像英语;但是尝试使用希腊语代码页来读取俄语文本文件,您最终会得到希腊语和符号乱码。

后来 Windows 添加了它自己的编码,其中一些具有可变长度的编码(与 DOS 代码页相反,其中每个字符由单个字节代码表示),然后 Unicode 出现了引入代码点的概念。

在代码点下,每个字符都分配有一个由通用数字标识的代码点,即代码点由一个数字而不是 16 位数字标识。Unicode 还定义了将代码点编码为字节的编码。UCS-2 是一种固定长度编码,将代码点编号编码为 16 位数字。超过 16 位的代码点会发生什么情况,很简单,它们无法在 UCS-2 中编码。当从支持特定代码点的编码转换为不支持特定代码点的编码时,该字符被替换为指定字符,通常是问号。

因此,如果我得到一个带有希伯来字符 aleph 'א' 的 UTF-16 传输并将其翻译为没有此类字符的 latin-1 编码(或正式的 latin-1 没有代码点来表示 unicode 代码点 U+ 05D0) 我会得到一个问号字符而不是“?”

网站上发生的事情正是这样,它可以很好地获取您的输入,但它正在被转换为不支持您输入中所有字符的编码。

不幸的是,与可以通过手动指定页面编码来修复的编码错误陈述不同,您无法在客户端上解决此问题。

一个相关的问题是使用没有显示字符的字体。在这种情况下,您会看到一个空白方块而不是字符。可以通过覆盖站点 CSS 或安装适当的字体在客户端上解决此问题。

于 2013-02-06T04:40:56.373 回答
1

从按键到应用的字符剖析:

1 - 电脑键盘:

PC 键盘不是唯一的键盘类型,但我会限制自己使用它们。
令人惊讶的是,PC 键盘不理解字符,他们理解键盘按钮。这允许美国键盘使用的相同硬件用于 QEWERTY 或 Dvorak 以及使用美国 101/104 键格式的任何其他语言的英语(某些语言有额外的键。)

键盘使用标准扫描码来识别按键,为了使事情更有趣,可以将键盘配置为使用一组特定的代码:

Set 1 - 用于旧的 XT 键盘
Set 2 - 当前使用,
Set-3 用于今天没有人使用的 PS/2 键盘。

设置 1 和 2 使用通断代码(即按下和释放代码)。Set 3 仅对某些键(如 shift)使用 make 和 break 代码,并且只为字母制作代码,这允许键盘本身在长时间按下键时处理键重复。这对于从 PS/2 8086 或 80286 处理器卸载键重复处理很有好处,但对游戏来说却很不利。

您可以在此处阅读有关这一切的更多信息,我还找到了Microsoft 扫描代码规范,以防您想构建和认证自己的 104 键 Windows 键盘。

在任何情况下,我们都可以假设 PC 键盘使用 set 2,这意味着它会在按下键时向计算机发送一个代码,并在释放一个键时向计算机发送一个代码。
顺便说一下,USB HID 规范没有指定键盘发送的扫描码,它只指定了用于发送这些扫描码的结构。
现在,由于我们谈论的是硬件,所有操作系统都是如此,但是每个操作系统处理这些代码的方式可能会有所不同。我将限制自己在 Windows 中发生的事情,但我认为其他操作系统应该遵循大致相同的路径。

2 - 操作系统

我不知道 Windows 究竟是如何处理键盘的,哪些部分由驱动程序处理,哪些由内核处理,哪些在用户模式下;但足以说定期轮询键盘以更改为键状态,扫描代码被翻译并转换为包含虚拟键代码的 WM_KEYDOWN/WM_KEYUP 消息。准确地说,Windows 也会生成 WM_SYSKEYUP/WM_SYSKEYDOWN 消息,您可以在此处阅读有关它们的更多信息

3 - 应用程序

对于 Windows,应用程序获取原始虚拟键代码,并由它决定按原样使用它们或将它们转换为字符代码。
现在没有人写出好的诚实的 C windows 程序,但曾几何时,程序员曾经推出自己的消息泵处理代码,大多数消息泵将包含类似于以下的代码:

while (GetMessage( &msg, NULL, 0, 0 ) != 0)
{ 
        TranslateMessage(&msg); 
        DispatchMessage(&msg); 
} 

TranslateMessage 是魔法发生的地方。TranslateMessage 中的代码将跟踪 WM_KEYDOWN(和 WM_SYSKEYDOWN)消息并生成 WM_CHAR 消息(和 WM_DEADCHAR、WM_SYSCHAR、WM_SYSDEADCHAR。)
WM_CHAR 消息包含 UTF-16(实际上是 UCS-2,但不允许拆分头发)代码考虑到当时的活动键盘布局,从 WM_KEYDOWN 消息翻译的字符。
在 unicode 之前编写的应用程序呢?这些应用程序使用 ANSI 版本的 RegisterClassEx(即 RegisterClassExA)来注册它们的窗口。在这种情况下,TranslateMessage 根据键盘布局和活动区域性生成带有 8 位字符代码的 WM_CHAR 消息。

4 - 5 - 调度和显示字符。

在使用 UI 库的现代代码中,完全有可能(尽管不太可能)不使用 TranslateMessage 并自定义翻译 WM_KEYDOWN 事件。标准窗口控件(小部件)理解和处理发送给它们的 WM_CHAR 消息,但在窗口下运行的 UI 库/VM 可以实现它们自己的调度机制,而且许多都可以这样做。

希望这能回答你的问题。

于 2013-02-06T11:10:48.787 回答