标准IO

1. 为什么要使用标准IO?

每个操作系统下面,对文件的管理和接口是不一样的!!!

同一个文件,在不同的操作系统下面,我们的操作文件的代码都不一样。

所以,C语言标准委员会,统一文件操作的接口。

标准IO库:主要统一对文件及常用接口的操作

​ "文件" : 普通文件和二进制文件

在标准IO库中,用结构体FILE来描述或表示一个文件,然后在这个结构体中创建了两个缓冲区(内存/缓冲区),分别为读缓冲和写缓冲。

缓存:同步问题

  • 行缓冲:行缓冲缓冲区中的数据达到一行的时候,同步到外设上面去。
  • 全缓冲:缓存区中数据要填满整个缓冲区,才会同步到外设上去
  • 无缓冲:缓冲区有一个字节,就同步到外设上去。

2. 标准IO流(stdin, stdout, stderr

#include <stdio.h> // 在其中定义
#define stdin
#define stdout
#define stderr

有三个文本流是预定义的。这些流在程序启动时隐式打开,且为无取向的。

  1. 标准输入流关联,用于读取传统的输入。程序启动时,当且仅当能确定流不引用交互式设备时该流为完全缓冲。
  2. 标准输出流关联,用于写入传统的输出。程序启动时,当且仅当能确定流不引用交互式设备时该流为完全缓冲。
  3. 标准错误流关联,用于写入诊断输出。程序启动时,该流不为完全缓冲。

这些宏展开成 FILE类型的表达式。

3. 标准IO的函数接口

3.1 打开文件流

#include <stdio.h>

FILE *fopen( const char *filename, const char *mode );

FILE *fopen( const char *restrict filename, const char *restrict mode ); 

errno_t fopen_s( FILE *restrict *restrict streamptr,
                 const char *restrict filename,
                 const char *restrict mode ); //(C11)

参数:

  • filename : 关联到文件系统的文件名
  • mode : 确定访问模型的空终止字符串文件访问模式
  • streamptr : 指向存储函数结果的指针的指针

文件访问标记:

文件访问 模式字符串 含义 解释 若文件已存在的动作 若文件不存在的动作
"r" 打开文件以读取 从头读 打开失败
"w" 创建文件以写入 销毁内容 创建新文件
"a" 后附 后附到文件 写到结尾 创建新文件
"r+" 读扩展 打开文件以读/写 从头读 错误
"w+" 写扩展 创建文件以读/写 销毁内容 创建新文件
"a+" 后附扩展 打开文件以读/写 写到结尾 创建新文件

返回值:

  • 成功 则返回指向新文件流的指针。流为完全缓冲,除非 filename 表示一个交互设备。错误时,返回空指针。

  • 失败 返回NULL

3.2 关闭文件

#include <stdio.h>
int fclose( FILE *stream );

关闭给定的文件流。冲入任何未写入的缓冲数据到 OS 。舍弃任何未读取的缓冲数据。

无论操作是否成功,流都不再关联到文件,且由 setbufsetvbuf分配的缓冲区若存在,则亦被解除关联,并且若使用自动分配则被解分配。

若在 fclose 返回后使用指针 stream 的值则行为未定义。

参数:

  • stream 需要关闭的文件流

返回值:

  • 成功 0
  • 失败 EOF

3.3 直接读写

fread

size_t fread( void *restrict buffer, size_t size, size_t count,
               FILE *restrict stream );

从给定输入流 stream 读取至多 count 个对象到数组 buffer 中,如同以对每个对象调用 size 次 fgetc,并按顺序存储结果到转译为 unsigned char 数组的 buffer 中的相继位置。

参数

  • buffer 指向要读取的数组中首个对象指针
  • size 每个对象的大小
  • count 要读取的对象数
  • stream 读取来源的输入文件流

返回值

  • 成功读取的对象数
  • 若出现错误或文件尾条件,则可能小于 count 。

若 size 或 count 为零,则 fread 返回零且不进行其他动作。

fread 不区别文件尾和错误,而调用者必须用feofferror 鉴别出现者为何。

fwrite

size_t fwrite( const void *restrict buffer, size_t size, size_t count,
                FILE *restrict stream );

从给定数组 buffer 向输出流 stream 写入 count 个对象。各个对象的写入,如同将每个对象解读为 unsigned char 数组,并对每个对象调用 size 次fputc以将那些 unsigned char 按顺序写入 stream 一般来进行。

参数

  • buffer 指向要写入的数组中首个对象指针
  • size 每个对象的大小
  • count 要被写入的对象数
  • stream 指向输出流的指针

返回值

  • 成功写入的对象数
  • 若出现错误则可能小于 count 。

若 size 或 count 为零,则 fwrite 返回零且不进行其他动作。

3.4 无格式的输入输出

函数名 功能
fgetc / getc 从文件流获取一个字符 (函数)
fgets 从文件流获取一个字符串 (函数)
fputc /putc 将一个字符写入文件流 (函数)
fputs 将一个字符串写入文件流 (函数)
getchar stdin 读取一个字符 (函数)
gets(C11 中移除)/gets_s(C11) stdin 读取一个字符串 (函数)
putchar 将一个字符写入 stdout (函数)
puts 将一个字符串写入 stdout (函数)
ungetc 将一个字符送回文件流 (函数)

3.4.1 fgetc / getc

#include <stdio.h>
// 从给定的输入流读取下一个字符
int fgetc( FILE* stream );
// 同fgetc,但若 getc 实现为宏,则它可能求值 stream 多于一次,所以实参始终不应为带有副作用的表达式。
int getc( FILE* stream );

参数

  • stream - 读取字符的来源

返回值

  • 成功 返回作为 unsigned char 获得并转换为 int 的字符

  • 失败 返回 EOF

3.4.2 fgets

#include <stdio.h>

char* fgets( char*          str, int count, FILE*          stream ); (C99 前) 
char* fgets( char* restrict str, int count, FILE* restrict stream ); (C99 起) 

从给定文件流读取最多 count - 1 个字符并将它们存储于 str 所指向的字符数组。

若文件尾出现或发现换行符则终止分析,后一情况下 str 将包含一个换行符。

若读入字节且无错误发生,则紧随写入到 str 的最后一个字符后写入空字符。

参数

  • str 指向 char 数组元素的指针
  • count 写入的最大字符数
  • stream 读取数据来源的文件流

返回值

  • 成功 返回str
  • 失败 返回空指针

3.4.3 fputc / putc

#include <stdio.h>
// 写入字符 ch 到给定输出流 stream 。
int fputc( int ch, FILE *stream );
// putc() 可以实现为宏并对 stream 求值超过一次,故对应的参数决不应是有副效应的表达式。
int putc( int ch, FILE *stream );

参数

  • ch - 要写入的字符
  • stream - 输出流

返回值

  • 成功 返回被写入字符

  • 失败 返回 EOF并设置 stream 上的错误指示器

3.4.4 fputs

#include <stdio.h>

int fputs( const char*          str, FILE*          stream );(C99 前) 
int fputs( const char* restrict str, FILE* restrict stream );(C99 起) 

将以NULL结尾的字符串 str 的每个字符写入到输出流 stream,如同通过重复执行 fputc

不写入 str 的终止空字符。

参数

  • str 要写入的空终止字符串
  • stream 输出流

返回值

  • 成功 返回非负值
  • 失败 返回EOF并设置 stream 上的错误指示器

3.5 有格式的输入/输出

(1)scanf

  • scanf : 从 stdin 读取数据

  • fscanf : 从文件流 stream 读取数据

  • sscanf : 从空终止字符串 buffer 读取数据。抵达字符串结尾等价于 fscanf 的抵达文件尾条件。

#include <stdio.h>
int scanf( const char *restrict format, ... );
int fscanf( FILE *restrict stream, const char *restrict format, ... );
int sscanf( const char *restrict buffer, const char *restrict format, ... );

参数

  • stream 要读取的输入文件流
  • buffer 指向要读取的空终止字符串的指针
  • format 指向指定读取输入方式的空终止字符串的指针
  • ... 各接受实参

返回值

成功 赋值的接收参数的数量

(2)printf

  • printf : 将结果写入输出流 stdout

  • fprintf : 将结果写入输出流 stream

  • sprintf : 将结果写入字符串 buffer

  • snprintf : 将结果写入字符串 buffer。至多写 bufsz - 1 个字符。

#include <stdio.h>
int scanf( const char *restrict format, ... );
int fscanf( FILE *restrict stream, const char *restrict format, ... );
int sscanf( const char *restrict buffer, const char *restrict format, ... );
int snprintf( char* restrict buffer, size_t bufsz,
               const char* restrict format, ... );

参数

  • stream 要写入的输出文件流
  • buffer 指向要写入字符串的指针
  • bufsz 最多会写bufsz - 1 个字符,再加空终止符
  • format 指向指定数据转译方式的空终止字符串的指针
  • ... 指定要打印数据的参数。

3.6 冲洗一个文件流(fflush)

将输出流与实际文件同步

#include <stdio.h>
int fflush(FILE *stream);

对于输出流(及最后操作为输出的更新流),从 stream 的缓冲区写入未写的数据到关联的输出设备。

对于输入流(及最后操作为输入的更新流),行为未定义。

stream 是空指针,则冲入所有输出流,包括操作于库包内者,或在其他情况下程序无法直接访问者。

参数

  • stream - 要写入的文件流02

返回值

  • 成功 返回零
  • 失败 返回 EOF 并设置文件流的错误指示器。

3.7 文件定位

(1) ftell

返回 stream 的文件位置指示器

若流以二进制模式打开,则由此函数获得的值是从文件开始的字节数。
若流以文本模式打开,则由此函数返回的值未指定,且仅若作为 fseek() 的输入才有意义。

#include <stdio.h>
long ftell( FILE *stream );

参数

  • stream 要检验的文件流

返回值

  • 成功 返回文件位置指示器,
  • 失败 返回 -1L

(2) fgetpos

获得文件流 stream 的文件位置指示器和当前分析状态(若存在),并将它们存储于 pos 所指向的对象。存储的值仅在作为fsetpos的输入的情况有意义。

#include <stdio.h>
int fgetpos( FILE *restrict stream, fpos_t *restrict pos );

参数

stream : 要检验的文件流

pos : 指向要存储文件位置指示器到的fpos_t 对象的指针。

返回值

  • 成功 0
  • 失败 非0

(3)fseekfsetpos

fseek

stream 以二进制模式打开,则新位置恰好是文件起始后,或当前文件位置后,或文件结尾后的 offset 字节。

stream 以文本模式打开,则仅有的受支持 offset 值为零(可用于任何 origin)和先前在关联到同一个文件的流上对ftell的调用的返回值(仅可用于 SEEK_SETorigin)。

#include <stdio.h>
int fseek( FILE* stream, long offset, int origin );

参数

  • stream 要修改的文件流
  • offset 相对 origin 迁移的字符数
  • origin offset 所加上的位置。(SEEK_SET、SEEK_CUR、SEEK_END)

返回值

  • 成功 0
  • 失败 非零

(4)fsetpos

按照 pos 所指向的值,设置文件流 stream 的文件位置指示器和多字节分析状态(若存在)。

#include <stdio.h
int fsetpos( FILE *stream, const fpos_t *pos );

参数

  • stream 要修改的文件流
  • pos 指向 fpos_t 对象的指针,用作文件位置指示器的新值。

返回值

  • 成功 0
  • 失败 非零

(5)rewind

移动文件位置指示器到给定文件流的起始。

函数等价于 fseek(stream, 0, SEEK_SET);,但它会清除文件尾和错误指示器。

#include <stdio.h>
void rewind( FILE *stream );

参数

  • stream 要修改的文件流

3.8 错误处理(feof、ferror、perror)

  • feof : 检查是否已抵达给定文件流的结尾。
  • ferror : 检查给定文件流的错误。
  • perror : 打印当前存储于系统变量 erron 的错误码到 stderr
int feof( FILE *stream );
int ferror( FILE *stream );
void perror( const char *s );

注意

feof函数仅报告最近一次 I/O 操作所报告的流状态,而不检验关联的数据源。例如,若最近一次 I/O 是抵达文件最后字节的fgetc,则 feof 返回零。下个fgetc失败并更改流状态为文件尾。然后 feof 才返回非零。

典型用法中,输入流处理在任何错误时停止;而 feofferror用于区别不同错误条件。

posted @ 2025-07-11 10:40  星空丶star  阅读(8)  评论(0)    收藏  举报