【C语法学习】1 - 关于流

0 前言

在C语言中,程序对所有外部设备(包括键盘、屏幕和磁盘文件等)进行的输入和输出操作都是基于流的。

1 流的概念

cstdio库描述如下:

1. The cstdio library uses what are called streams to operate with physical devices such as keyboards, printers, terminals or with any other type of files supported by the system.
2. Streams are an abstraction to interact with these in an uniform way; All streams have similar properties independently of the individual characteristics of the physical media they are associated with.
3. Streams are handled in the cstdio library as pointers to FILE objects. A pointer to a FILE object uniquely identifies a stream, and is used as a parameter in the operations involving that stream.
4. There also exist three standard streams: stdin, stdout and stderr, which are automatically created and opened for all programs using the library.

流是一个抽象的概念,对初学者来说很难理解。

1.1 流的比喻

想象一下,你手中拿着一根水管,水管的一端连接着水源(比如一个水塔),另一端连接到需要用水的地方(比如一个花园)。当你打开水管的阀门时,水就会沿着水管连续不断地流动,从水塔流向花园。在这个比喻中,水管就是“流”,而水就是数据。

简而言之,流就是数据在源和目的地之间传输的通道。

1.2 流的抽象

在C语言中,程序需要经常读写外部设备。每种外部设备的读写方式都有所差异,如写文件和写屏幕、读文件和读键盘等。如果程序员在写代码时需要理解每种外部设备的读写方式,将会大大增加编程的复杂性。而流的概念作为一层抽象,使得程序员在编写代码时无需关注外部设备的具体细节,只需将数据写入流中或从流中读取数据即可。

如此,流提供了一种统一的数据读写方式,使得无论数据来源于何种外部设备,都可以通过流的方式进行读取和写入。这大大简化了程序员的工作,因为他们不需要为每种外部设备编写特定的读写代码。

简单来说,流使得程序员以一种统一的方式和外部设备进行交互。

1.3 流的分类

标准流:标准流用于处理标准输入输出的读写。C语言提供了三个预定义的标准流:stdin(标准输入流,通常对应键盘输入)、stdout(标准输出流,通常对应屏幕输出)和stderr(标准错误输出流,通常对应屏幕输出,用于显示错误信息)。标准流在程序启动时自动打开,在程序结束时自动关闭。

文件流:文件流用于处理磁盘文件的读写。文件流通过fopen()函数打开,通过fclose()函数关闭或程序结束时自动关闭。

1.4 流的特点

顺序性:流中的数据是顺序传送的,即先发送的数据先到达,后发送的数据后到达。
连续性:流中的数据是连续不断的,除非显式地关闭流或遇到文件结束(EOF)等特殊情况。
方向性:流可以是单向的,即只能读(输入)或只能写(输出),如以只读或只写模式打开的文件;也可以是双向的,即可读(输入)可写(输出),如以更新模式打开的文件。
缓冲区:为了提高读写效率,流操作通常是基于缓冲的,数据先被存储在缓冲区中,当缓冲区满或满足其他条件时,再一次性进行数据的读写操作。

2 流的属性

cstdio库描述如下:

1. Streams have some properties that define which functions can be used on them and how these will treat the data input or output through them. 
2. Most of these properties are defined at the moment the stream is associated with a file (opened) using the fopen function.

2.1 Read/Write Access

cstdio库描述如下:

1. Specifies whether the stream has read or write access (or both) to the physical media they are associated with.

流具有读写属性,即仅可读、仅可写或可读写。

2.2 Text / Binary

cstdio库描述如下:

1. Text streams are thought to represent a set of text lines, each one ending with a new-line character. 
2. Depending on the environment where the application is run, some character translation may occur with text streams to adapt some special characters to the text file specifications of the environment. 
3. A binary stream, on the other hand, is a sequence of characters written or read from the physical media with no translation, having a one-to-one correspondence with the characters read or written to the stream.

如前所述,流可分为标准流和文件流。文件流可进一步分为文本流和二进制流,两者区别如下:

文本流
文本流主要用于处理文本数据,即字符数据。在文本流中,数据以字符的形式进行读写,并且会根据操作系统的不同,对特定的字符进行转换,主要是换行符的转换。

  1. 换行符转换:在Windows系统中,文本文件通常使用\r\n(回车加换行)作为行结束符;而在Unix/Linux系统中,只使用\n(换行)作为行结束符。当使用文本模式打开文件时,系统会负责这些换行符的转换,确保数据在不同的系统上能正确地以文本形式表示。
  2. 字符编码:文本流还涉及到字符编码的问题,尤其是在处理非ASCII字符时。不同的系统和应用可能会使用不同的字符编码(如UTF-8, GBK等),但文本流通常不会直接处理这些编码转换的细节,而是依赖于外部机制或库函数。

二进制流
二进制流用于处理任何类型的数据,包括文本数据、图像、视频等。在二进制流中,数据以字节(byte)为单位进行读写,不进行任何形式的转换或解释。

  1. 数据完整性:由于二进制流不对数据进行任何转换,因此它能够确保数据的完整性。这对于需要精确控制数据格式和结构的场景(如文件传输、图像处理等)尤为重要。
  2. 通用性:二进制流不依赖于特定的字符编码或换行符约定,因此它可以在不同的系统和平台之间无缝传输和处理数据。

