type
status
date
slug
summary
tags
category
icon
password
Property
单字符I/O
字符输入/输出函数:
getchar()
和putchar()
,这两个函数通常定义在 stdio.h
头文件中。getchar()
函数不带任何参数,它从输入队列中返回下一个字符:putchar()
函数打印它的参数这些函数只处理字符,比更通用的
scanf()
和printf()
函数更快、更简洁。而且 getchar()
和 putchar()
不需要转换说明,因为它们只处理字符。ctype.h系列的字符函数
ctype.h头文件中的字符测试函数:
函数名 | 如果是下列参数,返回值为真 |
isalnum | 字母或数字 |
isalpha | 字母 |
isblank | 标椎的空白字符(空格 水平制表符) |
iscntrl | 控制字符 ctrl+B |
isdigit | 数字 |
isgraph | 除空格之外的任意可打印字符 |
islower | 小写字母 |
isprint | 可打印字符 |
ispunct | 标点符号 (除空格或字母数字字符以外的任意可打印的字符) |
isspace | 空白字符(空格,换行 ,换页, 回车, 垂直制表符 ,水平制表符) |
isupper | 大写字母 |
isxdigit | 十六进制数字符 |
ctype.h头文件中的字符映射函数:
函数名 | 行为 |
tolower | 如果参数是大写字符,该函数返回小写字符;否则返回原始参数 |
toupper | 如果参数是小写字符,该函数返回大写字符;否则返回原始参数 |
缓冲区
为什么要有缓冲区?
首先,把若干字符作为一个块进行传输比逐个发送这些字符节约时间。其次,如果用户打错字符,可以直接通过键盘修正错误。当最后按下Enter键时,传输的是正确的输入。
缓冲分为两类:完全缓冲I/O和行缓冲I/O。
- 完全缓冲输入指的是当缓冲区被填满时才刷新缓冲区(内容被发送至目的地),通常出现在文件输入中。缓冲区的大小取决于系统,常见的大小是 512 字节和 4096字节。
- 行缓冲I/O指的是在出现换行符时刷新缓冲区。键盘输入通常是行缓冲输入,所以在按下Enter键后才刷新缓冲区。
虽然缓冲输入好处很多,但是某些交互式程序也需要无缓冲输入。例如,在游戏中,你希望按下一个键就执行相应的指令。因此,缓冲输入和无缓冲输入都有用武之地。那么,使用缓冲输入还是无缓冲输入?
ANSI C和后续的C标准都规定输入是缓冲的,不过最初K&R把这个决定权交给了编译器的编写者。
ANSI C决定把缓冲输入作为标准的原因是:一些计算机不允许无缓冲输入。许多IBM PC兼容机的编译器都为支持无缓冲输入提供一系列特殊的函数,其原型都在
conio.h
头文件中。这些函数包括用于回显无缓冲输入的getche()
函数和用于无回显无缓冲输入的getch()
函数(回显输入意味着用户输入的字符直接显示在屏幕上,无回显输入意味着击键后对应的字符不显示)。UNIX系统使用另一种不同的方式控制缓冲。在UNIX系统中,可以使用ioctl()
函数(该函数属于UNIX库,但是不属于C标准)指定待输入的类型,然后用getchar()
执行相应的操作。在ANSI C中,用setbuf()
和setvbuf()
函数控制缓冲,但是受限于一些系统的内部设置,这些函数可能不起作用。总之,ANSI没有提供调用无缓冲输入的标准方式,这意味着是否能进行无缓冲输入取决于计算机系统。结束键盘输入
文件、流和键盘输入
文件(file)是存储器中储存信息的区域。通常,文件都保存在某种永久存储器中(如,硬盘等)。编写的C程序就保存在文件中,用来编译C程序的程序也保存在文件中。后者说明,某些程序需要访问指定的文件。当编译储存在名为
echo.c
文件中的程序时,编译器打开echo.c
文件并读取其中的内容。当编译器处理完后,会关闭该文件。其他程序,例如文字处理器,不仅要打开、读取和关闭文件,还要把数据写入文件。C 是一门强大、灵活的语言,有许多用于打开、读取、写入和关闭文件的库函数。从较低层面上,C可以使用主机操作系统的基本文件工具直接处理文件,这些直接调用操作系统的函数被称为底层 I/O。由于计算机系统各不相同,所以不可能为普通的底层I/O函数创建标准库,ANSI C也不打算这样做。然而从较高层面上,C还可以通过标准I/O包来处理文件。这涉及创建用于处理文件的标准模型和一套标准I/O函数。在这一层面上,具体的C实现负责处理不同系统的差异,以便用户使用统一的界面。
不同的系统储存文件的方式不同。有些系统把文件的内容储存在一处,而文件相关的信息储存在另一处;有些系统在文件中创建一份文件描述。在处理文件方面,有些系统使用单个换行符标记行末尾,而其他系统可能使用回车符和换行符的组合来表示行末尾。有些系统用最小字节来衡量文件的大小,有些系统则以字节块的大小来衡量。
如果使用标准 I/O 包,就不用考虑这些差异。因此,可以用
if (ch ==)
检查换行符。即使系统实际用的是回车符和换行符的组合来标记行末尾,I/O函数会在两种表示法之间相互转换。
从概念上看,C程序处理的是流而不是直接处理文件。
流(stream)是一个实际输入或输出映射的理想化数据流。这意味着不同属性和不同种类的输入,由属性更统一的流来表示。于是,打开文件的过程就是把流与文件相关联,而且读写都通过流来完成。stdin流表示键盘输入,stdout流表示屏幕输出。
getchar()
、putchar()
、printf()
和scanf()
函数都是标准I/O包的成员,处理这两个流。可以用处理文件的方式来处理键盘输入。例如,程序读文件时要能检测文件的末尾才知道应在何处停止。因此,C 的输入函数内置了文件结尾检测器。既然可以把键盘输入视为文件,那么也应该能使用文件结尾检测器结束键盘输入。
文件结尾
计算机操作系统要以某种方式判断文件的开始和结束。检测文件结尾的一种方法是,在文件末尾放一个特殊的字符标记文件结尾。CP/M、IBMDOS和MS-DOS的文本文件曾经用过这种方法。如今,这些操作系统可以使用内嵌的Ctrl+Z字符来标记文件结尾。这曾经是操作系统使用的唯一标记,不过现在有一些其他的选择,例如记录文件的大小。所以现代的文本文件不一定有嵌入的Ctrl+Z,但是如果有,该操作系统会将其视为一个文件结尾标记。
操作系统使用的另一种方法是储存文件大小的信息。如果文件有3000字节,程序在读到3000字节时便达到文件的末尾。MS-DOS 及其相关系统使用这种方法处理二进制文件,因为用这种方法可以在文件中储存所有的字符,包括Ctrl+Z。新版的DOS也使用这种方法处理文本文件。UNIX使用这种方法处理所有的文件。
无论操作系统实际使用何种方法检测文件结尾,在C语言中,用
getchar()
读取文件检测到文件结尾时将返回一个特殊的值,即EOF(end of file)。scanf()
函数检测到文件结尾时也返回EOF。通常, EOF定义在stdio.h
文件中:为什么是-1?因为
getchar()
函数的返回值通常都介于0~127,这些值对应标准字符集。但是,如果系统能识别扩展字符集,该函数的返回值可能在0~255之间。无论哪种情况,-1都不对应任何字符,所以,该值可用于标记文件结尾。某些系统也许把EOF定义为-1以外的值,但是定义的值一定与输入字符所产生的返回值不同。如果包含
stdio.h
文件,并使用EOF符号,就不必担心EOF值不同的问题。把
getchar()
的返回值和EOF作比较,如果两值不同,就说明没有到达文件结尾:使用该程序进行键盘输入,要设法输入EOF字符。
在大多数UNIX和Linux系统中,在一行开始处按下Ctrl+D会传输文件结尾信号。许多微型计算机系统都把一行开始处的Ctrl+Z识别为文件结尾信号,一些系统把任意位置的Ctrl+Z解释成文件结尾信号。
每次按下Enter键,系统便会处理缓冲区中储存的字符,并在下一行打印该输入行的副本。这个过程一直持续到以UNIX风格模拟文件结尾(按下Ctrl+D)。在PC中,要按下Ctrl+Z。
重定向
在默认情况下,C程序使用标准I/O包查找标准输入作为输入源。就是
stdin
流,它是把数据读入计算机的常用方式。它可以是一个过时的设备,如磁带、穿孔卡或电传打印机,或者是键盘,甚至是一些先进技术,如语音输入。然而,现代计算机非常灵活,可以让它到别处查找输入。尤其是,可以让一个程序从文件中查找输入,而不是从键盘。程序可以通过两种方式使用文件。第 1 种方法是,显式使用特定的函数打开文件、关闭文件、读取文件、写入文件,诸如此类。第2种方法是,设计能与键盘和屏幕互动的程序,通过不同的渠道重定向输入至文件和从文件输出。换言之,把
stdin
流重新赋给文件。继续使用getchar()
函数从输入流中获取数据,但它并不关心从流的什么位置获取数据。虽然这种重定向的方法在某些方面有些限制,但是用起来比较简单。重定向的一个主要问题与操作系统有关,与C无关。UNIX(运行命令行模式时)、Linux(ditto)和Window命令行都能重定向输入、输出。
重定向输入让程序使用文件而不是键盘来输入,重定向输出让程序输出至文件而不是屏幕。
重定向输入
假设已经编译了
test.c
程序,并把可执行版本放入一个名为test(在Windows系统中名test.exe)的文件中。运行该程序,输入可执行文件名:该程序获取用户从键盘输入的输入。
现在,假设要用该程序处理名为words的文本文件,其中储存的数据是可识别的字符。由于该程序的操作对象是字符,所以要使用文本文件。只需用下面的命令代替上面的命令即可:
<
符号是UNIX和DOS/Windows的重定向运算符。该运算符使文件与stdin流相关联,把文件中的内容导入程序。程序本身并不知道(或不关心)输入的内容是来自文件还是键盘,它只知道这是需要导入的字符流,所以它读取这些内容并把字符逐个打印在屏幕上,直至读到文件结尾。因为C把文件和I/O设备放在一个层面,所以文件就是现在的I/O设备。重定向输出
现在假设要用teset把键盘输入的内容发送到名为
mywords
的文件中。然后,输入以下命令并开始输入:>
符号是第2个重定向运算符。它创建了一个名为mywords的新文件,然后把test的输出(即你输入字符的副本)重定向至该文件中。重定向把stdout从显示设备(即,显示器)赋给mywords文件。如果已经有一个名为mywords的文件,通常会擦除该文件的内容,然后替换新的内容(但是,许多操作系统有保护现有文件的选项,使其成为只读文件)。所有出现在屏幕的字母都是刚才输入的,其副本储存在文件中。在下一行的开始处按下Ctrl+D(UNIX)或Ctrl+Z(DOS)即可结束该程序。记住在每行的末尾单击Enter键,这样才能把缓冲区的内容发送给程序。组合重定向
现在,假设希望制作一份mywords文件的副本,并命名为savewords。只需输入以下命令即可:
下面的命令也起作用,因为命令与重定向运算符的顺序无关:
注意:在一条命令中,输入文件名和输出文件名不能相同。
重定向运算符连接一个可执行程序(包括标准操作系统命令)和一个数据文件,不能用于连接一个数据文件和另一个数据文件,也不能用于连接一个程序和另一个程序。
使用重定向运算符不能读取多个文件的输入,也不能把输出定向至多个文件。
通常,文件名和运算符之间的空格不是必须的,除非是偶尔在UNIX shell、Linux shell或Windows命令行提示模式中使用的有特殊含义的字符。例如
test<words
。UNIX、Linux或Windows/DOS 还有
>>
运算符,该运算符可以把数据添加到现有文件的末尾,而 | 运算符能把一个文件的输出连接到另一个文件的输入。重定向是一个命令行概念,在命令行输入特殊的符号发出指令。如果用不了重定向,可以用程序直接打开文件。