很久很久之前
学C语言的时候, 文件函数涉及到所谓的文本文件和二进制文件. 因为一直没有做过实验, 所以一直只是"大概"知道是怎么回事. 今天又遇到这个问题, 于是做了个小实验.
实验一:
步骤:在windows下新建一个文本文件并输入内容”a“,保存。 用文本编辑器打开,可以看到一个字符”a“。(除非见鬼了)。然后选择以16进制重新打开文件,当当当,一个”61“出现了,显然这个”61“是字符”a“的ascii码。查看文件大小,一个byte刚好。
结论:在实验条件下,文本文件没有特殊的存储方式。仅仅是将字符对应的编码存储起来而已。
主要是想确定文本文件的存储方式,有没有文件头等信息,进而弄清楚和二进制文件的区别。大的知识大家都懂,就不累赘了。
后面打算说说字符编码,顺便讲讲我们平时打开一个文本编辑器,敲下键盘,屏幕上出现对应的字符,这个”现象“后面的故事。占个位先……
-------------------------------------------------------------------------------------------------------------------------------------
迟到的填坑
真是不好意思,这个位空占了好久。那么,我来填坑了。
为了说明上面的问题,要涉及到3个东西 - 键盘输入、字符集/编码、输入法。
键盘输入
中断
键盘输入跟中断密切相关,不妨顺便先聊聊中断。
---------------------------------------------------------------------------------------------------------------------------------------------------
# 以下部分摘录自王爽先生的《汇编语言》
---------------------------------------------------------------------------------------------------------------------------------------------------
任何一个通用的CPU,都具备一种能力,可以在执行完当前正在执行的指令之后,检测到从CPU外部发送过来的或内部产生的一种特殊信息,并且可以立即对所接收到的信息进行处理。这种特殊的信息,我们可以称其为:中断信息。
中断的意思是指,CPU不再接着(刚执行完的指令)向下执行,而是转去处理这个特殊信息。
中断处理程序
CPU收到中断信息后,需要对中断信息进行处理。而如何对中断信息进行处理,可以由我们编程决定。我们编写的,用来处理中断信息的程序被称为中断处理程序。
中断向量表
CPU在收到中断信息后,应该转去执行该中断信息对应的处理程序。我们知道,要让CPU执行某处的程序,就要将CS:IP执行它的入口(即程序第一条指令的地址)。那么,CPU在收到中断信息后,如何根据中断信息确定其处理程序的入口?
CPU的设计者必须在中断信息和其处理程序的入口地址之间建立某种联系,使得CPU根据中断信息可以找到要执行的处理程序。
上面是一个典型的汇编中断调用,调用21h号中断。我们可以看到,中断信息包含有标志中断源的类型码(这里是21h)。根据CPU的设计,中断类型码的作用就是用来定位中断处理程序的。比如CPU根据中断类型码21h,就可以找到33号(21h是16进制数)中断的处理程序。因此,内存中维护着一个中断向量表。所谓中断向量表,就是中断处理程序入口地址的列表。
中断向量表
0号中断源对应的中断处理程序的入口地址 |
1号中断源对应的中断处理程序的入口地址 |
2号中断源对应的中断处理程序的入口地址 |
…… |
中断向量表在内存的固定位置(8086PC机指定放在内存地址0处),每一项占固定长度。因此,CPU就可以根据中断类型码在中断向量表中找到对应处理程序的入口地址,从而执行对中断信息的处理了。
举个例子:
int 08h
假设每一个表项占4个字节,CPU处理的过程是,从内存地址 0 + 4×8 处取得对应处理程序的入口地址,设置给CS:IP。然后控制权就移交给对应的中断处理程序了。
回到键盘输入
键盘上的一个键相当于一个开关。按下一个键时,开关接通,键盘控制芯片就产生一个扫描码,扫描码对应着刚刚被按下的键。接着,扫描码被送入主板上相关接口芯片的寄存器中,该寄存器的端口地址为60h。
松开按下的键也产生一个扫描码,同样对应着刚刚被松开的键,同样被送入60h端口。
一般将按下一个键产生的扫描码称为通码,松开一个键产生的扫描码称为断码。扫描码长度为一个字节,通码的第7位为0,断码的第7位为1,即:
断码 = 通码 +80h (表示16进制 HEX)
比如,D 键的通码是20h,断码是a0h
下表是键盘上部分键的扫描码,只列出通码,断码 = 通码 + 80h
键盘上部分键的扫描码
键 | 扫描码 | 键 | 扫描码 | 键 | 扫描码 | 键 | 扫描码 |
Esc | 01 | Q | 10 | I | 17 | A | 1E |
1~9 | 02~0A | W | 11 | O | 18 | S | 1F |
0 | 0B | E | 12 | P | 19 | D | 20 |
- | 0C | R | 13 | [ | 1A | F | 21 |
= | 0D | T | 14 | ] | 1B | G | 22 |
Backspace | 0E | Y | 15 | Enter | 1C | H | 23 |
Tab | 0F | U | 16 | Ctrl | 1D | J | 24 |
键盘的输入到达60h端口时,相关的芯片就会向CPU发出中断类型码为9的可屏蔽中断信息。CPU检测到该中断信息后,如果处于可响应中断的状态(由CPU的标志寄存器指明),则响应中断,引发中断过程,转去执行 int 9 中断例程(中断处理程序的另一说法)。
BIOS提供了int 9 中断例程,用来进行基本的键盘输入处理,主要的工作如下:
- 读出60h端口中的扫描码;
- 如果是字符键的扫描码,将该扫描码和它对应的字符码(ASCII码)送入内存中的BIOS键盘缓冲区;如果是控制键(如何Ctrl)和切换键(比如CapsLock)的扫描码,则更新内存中储存状态字节的单元。
- 读键盘系统进行相关的控制,比如说,向相关芯片发出应答信息。
总结一下,键盘输入的过程是:①键盘产生扫描码;②扫描码送入60h端口;③引发9号中断;④CPU执行int 9 中断例程处理键盘输入。
4个步骤执行完后,键盘输入的内容就已经转换为对应的ASCII码,乖乖地躺在内存中等待我们使用了。比如我们的记事本可以读取键盘缓冲区的字符到记事本程序自己的缓冲区,然后爱干嘛干嘛。
但是,有一个问题:BIOS键盘缓冲区的字符只能是ASCII字符,那我们远东地区的乡亲们要怎么输入中文呢?很不幸,我们又要来一次很久很久以前……
---------------------------------------------------------------------------------------------------------------------------------------------------
# 以上部分摘录自王爽先生的《汇编语言》
---------------------------------------------------------------------------------------------------------------------------------------------------
字符集/编码
这个故事我们有请Kevin Yang同学给大家
讲。
输入法
引用MSDN上对“输入法编辑器” (input method editor)的概述:
IME 是一种软件组件,可让用户(通常是但不限于东亚用户)输入由于语言中的字符太多而不便用硬件键盘表现的语言文字。
用户键入的不是键盘上显示的每个单一字符,而是键入由 IME 解释的键组合。IME 生成的字符与一组键击匹配,可能向用户显示供其选择的候选字符列表,然后将字符插入用户交互的编辑控件。
我想用图来说明:

由于我对输入法和操作系统相关部分不了解,不清楚输入法传递给应用程序的是字符码还是字符编码。字符编码看起来是合理的猜测,但是大多数文本编辑器在保存文件时,都可以让用户选择编码方案,从这个角度看,字符码也不是完全没有可能。希望知道的朋友赐教!
另外,关于输入法推荐几个参考:
- 优快云 - 输入法工作原理;
- MSDN - 输入法编辑器;
ENDING
到此这个坑算是填完了。整篇文章引用摘抄居多,依旧标翻译。