C 语言的输入输出流以及数据缓冲
1. 数据流与缓冲区的概念
1.1数据流
- 数据流的意义
数据流是一串连续不断的集合,就像水管里的水流,在水管的一端一点一点的地供水,而在水管的另一端看到的是一股连续不断的水流。
对数据写入程序是一段、一段地向数据流管道写入数据,这些数据会按先后顺序被整合形成一个长的数据流。
对数据读取程序而言,看不到数据流在写入时的分段情况。
每次读取都可以读取**任意长度**的数据,但只能先读取前面的数据后,再读取后面的数据,不管写入时是将数据分多次写入,读取时的效果都是完全一样的。
- 数据流的特点
在Unix/Linux中,文本流和二进制流是相同的,但在Windows中,稍有差异,所以C标准库提供了这两种类型的流。
文本流是由文本行组成的序列,每一行包含0个或多个字符,并以'\n'结尾。在某些环境中, 可能需要将文本流转换为其它表示形式(例如把'\n'映射成回车符和换行符),或从其它表示形式转换为文本流。
二进制流是由未经处理的字节构成的序列,这些字节记录着内部数据, 并具有下列性质:如果在同一系统中写入二进制流,然后再读取该二进制流,则读出和写入 的内容完全相同。
- 数据流的映射
在操作系统中,为了统一对各种硬件的操作,简化接口,不同的硬件设备都被映射成一个文件。对这些文件的操作,等同于对磁盘上普通文件的操作。
文件 | 硬件设备 |
---|---|
stdin | 标准输入设备(键盘):如 scanf()、getchar() 等函数从 stdin 获取输入。 |
stdout | 标准输出设备(显示器);printf()、putchar() 等函数向 stdout 输出数据。 |
stderr | 标准错误输出设备(显示器);perror() 等函数向 stderr 输出数据。 |
stdprn | 标准打印设备(打印机) |
stdaux | 标准辅助输入输出设备(异步串行口)。 |
程序开始执行时,默认会打开 stdin、stdout和stderr三个文件,所以我们使用 scanf()、printf() 等函数时就不需要再使用 fopen() 显式打开这些文件。
- C语言数据流的映射过程
- 编译时打开一个流。系统将会把该流与一个文件或设备连接起来,如我要打开一个zyx.txt或从键盘输入一段数据,这时候会将该stdin流与该文件或键盘连接起来。其中在打开文件的时候,先将文件载入缓冲区,并返回一个指向FILE结构体类型的指针,该指针记录了所有控制该流的所有必要信息。
- 接下来对这个文件zyx.txt的所有操作,将会映射成对缓冲区的操作,如我要修改文件中的一个字,只要修改该缓冲区的一个字。
- 只有当强制刷新缓冲区、关闭文件或程序运行结束时,才将缓冲区的内容更新到文件中。
- 采用数据流的目的
采用数据流的目的就是使得输出输入独立于设备。
Input Stream不关心数据源来自何种设备(键盘,文件,网络);Output Stream不关心数据的目的是何种设备(键盘,文件,网络)。
1.2 缓冲区
- 缓冲区的意义
>缓冲区又称为缓存,它是内存空间的一部分,用来存储输入和要输出的数据,可以加速对数据的操作。
- 缓冲区的分类
缓冲区 分为三种类型:全缓冲、行缓冲和不带缓冲。
全缓冲
在这种情况下,当填满标准I/O缓存后才进行实际I/O操作。全缓冲的典型代表是对磁盘文件的读写。
行缓冲
在这种情况下,当在输入和输出中遇到换行符时,执行真正的I/O操作。这时,我们输入的字符先存放在缓冲区,等按下回车键换行时才进行实际的I/O操作。典型代表是标准输入(stdin)和标准输出(stdout)。
不带缓冲
也就是不进行缓冲,标准出错情况stderr是典型代表,这使得出错信息可以直接尽快地显示出来。
- 缓冲区的特点
ANSI C( C89 )要求缓存具有下列特征:
当且仅当标准输入和标准输出并不涉及交互设备时,它们才是全缓存的。
标准出错决不会是全缓存的。
大部分系统默认使用下列类型的缓存:
标准出错是不带缓存的。
如果是涉及终端设备的流,则它们是行缓存的;
否则是全缓存的。
我们经常要用到标准输入输出流,而ANSI C对stdin、stdout和stderr的缓存特征没有强行的规定,以至于不同的系统可能有不同的stdin、stdout和stderr的缓存特征。
目前主要的缓存特征是:stdin和stdout是行缓存;而stderr是无缓存的。
- 缓冲区的大小
如果我们没有自己设置缓冲区的话,系统会默认为标准输入输出设置一个缓冲区,这个缓冲区的大小通常是512个字节的大小。
缓冲区大小由 stdio.h 头文件中的宏 BUFSIZ 定义,如果希望查看它的大小,包含头文件,直接输出它的值即可,如下所示:
缓冲区的大小是可以改变的,也可以将文件关联到自定义的缓冲区,详情可以查看 setbuf() 函数。 - 缓冲区的刷新
以下情况会引发缓冲区的刷新- 缓冲区满时;
- 行缓冲区遇到回车时;
- 关闭文件;
- 使用特定函数刷新缓冲区。
2. 几个常用输入输出函数
- scanf() and printf()
scanf() 函数用于从标准输入stdin(键盘)读取并格式化, printf() 函数发送格式化输出到标准输出stdout(屏幕)。
scanf的函数声明如下:
int scanf(const char *format,…)
而参数format是是一个字符指针,简单说就是一个 C 字符串,format 的说明符形式为:
[=%[*][width][modifiers]type=]
详情请参考https://www.runoob.com/cprogramming/c-function-scanf.html
printf的函数声明如下:
int printf(const char *format, …)
format – 这是字符串,包含了要被写入到标准输出 stdout 的文本。它可以包含嵌入的 format 标签,format 标签可被随后的附加参数中指定的值替换,并按需求进行格式化。
format 标签属性是:
%[flags][width][.precision][length]specifier
示例如下:
- getchar() and putchar()
int getchar(void) 函数从屏幕读取下一个可用的字符,并把它返回为一个整数。这个函数在同一个时间内只会读取一个单一的字符。您可以在循环内使用这个方法,以便从屏幕上读取多个字符。
int putchar(int c) 函数把字符输出到屏幕上,并返回相同的字符。这个函数在同一个时间内只会输出一个单一的字符。您可以在循环内使用这个方法,以便在屏幕上输出多个字符。
示例如下:
#include <stdio.h>
int main() {
// int c[10];
// 报错:c = getchar();incompatible types when assigning to type 'int[10]' from type 'int'
// 也就是:从int到int[10]是不兼容的类型;
// char *c;
// 警告:c = getchar();从int到指针的类型是没有强制转换的
// 可以输出,getchar()返回的整数被赋值给了指针(指向的地址),而指针直接把地址作为参数,让putchar以此为输出,
// 由于中间没有经过类型转换,或其他中间步骤,所以可以直接输出字符。
// 虽然逻辑上没有问题,但是这样有可能造成内存访问违规。
// 而无论是int c;还是char c;都没有问题,因为中间的强制类型转换是按照ASCII码对照表来转换,所以不会出现问题。
char c;
printf("pls input hello:\n");
c = getchar();/*getchar()经历了先将c强制类型转换为整形,又由于“=”强制转换为char类型*/
printf("ASCII码Number:%d ->", c);
printf("转换成字符:%c\n", c);
putchar(c);
return 0;
}
运行结果如下:
当程序调用getchat()时,程序等待用户通过键盘非回车键输入值,当用户按下回车键,这些输入的值包括最后输入的回车‘\n’将全部存放在缓冲区,第一个字符作为函数返回值返回。
如果这时候继续调用getchar()函数,那么剩余的在缓冲区中未经过返回的值,将会把第二个返回,以此类推,直到调用getchar()将剩余值全部都输出。而这时候需要一个终止量来终止getchar()的内容,在值的最后是一个回车符号‘\n’,可以通过它来判断。
#include <stdio.h>
int main() {
char s;
s = getchar();
printf("%c\n", s);
system("PAUSE");
while((s = getchar())!= '\n')
{
printf("%c\n", s);
}
system("PAUSE");
return 0;
}
注意上面的判断语句中\n是用单引号。
在C语言中单双引号是不同的意义,单引号修饰单个字符,双引号修饰的是字符串。而在C语言的字符串中,默认会在字符串的最后加上’\0’,而这就意味着字符串"\n"里面实际上是(转义字符修饰的可以算作一个字符)字符’\n’的后面还有一个字符’\0’,也就是字符串"\n\0" ,而自然’\n’!="\n\0","\n"也就无法实现终止了。
用单引号的结果,有终止,如下所示:
用双引号,无终止,如下所示:
- gets() and puts()
gets()函数声明如下:
char *gets(char *s)
gets函数从stdin中读取一行到s所指向的缓冲区,知道一个终止符或EOF。
puts()函数声明如下:
int puts(const char *s)
puts函数把字符串s和一个尾随的换行符写入到stdout。
示例如下:
#include <stdio.h>
int main() {
char str[100];
printf("请你输入:\n");
gets(str);
printf("你的输入是:");
puts(str);
return 0;
}
编译运行结果为:
参考文档1https://blog.csdn.net/weixin_41632560/article/details/82141591
参考文档2http://c.biancheng.net/cpp/html/2415.html
参考文档3https://www.jianshu.com/p/2a4a4056eae7
推荐阅读
-
C 语言的输入输出流以及数据缓冲
-
I / O之带缓冲的输入输出流及数据输入输出流
-
php读取二进制流(C语言结构体struct数据文件)的深入解析
-
c语言实现系统(Linux)文件权限的修改,以及系统文件的创建,写入和读取数据
-
王道数据结构:栈顺序存储和链式存储以及基本操作的实现(C语言版)
-
php读取二进制流(C语言结构体struct数据文件)的深入解析
-
php读取二进制流(C语言结构体struct数据文件)的深入解析_PHP教程
-
php读取二进制流(C语言结构体struct数据文件)的深入解析
-
php读取二进制流(C语言结构体struct数据文件)的深入解析_PHP教程
-
php读取二进制流(C语言结构体struct数据文件)的深入解析