欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

C 语言的输入输出流以及数据缓冲

程序员文章站 2024-03-06 17:18:20
...

1. 数据流与缓冲区的概念

C 语言的输入输出流以及数据缓冲

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语言数据流的映射过程
  1. 编译时打开一个流。系统将会把该流与一个文件或设备连接起来,如我要打开一个zyx.txt或从键盘输入一段数据,这时候会将该stdin流与该文件或键盘连接起来。其中在打开文件的时候,先将文件载入缓冲区,并返回一个指向FILE结构体类型的指针,该指针记录了所有控制该流的所有必要信息。
  2. 接下来对这个文件zyx.txt的所有操作,将会映射成对缓冲区的操作,如我要修改文件中的一个字,只要修改该缓冲区的一个字。
  3. 只有当强制刷新缓冲区、关闭文件或程序运行结束时,才将缓冲区的内容更新到文件中。
  • 采用数据流的目的
    采用数据流的目的就是使得输出输入独立于设备。
    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 定义,如果希望查看它的大小,包含头文件,直接输出它的值即可,如下所示:
    C 语言的输入输出流以及数据缓冲
    缓冲区的大小是可以改变的,也可以将文件关联到自定义的缓冲区,详情可以查看 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

示例如下:
C 语言的输入输出流以及数据缓冲

  • 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;
}

运行结果如下:
C 语言的输入输出流以及数据缓冲

当程序调用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"也就无法实现终止了。
用单引号的结果,有终止,如下所示:
C 语言的输入输出流以及数据缓冲
用双引号,无终止,如下所示:
C 语言的输入输出流以及数据缓冲

  • 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;
}

编译运行结果为:
C 语言的输入输出流以及数据缓冲


参考文档1https://blog.csdn.net/weixin_41632560/article/details/82141591
参考文档2http://c.biancheng.net/cpp/html/2415.html
参考文档3https://www.jianshu.com/p/2a4a4056eae7