标准I/O:库函数
- 一、I/O相关知识
- 二、标准I/O函数
- 1.fopen、fclose、strerror、perror(打开、关闭文件,输出错误码信息)
- 2.fgetc、fputc、getchar、putchar(读、写一个字符)
- 3.fgets、fputs、gets、puts(读、写一行字符)
- 4.fread、fwrite、feof、ferror(读、写若干个对象,判断文件是否结束或出错)
- 5.sprintf、snprintf、fprintf(格式化输出到内存、文件)
- 6.time、localtime(获取系统时间,将系统时间转为本地时间)
- 8.fflush(刷新缓冲区)
- 9.fseek、rewind、ftell(定位文件指针)
- 10.freopen(重定向输入输出流)
- 三、缓冲区相关知识
一、I/O相关知识
1.1最早接触的I/O
#include<stdio.h> 标准的输入输出头文件
它是printf、scanf函数的头文件,它们都是标准IO
1.2I/O的种类
标准IO:库函数
文件IO:系统调用
1.3库函数和系统调用
系统调用:系统调用就是从用户空间进入内核空间的一次过程,系统调用没有缓冲区,系统调用的效率低,系统调用可移植性比较差。
库函数:库函数=缓冲区+系统调用,库函数的效率比系统调用的高,库函数可移植比较强。
1.4什么是FILE?
FILE:又被称为流,是一个结构体类型,标准I/O用FILE来存放打开的文件的相关信息(缓冲区,系统调用相关的内容),标准I/O的所有操作都是围绕FILEl来进行的。在一个正在执行的程序中默认已经有三个FILE的指针stdin,stdout,stderr。
标准I/O预定义3个流,程序运行时自动打开
标准输入流 | 0 | STDIN_FILENO | stdin |
标准输出流 | 1 | STDOUT_FILENO | stdout |
标准错误流 | 2 | STDERR_FILENO | stderr |
FILE结构体
typedef struct _IO_FILE FILE;
struct _IO_FILE {
char* _IO_buf_base; //缓冲区的起始地址
char* _IO_buf_end; //缓冲区的结束地址
int _fileno; //系统调用的文件描述符
...
};
二进制流/文本流换行符
Liniux
换行符 <---> '\n'
windows
二进制流:换行符 <---> '\n'
文本流:换行符 <---> '\r''\n'
二、标准I/O函数
1.fopen、fclose、strerror、perror(打开、关闭文件,输出错误码信息)
fopen、fclose函数API
#include <stdio.h>
FILE *fopen(const char *pathname, const char *mode);
功能:打开文件(标准IO)
参数:
@pathname:想要打开文件的路径及名字 "./hello.txt" "/home/linux/1.c"
@mode:打开文件的方式 "a" "a+" "w" "w+" "r" "r+"
r 以只读的方式打开文件,将光标定位到文件的开头
r+ 以读写的方式打开文件,将光标定位到文件的开头
w 以只写方式打开文件,如果文件存在就清空,如果文件不存在就创建,将光标定位到开头
w+ 以读写方式打开文件,如果文件存在就清空,如果文件不存在就创建,将光标定位到开头
a 以追加的方式打开文件,如果文件不存在就创建,如果文件存在不会清空,将光标定位到结尾
a+ 以读和追加的方式打开文件,如果文件不存在就创建,读光标在开头,写光标在结尾(光标就一个)
返回值:成功返回文件指针,失败返回NULL,置位错误码errno
int fclose(FILE *stream);
功能:关闭文件
参数:
@stream:文件指针
返回值:成功返回0,失败返回EOF(-1),置位错误码 //#define EOF (-1)
errno(内核中错误码问题)
#include <errno.h>
extern int errno;
errno 存放错误码
当用户通过fopen调用系统接口的时候,会打开文件,如果文件打开失败会给fp设置为NULL,同时将errno设置为对应的错误码,在内核中一个有4K个错误码,通过错误码的数值表示错误的类型。(errno是在#include <errno.h>的头文件中声明的)
strerror、perror函数API
#include <string.h>
char *strerror(int errnum);
功能:将错误码转换为错误信息
参数:
@errnum:错误码
返回值:错误信息的字符串
#include <stdio.h>
void perror(const char *s);
功能:打印错误信息
参数:
@s:用户的附加信息
返回值:无
1.1fopen、fclose函数实例1
#include <stdio.h>
int main(int argc, const char *argv[])
{
// 1.定义文件流指针
FILE *fp;
// 2.以只写方式打开文件,若文件不存在,创建文件
if (NULL == (fp = fopen("./1.txt", "w"))) {
perror("fopen errror");
return -1;
}
// 3.关闭文件流指针
fclose(fp);
puts("--------start");
fclose(stdout);
puts("--------end"); //关闭标准输出流stdout,终端不会打印这行
return 0;
}
1.2strerror、perror函数实例2
#include <stdio.h>
#include <errno.h>
#include <string.h>
int main(int argc, const char *argv[])
{
// 1.定义文件流指针
FILE *fp;
// 2.以只读方式打开文件,若文件不存在,打印错误信息
if (NULL == (fp = fopen("./1.txt", "r"))) {
printf("errno = %d, errmsg = %s\n", errno, strerror(errno));
perror("fopen error");
return -1;
}
// 3.关闭文件流指针
fclose(fp);
return 0;
}
2.fgetc、fputc、getchar、putchar(读、写一个字符)
fgetc、fputc函数API
int fgetc(FILE *stream);
功能:从文件中读取一个字符(光标自动向后移动)
参数:
@stream:文件指针
返回值:成功返回读取到的字符的ASCII,读取到结尾或者遇到错误就返回EOF
int fputc(int c, FILE *stream);
功能:向文件中写入一个字符(光标自动向后移动)
参数:
@c:字符的ascii的值 'c' 65
@stream:文件指针
返回值:成功返回写入到的字符的ASCII,失败返回EOF
getchar、putchar函数API
int getchar(void);
getchar()等同于fgetc(stdin)
int putchar(int c);
putchar(c)等同于fputc(ch, stdout)
2.1fgetc函数实例1(从文件读)
#include <stdio.h>
int main(int argc, const char *argv[])
{
FILE *fp;
if (NULL == (fp = fopen("./1.txt", "r"))) {
perror("fopen error");
return -1;
}
// 文件内容 abcd
char ch;
while (EOF != (ch = fgetc(fp))) {
printf("%c", ch);
}
fclose(fp);
return 0;
}
2.2fgetc函数实例2(从终端读)
#include <stdio.h>
int main(int argc, const char *argv[])
{
char ch;
ch = fgetc(stdin);
printf("ch = %c\n", ch);
return 0;
}
2.3fgetc函数实例3(统计文件行号)
#include <stdio.h>
int main(int argc, const char *argv[])
{
// 1.对输入参数的个数进行检查
if (2 != argc) {
fprintf(stderr, "Usage: %s <file>\n", argv[0]);
return -1;
}
// 2.以只读的方式打开文件
FILE *fp;
if (NULL == (fp = fopen(argv[1], "r"))) {
perror("fopen error");
return -1;
}
// 3.读取文件中的字符
int line = 0;
char ch;
// 如果没有读取到文件的结尾,循环就继续,如果读取到文件的结尾那就停止
while (EOF != (ch = fgetc(fp))) {
if ('\n' == ch) {
line++;
}
}
// 4.打印行号,关闭文件
printf("%s line = %d\n", argv[1], line);
fclose(fp);
return 0;
}
2.4fputc函数实例4(向文件写)
#include <stdio.h>
int main(int argc, const char *argv[])
{
if (2 != argc) {
fprintf(stderr, "Usage: %s <file>\n", argv[0]);
return -1;
}
FILE *fp;
if (NULL == (fp = fopen(argv[1], "w"))) {
perror("fopen error");
return -1;
}
char ch = 'A';
fputc(ch, fp);
fputc('h', fp);
fputc('e', fp);
fputc('l', fp);
fputc('l', fp);
fputc('o', fp);
fputc('!', fp);
fclose(fp);
return 0;
}
2.5fputc函数实例5(向终端写)
#include <stdio.h>
int main(int argc, const char* argv[])
{
//向终端上写hello'\n'
fputc('h', stdout);
fputc('e', stdout);
fputc('l', stdout);
fputc('l', stdout);
fputc('o', stdout);
fputc('\n', stdout);
return 0;
}
2.6fputc函数实例6(文件拷贝)
#include <stdio.h>
int main(int argc, const char *argv[])
{
// 1.对输入参数的个数进行检查
if (3 != argc) {
fprintf(stderr, "Usage: %s <src_file> <dest_file>\n", argv[0]);
return -1;
}
// 2.以只读的方式打开文件,以只写方式打开目标文件
FILE *sfp, *dfp;
if (NULL == (sfp = fopen(argv[1], "r"))) {
perror("open src_file error");
return -1;
}
if (NULL == (dfp = fopen(argv[2], "w"))) {
perror("open dest_file error");
return -1;
}
// 3.读取文件中的字符
char ch;
while (EOF != (ch = fgetc(sfp))) {
fputc(ch, dfp);
}
// 4.关闭文件
fclose(sfp);
fclose(dfp);
return 0;
}
3.fgets、fputs、gets、puts(读、写一行字符)
fgets、fputs函数API
char *fgets(char *s, int size, FILE *stream);
功能:从文件中读取字符串到向s指向的内存中,遇到'\n'或size-1个字符返回(最后一个用于存'\0')。
fgets遇到EOF或者换行符的时候就会停止,如果遇到换行符停止的,换行符也会被存储到s中。
参数:
@s:指向存储字符的首地址
@size:想要读取字符的个数
@stream:文件指针
返回值:成功返回s,失败返回NULL
int fputs(const char *s, FILE *stream);
功能:将s指向的内存中的字符串写入到文件中
参数:
@s:被写字符串的首地址
@steam:文件指针
返回值:成功返回大于0的值,失败返回EOF
gets、puts函数API
char *gets(char *s);
功能:gets从标准输入stdin读字符串到s指向的内存
返回值:成功返回时s,遇到'\n'或已输入size-1个字符时返回,总是包含'\0',到文件末尾或出错返回NULL
gets不推荐使用,容易造成缓冲区溢出
int puts(const char *s);
功能:puts将缓冲区s中的字符串输出到stdout,并追加'\n'
返回值:成功时返回输出的字符个数,失败返回EOF
3.1fgets函数实例1(从文件读)
#include <stdio.h>
int main(int argc, const char *argv[])
{
FILE *fp;
if (NULL == (fp = fopen("./1.txt", "r"))) {
perror("fopen error");
return -1;
}
char buf[10] = {0};
//fgets读取到换行符'\n'或sizeof(buf)-1个字符返回,将读取字符串最后一个字符buf[strlen(buf)]填充为'\0'
while (NULL != fgets(buf, sizeof(buf), fp)) {
printf("%s", buf); //注意:在linux中创建的文本中最后末尾一行也有换行符'\n'
}
fclose(fp);
return 0;
}
3.2fgets函数实例2(从终端读)
#include <stdio.h>
#include <string.h>
int main(int argc, const char* argv[])
{
char buf[10] = {0};
//终端输入: hello\n
//fgets读取到换行符'\n'或sizeof(buf)-1个字符返回,将读取字符串最后一个字符buf[strlen(buf)]填充为'\0'
if (NULL == fgets(buf, sizeof(buf), stdin)) {
printf("fgets error\n");
return -1;
}
buf[strlen(buf) - 1] = '\0'; //将终端读取的'\n'设置为'\0'
printf("buf = %s\n", buf); //buf = hello,终端输出的换行符是printf里的
return 0;
}
3.3fgets函数实例3(统计文件行号)
#include <stdio.h>
#include <string.h>
int main(int argc, const char *argv[])
{
// 1.对输入参数的个数进行检查
if (2 != argc) {
fprintf(stderr, "Usage: %s <file>\n", argv[0]);
return -1;
}
// 2.以只读的方式打开文件
FILE *fp;
if (NULL == (fp = fopen(argv[1], "r"))) {
perror("fopen error");
return -1;
}
// 3.读取文件中的字符
char buf[10] = {0};
int line = 0;
//fgets读取到换行符'\n'或sizeof(buf)-1个字符返回,将读取字符串最后一个字符buf[strlen(buf)]填充为'\0'
while (NULL != fgets(buf, sizeof(buf), fp)) {
if ('\n' == buf[strlen(buf) - 1]) { //注意:在linux中创建的文本中最后末尾一行也有换行符'\n'
line++;
}
}
// 4.打印结果,关闭文件
printf("%s line = %d\n", argv[1], line);
fclose(fp);
return 0;
}
3.4fputs函数实例4(向文件写)
#include <stdio.h>
#include <string.h>
int main(int argc, const char *argv[])
{
FILE *fp;
if (NULL == (fp = fopen("./1.txt", "w"))) {
perror("fopen error");
return -1;
}
char buf[20] = "hello world!";
fputs(buf, fp);
fputs("xiao ming", fp);
fclose(fp);
return 0;
}
3.5fputs函数实例(向终端写)
#include <stdio.h>
int main(int argc, const char *argv[])
{
char buf[20] = "hello wrorld";
fputs(buf, stdout);
fputs("xiao ming\n", stdout);
return 0;
}
4.fread、fwrite、feof、ferror(读、写若干个对象,判断文件是否结束或出错)
fread、fwrite函数API
即可读写文本文件,也可读写数据文件,效率高。
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
功能:从stream中读取nmemb项数据,每一项的大小是size,将它们存到ptr。
参数:
@ptr:存储读取到数据的首地址
@size:每一项的大小
@nmemb:项目的个数
@stream:文件指针
返回值:成功返回读取到的项目的个数,如果小于项目的个数就是错误或者到文件的结尾了。
fread读取到错误或文件结尾返回,不会给读取的字符串数据填充'\0'。
size_t fwrite(const void *ptr, size_t size, size_t nmemb,FILE *stream);
功能:将ptr中的数据写入到stream中,写nmemb向,每一项的大小是size
参数:
@ptr:被写数据的首地址
@size:每一项的大小
@nmemb:项目的个数
@stream:文件指针
返回值:成功返回项目的个数,失败返回小于项目的个数。
feof、ferror函数API
fread返回值无法区分是到达了文件的结尾还是错误发生了,调用者必须通过feof或者ferror函数来判断。
int feof(FILE *stream);
功能:判断是否读取到了文件的结尾,如果到了文件的结尾将返回真
参数:
@stream:文件指针
返回值:如果到了文件的结尾返回真,如果没有到文件结尾返回假
int ferror(FILE *stream);
功能:如果在读文件的时候发生了错误,这个函数返回真
参数:
@stream:文件指针
返回值:如果发生了错误返回真,如果没有发生错误返回假
4.1fread函数实例1(从文件读)
#include <stdio.h>
int main(int argc, const char *argv[])
{
FILE *fp;
if (NULL == (fp = fopen("./1.txt", "r"))) {
perror("fopen error");
return -1;
}
// fread读取到错误或文件结尾返回,不会给读取的字符串数据填充'\0'。
char buf[10] = {0};
int ret;
ret = fread(buf, 1, sizeof(buf)-1, fp); //最好sizeof(buf)-1, 防止当读满buf时,buf后连续内存不为'\0'
printf("buf = %s", buf);
printf("ret = %d\n", ret); //9
printf("feof = %d, ferror = %d\n", feof(fp), ferror(fp)); //用vim创建的1.txt, 当输入字符为 <= 7 + ('/n''/0')
//feof = 1, ferror = 0
//用vscode创建的1.txt, 当输入里字符为 <= 8 + ('/0')
//feof = 1, ferror = 0
fclose(fp);
return 0;
}
4.2fread函数实例2(从文件读)
#include <stdio.h>
#include <string.h>
int main(int argc, const char* argv[])
{
// 1.对输入参数的个数进行检查
if (argc != 2) {
printf("input error,try again\n");
printf("usage: ./a.out filename\n");
return -1;
}
// 2.以只读的方式打开文件
FILE* fp;
if ((fp = fopen(argv[1], "r")) == NULL) {
perror("fopen error");
return -1;
}
// 3.读取数据
char buf[10] = {0};
int ret;
// 如果在读的时候没有到结尾,并且也没有错误,循环继续
while (!(feof(fp) || ferror(fp))) {
// 在读取之前将数组中的内容清零,如果没有这句,最后一行打印的结果不对
// 因为fread读取到错误或文件结尾返回,不会给读取的字符串数据填充'\0'。
memset(buf, 0, sizeof(buf));
fread(buf, 1, sizeof(buf)-1, fp);
printf("%s", buf);
}
fclose(fp);
return 0;
}
4.3fread函数实例3(读取整数、结构体)
配合4.4fwrite函数实例4使用
#include <stdio.h>
typedef struct {
char name[20];
int age;
char sex;
}stu_t;
int main(int argc, const char* argv[])
{
FILE* fp;
if (NULL == (fp = fopen("./1.txt", "r"))) {
perror("fopen error");
return -1;
}
// 1.读取一个字符
// char ch;
// fread(&ch, 1, 1, fp);
// printf("ch = %c\n", ch);
// 2.读取数组
// char buf[128] = {0};
// fread(buf, 1, sizeof(buf)-1, fp);
// printf("buf = %s\n", buf);
// 3.读取整数
// int num;
// fread(&num, sizeof(num), 1, fp);
// printf("num = %d\n", num);
// 4.读取结构体
stu_t stu;
fread(&stu, sizeof(stu), 1, fp);
printf("name=%s, age=%d, sex=%c\n",stu.name,stu.age,stu.sex);
fclose(fp);
return 0;
}
4.4fwrite函数实例4(写入整数、结构体)
配合4.3fread函数实例3使用
#include <stdio.h>
#include <string.h>
typedef struct{
char name[20];
int age;
char sex;
}stu_t;
int main(int argc, const char* argv[])
{
FILE* fp;
if ((fp = fopen("./1.txt", "w")) == NULL) {
perror("fopen error");
return -1;
}
// 1.使用fwrite写一个字符
// char ch='q';
// fwrite(&ch, 1, 1, fp);
// 2.使用fwrite写字符串
// char buf[] = "hello everyone!";
// fwrite(buf, 1, strlen(buf), fp);
// 3.使用fwrite写整数
// hello.txt是一个文本文件,文本文件中只能显示字符,并不能直接显示整数,
// 但是数据确实是写进去了。写入的内容见下图
// int num=12345;
// fwrite(&num, sizeof(num), 1, fp);
// 4.使用fwrite写结构体
stu_t stu = {
.name = "xiao ming",
.age = 30,
.sex = 'm'
};
fwrite(&stu, sizeof(stu), 1, fp);
fclose(fp);
return 0;
}
4.5fwrite写整数到文件,文本内容解析
4.6fread、fwrite函数实例5(文件拷贝)
#include <stdio.h>
#include <string.h>
int main(int argc, const char *argv[])
{
// 1.对输入参数的个数进行检查
if (3 != argc) {
fprintf(stderr, "Usage: %s <src_file> <dest_file>\n", argv[0]);
return -1;
}
// 2.以只读的方式打开源文件,以只写方式打开目标文件
FILE *sfp, *dfp;
if (NULL == (sfp = fopen(argv[1], "r"))) {
perror("fopen src_file error");
return -1;
}
if (NULL == (dfp = fopen(argv[2], "w"))) {
perror("fopen dest_file error");
return -1;
}
// 3.循环拷贝
// 如果在读的时候没有到结尾,并且也没有错误,循环继续
char buf[10] = {0};
int ret;
while (!(feof(sfp) || ferror(sfp))) {
ret = fread(buf, 1, sizeof(buf), sfp);
fwrite(buf, 1, ret, dfp);
}
// 4.判断返回数据项个数也行
// while (0 < (ret = fread(buf, 1, sizeof(buf), sfp))) {
// fwrite(buf, 1, ret, dfp);
// }
fclose(sfp);
fclose(dfp);
return 0;
}
5.sprintf、snprintf、fprintf(格式化输出到内存、文件)
sprintf、snprintf、fprintf函数API
int sprintf(char *str, const char *format, ...);
功能:将format控制格式的内容写到str对应的内存中(不会向终端显示)
参数:
@str:内存地址
@format:和printf的参数完全相同
返回值:成功返回格式化的字符的个数,失败返回负数
int snprintf(char *str, size_t size, const char *format, ...);
功能:将format控制格式的内容写到str对应的内存中(不会向终端显示)
参数:
@str:内存地址
@size:最多格式化size个字符,其中还包括一个'\0'
@format:和printf的参数完全相同
返回值:成功返回格式化的字符的个数,失败返回负数
int fprintf(FILE *stream, const char *format, ...);
功能:将控制格式格式化的字符串写入到文件中
参数:
@stream:文件指针
@format:控制格式
返回值:成功返回格式化的字符的个数,失败返回负数
5.1sprintf函数实例1
#include <stdio.h>
#include <string.h>
typedef struct{
char name[20];
int age;
char sex;
}stu_t;
int main(int argc, const char* argv[])
{
FILE* fp;
if ((fp = fopen("./hello.txt", "w+")) == NULL) {
perror("fopen error");
return -1;
}
//1.使用fwrite写整数
//hello.txt是一个文本文件,文本文件中只能显示字符,并不能直接显示整数,
//但是数据确实是写进去了。
int num=123456;
char buf[10] = {0};
sprintf(buf,"%d\n",num);
//使用sprintf函数的时候一定要注意越界问题,如果越界程序错误
fwrite(buf,1,strlen(buf),fp);
//2.使用fwrite写结构体
stu_t stu = {
.name = "zhangsan",
.age = 30,
.sex = 'm'
};
char buf1[50] = {0};
sprintf(buf1,"%s,%d,%c\n",stu.name,stu.age,stu.sex);
fwrite(buf1,1,strlen(buf1),fp);
fclose(fp);
return 0;
}
5.2snprintf函数实例2
#include <stdio.h>
#include <string.h>
typedef struct{
char name[20];
int age;
char sex;
}stu_t;
int main(int argc, const char* argv[])
{
FILE* fp;
if ((fp = fopen("./hello.txt", "w+")) == NULL) {
perror("fopen error");
return -1;
}
//1.使用fwrite写整数
//hello.txt是一个文本文件,文本文件中只能显示字符,并不能直接显示整数,
//但是数据确实是写进去了。
int num=123456;
char buf[10] = {0};
snprintf(buf,sizeof(buf),"%d",num);
//使用sprintf函数的时候一定要注意越界问题,如果越界程序错误
fwrite(buf,1,strlen(buf),fp);
//2.使用fwrite写结构体
stu_t stu = {
.name = "zhangsan",
.age = 30,
.sex = 'm'
};
char buf1[50] = {0};
snprintf(buf1,sizeof(buf1),"%s,%d,%c\n",stu.name,stu.age,stu.sex);
fwrite(buf1,1,strlen(buf1),fp);
fclose(fp);
return 0;
}
5.4fprintf函数实例(获取系统时间到终端显示)
#include <stdio.h>
#include <time.h>
#include <unistd.h>
int main(int argc, const char *argv[])
{
time_t ts;
struct tm *tm;
while (1) {
if (-1 == (ts = time(NULL))) {
perror("get time error");
return -1;
}
if (NULL == (tm = localtime(&ts))) {
perror("change time error");
return -1;
}
fprintf(stdout, "%d-%02d-%02d %02d:%02d:%02d\r", tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec);
fflush(stdout);
sleep(1);
}
return 0;
}
6.time、localtime(获取系统时间,将系统时间转为本地时间)
time、localtime函数API
#include <time.h>
time_t time(time_t *tloc);
功能:获取自1970-01-01 00:00:00到当前的秒钟数
参数:
@tloc:NULL
返回值:成功返回秒钟数,失败返回-1,并置位错误码
struct tm *localtime(const time_t *timep);
功能:将time_t的秒钟转换为tm的结构体(结构体中包含年、月、日...)
参数:
@timep:秒钟变量的地址
返回值:成功返回tm结构体指针,失败返回NULL,并置位错误码
struct tm {
int tm_sec; /* Seconds (0-60) */
int tm_min; /* Minutes (0-59) */
int tm_hour; /* Hours (0-23) */
int tm_mday; /* Day of the month (1-31) */
int tm_mon; /* Month (0-11) */ //+1
int tm_year; /* Year - 1900 */ //+1900
int tm_wday; /* Day of the week (0-6, Sunday = 0) */ //周几
int tm_yday; /* Day in the year (0-365, 1 Jan = 0) */ //这一年中的第几天
int tm_isdst; /* Daylight saving time */ //夏令时
};
8.fflush(刷新缓冲区)
fflush函数API
int fflush(FILE *fp);
功能:将流缓冲区里的数据写入到实际的文件,Linux下只能刷新输出缓冲区
参数:
@fp:文件指针
返回值:成功返回0,失败返回EOF
9.fseek、rewind、ftell(定位文件指针)
fseek、rewind、ftell函数API
int fseek(FILE *stream, long offset, int whence);
功能:设置光标的位置
参数:
@stream:文件指针
@offset:光标的偏移
>0 向后偏移
=0 不偏移
<0 向前偏移
@whence:从那个位置偏移
SEEK_SET //从开头开始偏移
SEEK_CUR //从当前位置偏移
SEEK_END //从结尾的位置偏移
返回值:成功返回0,失败返回-1置位错误码
eg:
fseek(fp,0,SEEK_END); //将光标定位到文件的结尾
fseek(fp,50,SEEK_SET);//将光标定位到第50个字节的位置
fseek(fp,-5,SEEK_CUR);//将光标从当前位置向前偏移5个字节
1. 文件a模式打开时,函数fseek无效
2. rewind(fp)相当于fseek(fp, 0, SEEK_SET)
3. 这3个函数只适用于2G以下的文件
void rewind(FILE *stream);
功能:将光标恢复到文件的开头(rewind = fseek(stream, 0, SEEK_SET))
参数:
@stream:文件指针
返回值:无
long ftell(FILE *stream);
功能:返回光标到文件开头的字节数
参数:
@stream:文件指针
返回值:成功返回字节数,失败返回-1置位错误码
9.1fseek函数实例1
#include <stdio.h>
#define PRINT_ERR(msg) \
do { \
perror(msg); \
return -1; \
} while (0)
int main(int argc, const char* argv[])
{
FILE* fp;
if ((fp = fopen("./hello.txt", "r+")) == NULL)
PRINT_ERR("fopen error");
if (fseek(fp, 4, SEEK_SET))
PRINT_ERR("fseek error");
char ch;
ch = fgetc(fp);
printf("ch = %c\n",ch);
fputc('Q',fp);
fclose(fp);
return 0;
}
9.2ftell函数实例2(输出文件大小)
#include <stdio.h>
#define PRINT_ERR(msg) \
do { \
perror(msg); \
return -1; \
} while (0)
int main(int argc, const char* argv[])
{
FILE* fp;
if(argc !=2){
fprintf(stderr,"input error,try again\n");
fprintf(stderr,"usage:./a.out filename\n");
return -1;
}
if ((fp = fopen(argv[1], "r")) == NULL)
PRINT_ERR("fopen error");
if (fseek(fp, 0, SEEK_END))
PRINT_ERR("fseek error");
printf("pos = %ld\n",ftell(fp));
fclose(fp);
return 0;
}
10.freopen(重定向输入输出流)
10.1freopen函数实例
#include <stdio.h>
int main(int argc, const char **argv)
{
if (freopen("1.txt", "w", stdout) == NULL) {
perror("freopen");
return -1;
}
printf("stdout --> 1.txt\n");
fclose(stdout);
printf("end!\n");
return 0;
}
三、缓冲区相关知识
1.缓冲区的分类
行缓存:和终端相关的缓冲区就是行缓存(stdin stdout)
全缓存:和文件相关的缓冲区就是全缓存(fp)
无缓存:没有缓冲区(stderr)
2.缓冲区的大小
行缓存:1024(1K)
全缓存:4096(4K)
无缓存:0
#include <stdio.h>
int main(int argc, const char *argv[])
{
// 1.stdin缓冲区大小 1024(使用了才分配)
//FILE *stdin; --->FILE是一个结构体类型,stdin结构体指针,结构体指针访问内部成员->
int num;
scanf("%d", &num);
printf("stdio buffer size = %ld\n", stdin->_IO_buf_end - stdin->_IO_buf_base); //1024
// 2.stdout缓冲区大小 1024
printf("stdout buffer size = %ld\n", stdout->_IO_buf_end - stdout->_IO_buf_base); //1024
// 3.文件相关的缓冲区的大小 4096
FILE *fp;
if (NULL == (fp = fopen("./1.txt", "w"))) {
perror("fopen error");
return -1;
}
fputc(num, fp);
printf("fp buffer size = %ld\n", fp->_IO_buf_end - fp->_IO_buf_base); //4096
fclose(fp);
return 0;
}
3.缓冲区的刷新时机
3.1行缓冲的刷新时机(6种)
1.行缓存遇到换行符的时候会刷新缓冲区
2.当程序结束的时候会刷新行缓冲区
3.当关闭文件的时候会刷新行缓冲区
4.当输入和输出发生切换的时候也会刷新行缓冲区
5.当缓冲区满的时候会刷新缓冲区
6.自己使用fflush函数主动刷新缓冲区
#include <stdio.h>
int main(int argc, const char* argv[])
{
// 1.行缓存遇到换行符的时候会刷新缓冲区
// printf("1111111111\n");
// while(1);
// 2.当程序结束的时候会刷新行缓冲区
// printf("1111111111");
// 3.当关闭文件的时候会刷新行缓冲区
// printf("1111111111");
// fclose(stdout);
// while (1);
// 4.当输入和输出发生切换的时候也会刷新行缓冲区
// printf("1111111111");
// fgetc(stdin);
// while (1);
// 5.当缓冲区满的时候会刷新缓冲区
// for(int i = 0; i < 1025; i++){
// fputc('o',stdout);
// }
// while(1);
// 6.自己使用fflush函数主动刷新缓冲区
printf("1111111111");
fflush(stdout); //刷新缓冲区的函数
while(1);
return 0;
}
3.2全缓冲的刷新时机(5种)
除了换行符不能刷新缓冲区, 其他和行缓冲区一样。
1.当程序结束的时候会刷新行缓冲区
2.当关闭文件的时候会刷新行缓冲区
3.当输入和输出发生切换的时候也会刷新行缓冲区
4.当缓冲区满的时候会刷新缓冲区
5.自己使用fflush函数主动刷新缓冲区
#include <stdio.h>
int main(int argc, const char* argv[])
{
FILE* fp;
if (NULL == (fp = fopen("./1.txt", "w"))) {
printf("fopen error\n");
return -1;
}
// 1.全缓存遇到换行符的时候不会刷新缓冲区
// fputs("1111111111\n",fp);
// while(1);
// 2.当程序结束的时候会刷新全缓冲区
// fputs("1111111111",fp);
// 3.当关闭文件的时候会刷新全缓冲区
// fputs("1111111111",fp);
// fclose(fp);
// while (1);
// 4.当输入和输出发生切换的时候也会刷新全缓冲区
// fputs("1111111111",fp);
// fgetc(fp);
// while (1);
// 5.当缓冲区满的时候会刷新全缓冲区
// for(int i = 0; i < 4097; i++){
// fputc('o',fp);
// }
// while(1);
// 6.自己使用fflush函数主动刷新全缓冲区
fputs("1111111111", fp);
fflush(fp); // 刷新缓冲区的函数
while (1);
return 0;
}