流和缓冲区
- 流是C语言对I/O操作的一种抽象,在C语言的I/O操作只是从程序移进或移出字节,这便是字节流。
- C程序中大多数流是全缓冲的,即写入数据时,程序先把数据放入缓存(buffer),等到缓存满了,再把里面的数据会一次性刷新(flush)到设备或磁盘文件。这时,缓存区就空了,程序再把新的数据放入缓存,重复整个过程。
- 程序运行时系统至少提供3个流:
设备 文件指针(FILE*) 标准输入(键盘) stdin 标准输出(屏幕) stdout 标准出错(屏幕) stderr
文件指针
- FILE指针:每个被使用的文件都在内存中开辟出一个区域,用来存放文件的相关信息,这些信息是保存在一个名为FILE的结构体中,其定义在头文件
stdio.h
中。 - C语言标准io中通常用该结构体的指针 FILE* 作为句柄操作要处理的流。
fopen打开流
#include <stdio.h>
FILE *fopen(const char *pathname, const char *mode);
- 参数pathname为文件名
- 参数mode为打开文件的模式:
- r:读模式,只用来读取数据。如果文件不存在,返回 NULL 指针。
- w:写模式,只用来写入数据。如果文件存在,文件长度会被截为0,然后再写入;如果文件不存在,则创建该文件。
- a:写模式,只用来在文件尾部追加数据。如果文件不存在,则创建该文件。
- r+:读写模式。如果文件存在,指针指向文件开始处,可以在文件头部添加数据。如果文件不存在,返回 NULL 指针。
- w+:读写模式。如果文件存在,文件长度会被截为0,然后再写入数据。这种模式实际上读不到数据,反而会擦掉数据。如果文件不存在,则创建该文件。
- a+:读写模式。如果文件存在,指针指向文件结尾,可以在现有文件末尾添加内容。如果文件不存在,则创建该文件。
- 成功打开文件以后,返回一个 FILE 指针代表一个新创建的流;否则返回NULL。
fclose关闭流
#include <stdio.h>
int fclose(FILE *stream);
- stream为要关闭的流,若其为输出流,则会刷新缓冲区。
- 如果成功关闭文件,函数返回整数0;如果操作失败(比如磁盘已满,或者出现 I/O 错误),则返回EOF(通常是-1)。
字符I/O
-
字符输入
#include <stdio.h> int fgetc(FILE *stream); int getc(FILE *stream); int getchar(void);
- fgetc和getc用于从文件读取一个字符,getc一般用宏来实现,而fgetc用函数实现。而getchar只从stdin读取。
- 函数返回读到的字符,但是它们的返回值类型却不是char,而是int,这是因为读取失败或读取到文件尾的情况下,它们会返回 EOF。
-
字符输出
#include <stdio.h> int fputc(int c, FILE *stream); int putc(int c, FILE *stream); int putchar(int c);
- fputc和putc用于写入一个字符c到流stream,putc一般用宏来实现,而fputc用函数实现。而putchar只从stdout读取。
- 函数返回成功写入到流的字符,写入失败则会返回 EOF。
-
撤销字符I/O
#include <stdio.h> int ungetc(int c, FILE *stream);
- ungetc是将从输入流读到的字符退回到该流,下一次读数据时该字符便不会丢失。
- 每个流都允许至少退回一个字符。
行I/O
#include <stdio.h>
char *fgets(char *s, int size, FILE *stream);
char *gets(char *s);
int fputs(const char *s, FILE *stream);
int puts(const char *s);
- fgets用于从文件流stream中读取指定长度的字符串储存在s,读取 size - 1 个字符后或者遇到换行符与文件结尾,就会停止读取,然后在已经读取的内容末尾添加一个空字符\0。如果s够大,会将换行符 \n 存储进读取的数据末尾再添加空字符\0到s。读取成功时,fgets的返回值是它的第一个参数,即指向字符串的指针,否则(比如当文件读到末尾时)返回空指针 NULL。
- fputs函数用于向文件流stream写入字符串s,它不会在字符串末尾添加换行符。fputs出错时返回EOF,否则返回一个非负值。
- gets和puts向标准输入和标准输出上读取和写入一行内容,gets与fgets不同的是不会在s后添加换行符,而puts会在s后添加换行符到标准输出。值得一提的是gets并不检查字符数组的长度,多出的字符可能写到字符数组后面产生错误,使用gets时要谨慎!
格式化的行I/O
scanf
#include <stdio.h>
int scanf(const char *format, ...);
int fscanf(FILE *stream, const char *format, ...);
int sscanf(const char *str, const char *format, ...);
- scanf、fscanf和sscanf分别以标准输入、流和字符串作为输入源根据format字符串的格式将处理后的数据逐个保存在参数列表对应的变量(指针传递)中。
- 这些函数处理format时,会自动过滤空白字符,包括空格、制表符、换行符等。
- 返回值是一个整数,表示成功读取的变量个数。如果读取到文件结尾,则返回常量 EOF。
- 一些格式码:
格式 含义 %c 一个字符 %d或%i 一个十进制数 %f或%e或%g 一个浮点数 %u 一个无符号十进制数 %o 一个无符号八进制数 %x 一个无符号十六进制数 %n scanf函数已读的字符数量 %% %号 %*d 跳过一个十进制数 %宽度d 指定宽度个十进制数,数据宽度不够左面补空格 %[a-z] 匹配a到z中任意字符(尽可能多的匹配) %[aBc] 匹配a、B、c中一员,贪婪性 %[^a] 匹配非a的任意字符,贪婪性 %[^a-z] 表示读取除a-z以外的所有字符
printf
#include <stdio.h>
int printf(const char *format, ...);
int fprintf(FILE *stream, const char *format, ...);
int sprintf(char *str, const char *format, ...);
0nt snprintf(char *str, size_t size, const char *format, ...);
- sprintf、fprintf和sprintf将处理后的格式化字符串分别写入到标准输出、流和字符串。
- 返回值是一个整数,表示输出的字符数量
- 一些格式码:
格式 含义 %c 一个字符 %d或%i 一个十进制数 %f 一个浮点数 %e 一个浮点数,科学计数法表示 %g 一个6个有效数字的浮点数。整数部分超过6位为科学计数法表示 %u 一个无符号十进制数 %o 一个无符号八进制数 %x 一个无符号十六进制数 %n scanf函数已读的字符数量 %% %号 %*d 跳过一个十进制数 %宽度d 输出指定宽度个十进制数,数据宽度不够左面补空格 %宽度.精度f 输出指定宽度并确定小数点后位数的浮点数,数据宽度不够左面补空格
二进制I/O
#include <stdio.h>
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
size_t fwrite(const void *ptr, size_t size, size_t nmemb,
FILE *stream);
- fread和fwrite用于读取和写入二进制数据。
- ptr为储存数据的一段连续缓冲区的地址,size为缓冲区中每个元素的字节数,nmemb是元素的数目,stream即要读取或写入的流。
- 正常情况下,返回值为第三个参数nmemb(非字节数),但如果出现读取错误或读到文件结尾,该返回值就会比请求的nmemb小。
定位函数
#include <stdio.h>
int fseek(FILE *stream, long offset, int whence);
long ftell(FILE *stream);
void rewind(FILE *stream);
int fgetpos(FILE *stream, fpos_t *pos);
int fsetpos(FILE *stream, const fpos_t *pos);
- ftell返回流当前的位置,即从文件开头到当前的偏移量,发生错误会返回-1L。
- fseek用来改变流当前相对于文件的位置。stream是打开的流,whence用来确定计算起点,它的值是以下三个宏:SEEK_SET(文件开始处)、SEEK_CUR(当前位置)、SEEK_END(文件末尾)。offset为相对whence的字节数,可取正、负和0.。调用成功返回0。
- rewind函数可以让读/写指针回到流的起始位置,相当于fseek(fp, 0L, seek_set),且会清除当前流的错误提升标志。
- 由于long类型的大小有限,fgetpos和fsetpos可用于替代ftell和fseek,他们用fops_t类型来储存流的当前位置,fgetpos接收fops_t指针保存当前位置,fsetpos把流设置在pos储存的位置上。执行成功都会返回0.
流错误函数
#include <stdio.h>
void clearerr(FILE *stream);
int feof(FILE *stream);
int ferror(FILE *stream);
- 所有的标准I/O操作函数如果执行失败,都会在文件指针里面记录错误状态。
- 如果前面的操作出现错误,ferror返回真(非0值)),否则返回0。
- clearerr函数用来重置流的错误标志。
- feof函数用于判断流当前是否处于文件结尾,是则返回真(非0值),可以通过fseek、rewind、fsetpos函数清除这个函数的状态;否则返回0。
刷新函数
#include <stdio.h>
int fflush(FILE *stream);
- fflush强制将一个输出流缓冲区的数据进行物理写入。
- 如果不需要保存缓存区内容,则可以传入空指针 NULL。
- 如果清空成功,fflush返回0,否则返回 EOF。
删除文件和文件重命名
#include <stdio.h>
int remove(const char *pathname);
int rename(const char *oldpath, const char *newpath);
- remove函数用于删除指定文件,如果删除成功,remove()返回0,否则返回非零值。如果该文件被打开,则该函数行为未知。
- rename函数用于文件重命名,也用于移动文件。第一个参数是现在的文件名,第二个参数是新的文件名。如果改名成功,rename返回0,否则返回非零值。如果newpath已存在,则该函数行为未知。