目录
1. 单字符I/O:getchar() 和 putchar()
char c;
while((c = getchar()) != '#')
{
putchar(c);
}
2. 缓冲区
- 回显用户输入的字符后立即重复打印该字符是属于无缓冲(或直接)输入,即正在等待的程序可立即使用输入的字符
- 在按下 ENTER 键之前不会重复打印刚输入的字符,这种输入形式属于缓冲输入
- 用户输入的字符被收集并存储在一个被称为缓冲区(buffer)的临时存储区,按下 ENTER 键后,程序才可使用用户输入的字符
- 使用缓冲区的原因
- 把若干字符作为一个块进行传输比逐个发送这些字符节约时间
- 如果用户打错字符,可以直接通过键盘修正错误;当按下 ENTER 键时,传输的是正确的输入
- 缓冲分为两类
- 完全缓冲I/O:
- 当缓冲区被填满时才刷新缓冲区(内容被发送至目的地),通常出现在文件输入中
- 常见大小是512和4096字节
- 行缓冲I/O:
- 在出现换行符时刷新缓冲区
- 键盘输入通常是行缓冲输入,所以按下 ENTER 键后才刷新缓冲区
- 完全缓冲I/O:
3. 结束键盘输入
3.1 文件、流和键盘输入
-
**文件(file)**是存储器中存储信息的区域
-
通常文件都保存在某种永久存储器中(硬盘、U盘或DVD等)
-
C有许多用于打开、读取、写入和关闭文件的库函数
- 从较低层面上,C可以使用主机操作系统的基本文件工具直接处理文件,这些直接调用操作系统的函数被称为底层I/O(low-level I/O)
- 从较高层面上,C还可以通过**标准I/O包(standard I/O package)**来处理文件;这涉及创建用于处理文件的标准模型和一套标准I/O函数;在这一层面上,具体的C实现负责处理不同系统的差异(不同的系统存储文件的方式不同),以便用户使用统一的界面
-
可以用 if (c == ‘\n’) 检查换行符,即使系统实际用的是回车和换行符的组合来标记行末尾,I/O函数会在这两种表示方法之间相互转换
-
从概念上看,C程序处理的是流(stream)而不是直接处理文件
-
流是一个实际输入或输出映射的理想化数据流;这意味着不同属性和不同种类的输入,由属性更统一的流来表示;于是,打开文件的过程就是把流与文件相关联,而且读写都通过流来完成
3.2 文件结尾
-
计算机操作系统要以某种方式判断文件的开始和结束
-
检测文件结尾的方法:
- 在文件末尾放一个特殊的字符标记文件结尾,CTRL + Z 会被视为一个文件结尾标记
- 存储文件大小的信息,用这种方法可以在文件中存储所有的字符,包括 CTRL + Z
-
在C语言中,用 getchar() 读取文件检测到文件结尾时将返回一个特殊的值,即 EOF(end of file)
-
scanf() 函数检测到文件结尾时也返回 EOF
-
通常,EOF 定义在 stdio.h 文件中
#define EOF (-1)
- 定义为 -1 的原因:getchar() 函数的返回值通常都介于 0~127 ,这些值对应标准字符集;如果系统能识别扩展字符集,该函数的返回值可能为 0~255;无论哪种情况,-1 都不对应任何字符,所以,该值可以用于标记文件结尾
-
-
在大多数 UNIX 和 Linux 系统中,在一行开始处按下 CTRL + D 会传输文件结尾信号
-
其他系统中,在一行开始处或任意处按下 CTRL + Z 会传输文件结尾信号
4. 重定向和文件
- 程序可以通过两种方式使用文件
- 显式使用特定的函数打开文件、关闭文件、读取文件、写入文件
- 设计能与键盘和屏幕互动的程序,通过不同的渠道重定向输入至文件和从文件输出
- 把 stdin 流重新赋给文件
- 继续使用 getchar() 函数从输入流中获取数据,但是不关心从流的什么位置获取数据
4.1 UNIX、Linux 和 DOS 重定向
- 重定向输入让程序使用文件而不是键盘来输入
- 重定向输出让程序输出至文件而不是屏幕
4.1.1 重定向输入
-
文本文件(text file)是内含文本的文件,其中存储的数据是我们可识别的字符
-
文件内容可以是文本或者C程序
-
内含机器语言指令的文件(存储可执行程序的文件)不是文本文件
./ABC < words // 名为 ABC.exe 的可执行文件 // 名为 words 的文本文件
-
-
符号 < 是 UNIX 和 DOS / Windows 的重定向运算符
- 该运算符使文件与 stdin 流相关联,把文件中的内容导入程序
- 程序本身并不知道(不关心)输入的内容是来自文件还是键盘,它只直到这是需要导入的字符流,所以它读取这些内容并把字符逐个打印在屏幕上,直至读到文件结尾
4.1.2 重定向输出
./ABC > words
// 名为 ABC.exe 的可执行文件
// 名为 words 的文本文件
- 符号 > 也是重定向运算符
- 创建新的文本文件,然后把程序的输出重定向至该文件中
- 重定向把 stdout 流从显示设备赋给文本文件
- 如果已经有同名文件,通常会擦除该文件的内容,然后替换新的内容
4.1.3 组合重定向
./ABC < words > new_words
// 等效于
./ABC > new_words < words
- 通过程序将文本文件转存到另一个文本文件中
- 组合重定向的原则:
- 重定向运算符连接一个可执行程序(包括标准操作系统命令)和一个数据文件,不能用于连接一个数据文件和另一个数据文件,也不能用于连接一个程序和另一个程序
- 使用重定向u运算符不能读取多个文件的输入,也不能把输出定向至多个文件
- 通常,文件名和运算符之间的空格不是必须的,除非是偶尔在命令行提示模式中使用的有特殊含义的字符
5. 创建更友好的用户界面
5.1 使用缓冲输入
int i = 0;
printf("i = %d\n", i);
while(getchar() != 'y')
{
printf("i = %d\n", ++i);
}
// 优化
int i = 0;
printf("i = %d\n", i);
while (getchar() != 'y')
{
printf("i = %d\n", ++i);
while (getchar() != '\n')
{
continue;
}
}
// 优化
int i = 0;
char c = ' ';
printf("i = %d\n", i);
while ((c = getchar()) != 'y')
{
if (c == 'n')
{
printf("i = %d\n", ++i);
}else
{
printf("Error\n");
}
while (getchar() != '\n')
{
continue;
}
}
5.2 混合数值和字符输入
- getchar() 和 scanf() 都可以处理输入,但是不能混用
- getchar() :读取每个字符,包括空格、制表符和换行符
- scanf() :在读取时会跳过空格、制表符和换行符
int length = 0;
int width = 0;
char c = ' ';
while ((c = getchar()) != EOF)
{
if (c == '\n')
{
continue;
}
scanf("%d %d", &length, &width);
for (int i = 1; i <= length; i++)
{
for (int j = 1; j <= width; j++)
{
putchar(c);
}
putchar('\n');
}
}