1.缓冲区
(1)无缓冲输入与缓冲输入:
1.无缓冲输入:如果正在等待的程序可以立即使用输入的字符,称为无缓冲输入(或直接输入)
2.缓冲输入:如果输入的字符先被收集并存储在1个称为缓冲区(Buffer)的临时存储区
按下Enter键后正在等待的程序才能使用输入的字符,称为缓冲输入
如以下程序:
#include <stdio.h>
int main(void) {
char ch;
while ((ch=getchar())!='#') {
putchar(ch);
}
return 0;
}
//结果:
Hello,I would[Enter]
Hello,I would
like a#in the[Enter]
like a
//在部分老式系统中会是:
HHeelllloo,,II wwoouulldd[Enter]
lliikkee aa#
(2)缓冲输入的优点:
缓冲输入的优点有:
①把若干个字符作为1个块进行传输比逐个发送这些字符节约时间
②如果输入错误,在按下Enter键之前都可以直接用键盘进行修改
不过,有些时候也需要无缓冲输入,如在游戏中,按下1个键就需要执行相应的命令
(3)完全缓冲与行缓冲:
缓冲分为完全缓冲和行缓冲:
完全缓冲是指在缓冲区被填满时刷新缓冲区并发送内容至目的地,通常出现在文件输入中
缓冲区的大小取决于系统,通常是512B或4096B
行缓冲是指在出现换行符时(通常是按下Enter键时)刷新缓冲区并发送内容至目的地
(4)如何选择:
ANSI C及后续的C标准均规定输入是缓冲的,但K&R把决定权留给了编译器的编写者
ANSI C决定使用缓冲输入的原因是:一些计算机不允许无缓冲输入
不过,有些系统为无缓冲输入提供了一系列特殊的函数,但C标准没有该规定
getchar()使用的是行缓冲输入
2.结束键盘输入
(1)文件,流,键盘输入:
文件(file)是存储器中存储信息的区域;通常,文件都保存在某种永久存储器中.文件对计算机系统
很重要,如程序就保存在文件中
C有许多有关文件的库函数;从底层来说,C可以使用OS的基本文件工具直接处理文件,这些直接调用
操作系统的函数称为底层I/0(Low-Level I/O).由于OS不同,无法为普通的底层I/O函数创建标准
库,ANSI C也不打算这样做.但从较高层面上,C可以通过标准IO包(Standard I/O Package)来处
理文件.这涉及创建用于处理文件的标准模型和1套标准I/O函数.在这一层面上,具体的C实现负责
处理不同系统的差异,以便用户使用统一的界面
不同系统在处理文件时存在差异.如不同系统存储文件的方式不同,有些把文件内容存储在一处,而
文件相关的信息存储在另一处;有些在文件中创建1份文件描述.在处理文件方面,有些使用单个换
行符标记行末尾,有些使用回车符和换行符的组合来表示行末尾.在衡量文件大小上,有些使用最小
字节,有些使用字节块
如果使用标准I/O包,就不用考虑这些差异,因此,可以用if(ch=='n')检查换行符.即使系统实际用的是回车符和换行符的组合来标记行末尾,I/O函数会在2种表示法之间相互转换
从概念上看,C不是直接处理文件而是处理流(Stream).流是1个理想化数据流,实际的输入/输出映
射到流.这意味着不同属性/种类的输入,由属性更统一的流来表示.打开文件的过程就是把流与文
件相关联的过程,且读写都通过流来完成,而不用考虑具体的属性/种类(注:流是OS提供的接口)
stdin流表示输入的内容,stdout流表示输出的内容.getchar()/putchar()/printf()/scanf()
都是标准I/O包的成员,用于处理这2个流.输入通常是从键盘,输出通常是到屏幕
以上内容表明,可以用处理文件的方式来处理输入的内容.如可以用C的输入函数中内置的文件结尾
检测器来结束输入
(2)文件结尾:
OS需要以某种方式判断文件的开始和结束.旧的方法是在文件结尾放1个特殊字符作为标记
之后,OS使用内嵌的Ctrl+Z来标记文件结尾,这曾经是OS使用的唯一标记
不过现在有一些其他选择,如记录文件大小,所以现在的文本文件不一定有嵌入的Ctrl+Z
但是如果有,OS会将其视为1个文件结尾标记(见下图)
OS标记文件结尾的另1种方法是记录文件大小.如文件有3000B,程序读到3000B时便到达文件结尾
使用这种方法就可以在文件中存储任何字符,包括Ctrl+Z
无论OS使用哪种方法检测文件结尾,在C语言中,getchar()/scanf()检测到文件结尾时对会返回1个殊
值EOF(End of File).通常EOF在stdio.h中被定义为-1:
#define EOF -1
使用-1是因为getchar()的返回值为0~127(ASCII码)/0~255(扩展字符集),-1不对应任何字符
另外,绝大部分系统都可以通过键盘模拟文件结尾条件,因而无需特别处理
int ch;//或char ch;也可以,但部编译器可能报错,因为getchar()返回int类型
while ((ch=getchar())!=EOF) {//EOF定义在stdio.h中
putchar(ch);
}
//输入Ctrl+Z后终止(注意:①不是输入EOF或-1 ②不同系统需要输入的终止命令不同)
3.重定向和文件
在默认情况下,C程序使用标准I/O包查找stdin作为输入源,但也允许从其他地方(如文件)查找输入
使用文件:
1.使用特定函数对文件进行操作
2.设计能与键盘和屏幕互动的程序,通过不同渠道重定向输入到文件或从文件输入,即把stdin/
stdout赋给文件,继续使用getchar()/putchar()通过流操作数据
重定向的使用有一些限制,但用起来比较简单;此外,重定向和OS有关,与C无关
UNIX/Linux/Windows都有重定向特性,一些C实现还在缺乏该特性的系统中模拟该特性
//以下使用的echo_eof.c中的代码为:
#include <stdio.h>
int main(void) {
fflush(stdin);
int ch;
while ((ch=getchar())!=EOF) {
putchar(ch);
}
return 0;
}
(1)重定向输入:
使用(输入)重定向运算符(<)进行输入的重定向:
<programm_file> < <source_file>
#参数说明:
programm_file:要执行的程序(路径及文件名)
source_file:作为输入设备的文件(从该文件输入)
C程序不知道输入的内容来自哪里,只知道这是要导入的字符流
C把文件和I/O设备放在1个层面,重定向后,文件就相当于程序使用的输入设备
#实例(Linux/UNIX):编译echo_eof.c,生成可执行文件echo_eof并有1个名为words的文本文件
$ ./echo_eof#运行该文件
$ ./echo_eof < words#运行该文件,并将输入重定向至words
We secure,
we conatin,
we protect.
#实例(Windows):可执行文件为echo_eof.exe,文本文件为words.txt
C:\Users\1234>G:\echo_eof.exe < G:\words.txt
We secure,
we conatin,
we protect.
#注意:'>'两侧的空格在Linux/Windows/UNIX中都可选,但在某些系统中不允许有这些空格
(2)重定向输出:
使用(输出)重定向运算符(<)进行输出的重定向:
<programm_file> > <target_file>
#参数说明:
programm_file:要执行的程序(路径及文件名)
source_file:作为输出设备的文件(输出的该文件)
#实例(Windows):
C:\Users\1234>G:\echo_eof.exe > G:\words.txt
jdaklaksnlaskxn\n\r\tczx\n\nsasas\r
#注意:\n等不会被识别为换行符等,[Enter]才是换行
^Z#注意:需在某行开始处按Ctrl+Z才会结束,如果该行前面有字符,不会结束
#实例(Linux/UNIX):
$ ./echo_eof > G:words
jdaklaksnlaskxn\n\r\tczx\n\nsasas\r
^D
(3)组合重定向:
2个重定向运算符可以同时使用,如:
C:\Users\1234>G:\echo_eof.exe > G:\words.txt < G:\as.txt
也可以写为(因为命令与运算符的顺序无关):
C:\Users\1234>G:\echo_eof.exe < G:\as.txt > G:\words.txt
#注意:
1.在1条命令中,输入文件和输出文件不能相同,如下述命令错误:
C:\Users\1234>G:\echo_eof.exe < G:\words.txt > G:\words.txt
因为重定向输出会将输出文件中原有的内容清空,这样从该文件输入前该文件就已被清空
故该命令只会清空words.txt,而不执行其他操作
2.使用重定向符不能从多个文件输入,也不能输出到多个文件
3.用重定向运算符连接的2个文件必须1个是程序文件,另1个是数据文件
(4)通过函数重定向:
//这几个函数都在stdio.h里
打开指定文件:<fp>=fopen("<fname>","<mode>");
从文件中读取字符:<ch>=getc(<fp>);
关闭文件:fclose(<fp>);
//详情参见 C语言细节.文件的输入与输出.二 部分
//参数说明:
fname:文件路径及文件名
mode:打开模式;可为r/w/a/~b/~+
fp:返回文件内容;如果打开失败,为NULL
4.混合使用scanf()和getchar():
//二者均从stdin中获取数据
#include <stdio.h>
void display(char cr,int lines,int width);
int main(void) {
fflush(stdin);
int ch,rows,cols;
printf("enter a character and two integers,use blank to separate:\n");
while ((ch=getchar())!='\n') {
if (scanf("%d %d",&rows,&cols)!=2) {
break;
}
display(ch,rows,cols);
while (getchar()!='\n') {
continue;
}
printf("enter character and integer again,or enter a newline to quit\n");
}
printf("Bye");
return 0;
}
void display(char cr,int lines,int width) {
int row,col;
for (row=1;row<=lines;row++) {
for (col=1;col<=width;col++) {
putchar(cr);
}
putchar('\n');
}
}
//结果:
enter a character and two integers,use blank to separate:
d 3 4
dddd
dddd
dddd
enter character and integer again,or enter a newline to quit
s 2 5
sssss
sssss
enter character and integer again,or enter a newline to quit
Bye