区别总结

  1. 数据处理方式:文本流以字符为单位处理数据,可能会进行换行符的转换;二进制流以字节为单位处理数据,不进行任何形式的转换。
  2. 数据完整性:二进制流能够确保数据的完整性,而文本流在处理特定字符(如换行符)时可能会改变数据的原始表示。
  3. 应用场景:文本流适用于处理文本数据,特别是需要跨平台保持文本格式一致性的场景;二进制流适用于处理任何类型的数据,特别是需要精确控制数据格式和结构的场景。

2.3 Buffer

stdio库描述如下:

1. A buffer is a block of memory where data is accumulated before being physically read or written to the associated file or device. 
2. Streams can be either fully buffered, line buffered or unbuffered. 
3. On fully buffered streams, data is read/written when the buffer is filled.
4. On line buffered streams this happens when a new-line character is encountered.
5. On unbuffered streams characters are intended to be read/written as soon as possible.

缓冲机制是为了提高读写操作的效率而设计的,它允许数据在传输到最终目的地之前,在内存中暂存。这样,程序可以批量地处理数据,而不是每次读写操作都直接作用于最终目的地,从而减少了系统调用的次数,提高了性能。

2.3.1 缓冲区的分类

C语言标准库中的流可以具有三种类型的缓冲:

全缓冲(Fully Buffered)

  1. 全缓冲通常用于文件流;
  2. 写文件,程序首先将数据写入输出缓冲区中,当缓冲区被写满、被显示刷新(fflush()函数)或被隐式刷新(fclose()函数或程序结束)时,数据才会被写入文件;
  3. 读文件,当输入缓冲区为空或剩余数据不足以满足读请求时,会自动触发底层的系统调用从文件中读取数据到缓冲区并填满缓冲区,然后程序从缓冲区中读取数据。

行缓冲(Line Buffered)

  1. 行缓冲通常用于标准输入流stdin和标准输出流stdout;
  2. 对于标准输出流stdout,程序首先将数据写入输出缓冲区中,当缓冲区满、遇到换行符(\n)、被显示刷新(fflush()函数)或被隐式刷新(程序结束)时,数据就会输出到屏幕上;
  3. 对于标准输入流stdin,用户从键盘输入数据首先缓存在输入缓冲区中,当缓冲区满或遇到换行符(\n)时,程序从缓冲区中读取数据。

无缓冲(Unbuffered)

  1. 无缓冲通常用于标准错误流stderr;
  2. 数据会被立即写入或读取,不使用缓冲区。

2.3.2 缓冲区的操作

刷新(Flushing)

  1. 刷新缓冲区是指将输出缓冲区中的数据强制写入最终目的地;
  2. 刷新缓冲区的方式分为显示刷新和隐式刷新:
    (1)显示刷新是通过fflush()函数来刷新;
    (2)隐式刷新是缓冲区满、fclose()函数关闭文件或程序结束来刷新。

清空(Emptying)

  1. 如前所述,读文件时,如果输入缓冲区为空,首先会从文件中读取数据将缓冲区填满,然后程序开始从缓冲区中读取所需数据;当程序读取完所需数据后,缓冲区中总会有数据残留,此时需要清空缓冲区;
  2. 清空缓冲区是指将输入缓冲区中未被程序读取的数据丢弃掉;
  3. 清空缓冲区的方式:
    (1)显式清空:C语言标准库并未提供显式清空缓冲区的函数;
    (2)隐式清空:fclose()函数关闭文件或程序结束来隐式清空。

2.4 Orientation

stdio库描述如下:

1. On opening, streams have no orientation. 
2. As soon as an input/output operation is performed on them, they become either byte-oriented or wide-oriented, depending on the operation performed.
3. Generally, functions defined in <cstdio> are byte-oriented, while functions in <cwchar> are wide-oriented.

3 流的指示符

stdio库描述如下:

1. Streams have certain internal indicators that specify their current state and which affect the behavior of some input and output operations performed on them.

流有三种指示符,分别是:位置指示符、文件结尾指示符和错误指示符。

3.1 Error indicator

1. This indicator is set when an error has occurred in an operation related to the stream. 
2. This indicator can be checked with the ferror function, and can be reset by calling either to clearerr, freopen or rewind.

对流进行读写操作时发生的错误时,设置错误指示符,用ferror()函数查看。

3.2 End-Of-File indicator

1. When set, indicates that the last reading or writing operation performed with the stream reached the End of File. 
2. It can be checked with the feof function, and can be reset by calling either to clearerr or freopen or by calling to any repositioning function (rewind, fseek and fsetpos).

当位置指示符指在文件末尾,且对文件末尾发起了读写操作后,才会设置文件末尾指示符,使用feof()函数查看。

3.3 Position indicator

stdio库描述如下:

1. It is an internal pointer of each stream which points to the next character to be read or written in the next I/O operation. 
2. Its value can be obtained by the ftell and fgetpos functions, and can be changed using the repositioning functions rewind, fseek and fsetpos.

当使用fread()、fwrite()、fputc()、fgetc()、fprintf()和fscanf()等函数对文件进行读写操作时,文件的位置指示符会自动更新以反映下一次读写操作应该开始的位置。

  1. 对于fread()和fwrite()函数,位置指示符会根据读取或写入的字节数进行更新。
  2. 对于fputc()和fgetc()函数,位置指示符会在每次调用时向前移动一个字符的位置。
  3. 对于fprintf()和fscanf()函数,位置指示符会根据读取或写入的格式项来更新。

另外,C语言还提供rewind()和fseek()函数来手动设置文件的位置指示符,提供ftell()函数来获取当前文件位置指示符的值(即当前读写位置相对于文件开头的偏移量)。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值