目录
思维导图:
学习内容:
1. IO基础
2. 标准IO
2.1 标准IO提供的内容
2.2 FILE结构体
2.2.1 结构体解析
2.3 fopen 打开文件
2.4 fclose:关闭文件
例如:
2.5 fgetc\fputc:单字符的输入输出
例如:
2.6 错误码
2.7 fputs\fgets:字符串输入输出
例如:
2.8 缓冲区
课内练习:
1.使用fgetc完成,打开一个文件,统计该文件中的行数
2.使用fgetc和fputc完成,cp指令的功能,实现两个文件的拷贝,将src文件中的内容,拷贝到dest文件中
课外作业:
1.使用fgets统计给定文件的行号
2.使用fgets、fputs完成两个文件的拷贝
思维导图:
学习内容:
1. IO基础
1> IO:(inout output) 所谓IO,就是程序与外部设备进行信息交换的过程
2> IO的分类:标准IO和文件IO
3> 标准IO:调用封装好的相关库函数,来实现数据的输入输出
4> 文件IO:调用系统(内核)提供的相关函数,来实现数据的输入输出
5> 标准IO和文件IO的区别:
1、标准IO属于库函数,文件IO属于系统调用
2、标准IO操作的是文件指针,文件IO操作的是文件描述符
3、标准IO有缓冲器,文件IO没有缓冲区
6> 目前所接触过的IO函数:
printf、scanf、putchar、getchar、puts、gets
7> 常用的IO接口函数
标准IO:fprint、fscanf、fputc、fgetc、fputs、fgets、fread、 fwrite、fopen、fclose、 fseek、ftell、rewind。。。
文件IO:open、close、read、write、seek。
2. 标准IO
2.1 标准IO提供的内容
可以通过指令:man 7 stdio.h 可以查看该头文件提供的内容
1、标准的缓冲输入输出
2、提供的数据类型 FILE 文件结构体类型. off_t 偏移量类型 size_t 大小的类型,一般是一个long int类型
3、提供的偏移量的宏值 SEEK_SET:文件起始位置 SEEK_END:文件结束位置 SEEK_CUR:文件当前位置
4、提供文件结束标志:EOF (end of file)
5、默认提供的文件指针: stdin:标准输入指针 stdout:标准输出指针 stderr:标准出错指针
6、标准IO会提供三种缓冲区: 全缓存 行缓存 不缓存
2.2 FILE结构体
查看该结构体 :vi -t FILE
2.2.1 结构体解析
struct _IO_FILE {char* _IO_buf_base; /* 缓冲区开始地址 */char* _IO_buf_end; /* 缓冲区结束地址 */int _fileno; //用于系统调用的文件描述符};
当一个程序启动后,系统会自动打开三个文件指针:
stdin:标准输入文件指针 ---> scanf、getchar、gets
stdout:标准输出文件指针 ----> printf、putchar、puts
stderr:标准出错文件指针 ---> perror
以上三个文件指针,都是针对于终端文件操作而言的
2.3 fopen 打开文件
#include <stdio.h>FILE *fopen(const char *pathname, const char *mode);
功能:打开给定路径的文件,以mode的形式打开
参数1: 要打开的文件的路径,是一个字符串
参数2:打开文件时的打开方式,是一个字符串r 以只读的形式打开一个文件,文件指针定位在开头Open text file for reading. The stream is positioned at the beginning of the file.r+ 以读写的形式打开一个文件,文件指针定位在开头Open for reading and writing. The stream is positioned at the beginning of the file.w 以只写的形式打开文件,如果文件不存在则创建文件,如果文件存在则清空文件内容,文件指针定位在开头Truncate file to zero length or create text file for writing. The stream is positioned at the beginning of the file.w+ 以读写的形式打开文件,如果文件不存在则创建文件,如果文件存在则清空文件内容,文件指针定位在开头Open for reading and writing. The file is created if it does not exist, otherwise it is truncated. The stream is positionedat the beginning of the file.a 以追加的形式打开文件(结尾写),如果文件不存在则创建文件,如果存在则结尾写,文件指针定位在结尾Open for appending (writing at end of file). The file is created if it does not exist. The stream is positioned at the endof the file.a+ 以读或追加的形式打开文件,如果文件不存在,则创建文件,读指针定位在开头,写指针定位在结尾Open for reading and appending (writing at end of file). The file is created if it does not exist. The initial file posi‐tion for reading is at the beginning of the file, but output is always appended to the end of the file.
返回值:成功打开文件,返回该文件的地址,失败返回NULL,并置位错误码
2.4 fclose:关闭文件
#include <stdio.h>int fclose(FILE *stream);功能:关闭给定的文件指针参数1:要关闭的文件指针返回值:成功返回0,失败返回EOF,并置位错误码
例如:
#include<myhead.h>int main(int argc, const char *argv[])
{//定义文件类型的结构体以便于接受打开的文件地址FILE * fp = NULL;//打开一个文件//fp = fopen("./text.txt", "r"); //以只读的形式打开一个当前路径下的文件fp = fopen("./text.txt", "w"); //以只写的形式打开一个当前路径下的文件if(NULL == fp){printf("fopen error\n");return -1;}printf("fopen success"); //关闭文件指针fclose(fp);return 0;
}
2.5 fgetc\fputc:单字符的输入输出
#include <stdio.h>int fputc(int c, FILE *stream);功能:将给定的字符,写入到文件指针stream指向的文件中去参数1:要写入的字符参数2:打开的文件指针返回值:成功返回写入字符的ascii值,失败返回EOF,并置位错误码#include <stdio.h>int fgetc(FILE *stream);功能:从指定文件中,读取一个字符参数:打开的文件指针返回值:从文件中读取的第一个字符的ascii值,失败返回EOF并置位错误码
例如:
#include<myhead.h>int main(int argc, const char *argv[])
{//定义文件类型的结构体以便于接受打开的文件地址FILE * fp = NULL;//打开一个文件//fp = fopen("./text.txt", "r"); //以只读的形式打开一个当前路径下的文件fp = fopen("./text.txt", "w+"); //以只写的形式打开一个当前路径下的文件if(NULL == fp){printf("fopen error\n");return -1;}printf("fopen success\n");//向文件中写入字符fputc('H', fp);fputc('e', fp);fputc('l', fp);fputc('l', fp);fputc('o', fp);fputc('\n', fp);//文件中存储的结果为 Hello //说明每次写入数据时,光标会自动后移//关闭文件指针fclose(fp);//再次以只读的形式打开文件fp = fopen("./text.txt", "r"); //以只写的形式打开一个当前路径下的文件if(NULL == fp){printf("fopen error\n");return -1;}//定义变量用于读取数据char buf = 0;while(1){buf = fgetc(fp); //从fp中读取数据if(EOF == buf) //表示文件读取结束{break;}printf("%c\t", buf); //将读取的数据显示到终端}//关闭文件指针fclose(fp);return 0;
}
2.6 错误码
1> 错误码是调用内核提供的函数时,如果调用出错,那么内核空间会向用户空间反馈一个错误信息
2> 错误信息千奇百怪,并且是字符串内容,为了方便起见,给这些错误信息,进行编号
3> 如果,内核空间函数调用出问题时,只需要反馈错误编号即可,这个错误编号就叫做错误码
4> 常见错误码:可以通过指令 vi -t EIO 查看
5 #define EPERM 1 /* Operation not permitted */6 #define ENOENT 2 /* No such file or directory */7 #define ESRCH 3 /* No such process */8 #define EINTR 4 /* Interrupted system call */9 #define EIO 5 /* I/O error */10 #define ENXIO 6 /* No such device or address */11 #define E2BIG 7 /* Argument list too long */12 #define ENOEXEC 8 /* Exec format error */13 #define EBADF 9 /* Bad file number */14 #define ECHILD 10 /* No child processes */15 #define EAGAIN 11 /* Try again */16 #define ENOMEM 12 /* Out of memory */17 #define EACCES 13 /* Permission denied */18 #define EFAULT 14 /* Bad address */19 #define ENOTBLK 15 /* Block device required */20 #define EBUSY 16 /* Device or resource busy */21 #define EEXIST 17 /* File exists */22 #define EXDEV 18 /* Cross-device link */23 #define ENODEV 19 /* No such device */ 24 #define ENOTDIR 20 /* Not a directory */25 #define EISDIR 21 /* Is a directory */26 #define EINVAL 22 /* Invalid argument */27 #define ENFILE 23 /* File table overflow */28 #define EMFILE 24 /* Too many open files */29 #define ENOTTY 25 /* Not a typewriter */30 #define ETXTBSY 26 /* Text file busy */31 #define EFBIG 27 /* File too large */32 #define ENOSPC 28 /* No space left on device */33 #define ESPIPE 29 /* Illegal seek */34 #define EROFS 30 /* Read-only file system */35 #define EMLINK 31 /* Too many links */36 #define EPIPE 32 /* Broken pipe */37 #define EDOM 33 /* Math argument out of domain of func */38 #define ERANGE 34 /* Math result not representable */
5> 错误码的相关函数
1、strerror函数#include <string.h>char *strerror(int errnum);功能:将给定的错误码,转变成错误信息参数1:错误码返回值:错误码对应的错误信息的字符串错误码如何获得?答:需要加一个头文件:#include<errno.h>#include <errno.h>const char * const sys_errlist[];int sys_nerr;int errno; /* Not really declared this way; seeerrno(3) */2、perror函数#include <stdio.h>void perror(const char *s);功能:向标准出错缓冲区中,写入最新的错误码对应的信息参数:提示字符串,会自动提供一个冒号,并且输出结束后,会自动加一个换行返回值:无
2.7 fputs\fgets:字符串输入输出
#include <stdio.h>int fputs(const char *s, FILE *stream);功能:将给定的字符串,写入到文件中参数1:要写入的字符串起始地址参数2:打开的文件指针返回值:成功返回写入的字符个数(字符串长度),失败返回EOF#include <stdio.h>char *fgets(char *s, int size, FILE *stream);功能:从stream所指向的文件中,最多读取size-1的字符到s中,在读取过程中,如果遇到回车或者文件结束,会结束本次读取,并且会把回车也放入容器中。在后面自动加上'\0'参数1:存放数据的容器,一般是一个字符数组参数2:读取的大小参数3:文件指针返回值:成功返回容器的起始地址,失败返回NULL
例如:
#include<myhead.h>int main(int argc, const char *argv[])
{//定义文件指针FILE *fp = NULL;if((fp = fopen("./file.txt", "w")) == NULL){//strerror:将错误码转变成错误信息//printf("errno:%s\n", strerror(errno));perror("fopen error");return -1;}printf("open success\n");//向文件中写入字符串fputs("Hello world\n", fp);fputs("I love China\n", fp);fputs("good good study day day up\n", fp);//关闭文件fclose(fp);//以只读的形式重新打开文件if((fp = fopen("./file.txt", "r")) == NULL){perror("fopen error");return -1;}//循环读取数据char buf[128] = "";while(1){//清空容器bzero(buf, sizeof(buf));if( fgets(buf, sizeof(buf), fp) == NULL){break; //说明文件读取结束}//输出读取的数据printf("%s", buf); }printf("\n");//关闭文件fclose(fp);return 0;
}
2.8 缓冲区
1> 标准IO提供了三种缓冲区:行缓存、全缓存、不缓存
2> 行缓存:有关标准输入、标准输出指针对应的缓冲区,其大小位1024字节
3> 全缓存:有关普通文件指针对应的缓冲区,其大小位4096字节
4> 不缓存:有关标准出错文件指针对应的缓冲区,其大小位 0
#include<myhead.h>int main(int argc, const char *argv[])
{printf("行缓存的大小为:%ld\n", stdout->_IO_buf_end - stdout->_IO_buf_base);//0printf("行缓存的大小为:%ld\n", stdout->_IO_buf_end - stdout->_IO_buf_base);//1024getchar(); //使用一次标准输入printf("行缓存的大小为:%ld\n", stdin->_IO_buf_end - stdin->_IO_buf_base);//1024perror("usage"); //使用一次标准出错printf("不缓存的大小为:%ld\n", stderr->_IO_buf_end - stderr->_IO_buf_base);//0//验证全缓冲FILE *fp = NULL;if((fp = fopen("./tt.c", "w+")) == NULL){perror("fopen error");return -1;}fgetc(fp); //使用一次全缓冲printf("全缓存的大小为:%ld\n", fp->_IO_buf_end - fp->_IO_buf_base);//0//关闭fclose(fp);return 0;
}
5> 行缓存的刷新时机
课内练习:
1.使用fgetc完成,打开一个文件,统计该文件中的行数
解析:
#include<myhead.h>int main(int argc, const char *argv[])
{//判断传入的参数是否为2个if(argc != 2){printf("input file error!!!\n");printf("usage:./a.out fileName\n");return -1;}//表示传过来的有一个文件//定义文件指针FILE *fp = NULL;if((fp = fopen(argv[1], "r")) == NULL){printf("fopen error\n");return -1;}//说明文件已经打开char buf = 0; //接受字符int count = 0; //统计行数while(1){//从文件中读取一个字符buf = fgetc(fp);if(buf == EOF){break;}//判断是否读取到回车if(buf == '\n'){count ++;}}//输出行号printf("该文件一共有%d行\n", count);//关闭文件fclose(fp);return 0;
}
2.使用fgetc和fputc完成,cp指令的功能,实现两个文件的拷贝,将src文件中的内容,拷贝到dest文件中
#include<myhead.h>int main(int argc, const char *argv[])
{//判断传入的是否为三个文件if(argc != 3){printf("input file error\n");printf("usage:./a.out srcfile destfile\n");return -1;}//以只读的形式打开源文件FILE *sfp = NULL;if((sfp = fopen(argv[1], "r")) == NULL){printf("open src file error\n");return -1;}//以只写的形式打开目标文件FILE *dfp = NULL;if((dfp = fopen(argv[2], "w")) == NULL){printf("open destfile error\n");return -1;}//定义搬运工char buf = 0;//循环从源文件中读取数据放入到目标文件中while(buf = fgetc(sfp) != EOF){//将读取的数据写入目标文件中fputc(buf, dfp);}//关闭文件fclose(sfp);fclose(dfp);printf("拷贝成功\n");return 0;
}
课外作业:
1.使用fgets统计给定文件的行号
解析:
#include<myhead.h>
int main(int argc, char const *argv[])
{FILE *fp = NULL;int count=0;char buf[50];if(argc != 2){printf("intput file error\n");printf("usage: ./a.out xxx\n");return -1;}if((fp = fopen(argv[1],"r")) == NULL){perror("fopen error");return -1;}while(fgets(buf,sizeof(buf),fp) != NULL){count++;}fclose(fp);printf("有%d行\n",count);return 0;
}
2.使用fgets、fputs完成两个文件的拷贝
解析:
#include<myhead.h>
int main(int argc, char const *argv[])
{FILE *fp1 = NULL;FILE *fp2 = NULL;if(argc != 3){printf("intput file error\n");printf("usage: ./a.out xxx xxx\n");return -1;}if((fp1=fopen(argv[1],"r")) == NULL){perror("fopen error");return -1;}if((fp2=fopen(argv[2],"w")) == NULL){perror("fopen error");return -1;}char buf[50];while(fgets(buf,sizeof(buf),fp1) != NULL){fputs(buf,fp2);}fclose(fp1);fclose(fp2);return 0;
}