一.笔记
一、IO基础
1> IO:(inout output) 所谓IO,就是程序与外部设备进行信息交换的过程
2> IO的分类:标准IO和文件IO
3> 标准IO:调用封装好的相关库函数,来实现数据的输入输出
4> 文件IO:调用系统(内核)提供的相关函数,来实现数据的输入输出
5> 标准IO和文件IO的区别:
标准I | 文件IO | |
属于 | 库函数 | 系统调用 |
操作对象 | 文件指针 | 文件描述符 |
有无缓冲区 | 有 | 无 |
6>阻塞IO和非阻塞IO
7> 目前所接触过的IO函数:
printf、scanf、putchar、getchar、puts、gets
8> 常用的IO接口函数
标准IO:fprint、fscanf、fputc、fgetc、fputs、fgets、fread、 fwrite、fopen、fclose、fseek、ftell、rewind。。。
文件IO:open、close、read、write、seek。。。
9> IO操作流程
二、标准IO
2.1 标准IO提供的内容
可以通过指令:man 7 stdio.h 可以查看该头文件提供的内容
Plain Text
自动换行
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.2FILE结构体
1> 如何追该结构体:可以使用 vi -t FILE
2> 结构体解析
struct _IO_FILE {
char* _IO_buf_base; /* 缓冲区开始地址 */
char* _IO_buf_end; /* 缓冲区结束地址 */
int _fileno; //用于系统调用的文件描述符
};
3> 当一个程序启动后,系统会自动打开三个文件指针:
stdin:标准输入文件指针 ---> scanf、getchar、gets
stdout:标准输出文件指针 ----> printf、putchar、puts
stderr:标准出错文件指针 ---> perror
以上三个文件指针,都是针对于终端文件操作而言的
2.3 fopen 打开文件
#include <stdio.h>
FILE *fopen(const char *pathname, const char *mode);
功能:以指定的方式打开一个给定的文件,并返回该文件的文件地址
参数1:要打开的文件文件路径,可以是相对路径,也可以是绝对路径
参数2:文件打开的方式,需要以以下字符开头的字符串
r 以只读的形式打开文件,文件光标定位在开头.如果文件不存在,则报错
r+ 以读写的形式打开文件,文件光标定位在开头.如果文件不存在,则报错
w 以只写的形式打开文件,如果文件存在,则清空文件内容,如果文件不存在,则创建该文件,文件光标定位在开头.
w+ 以读写的形式打开文件,如果文件存在,则清空文件内容,如果文件不存在,则创建该文件,文件光标定位在开头.
a 以追加的形式打开文件,如果文件不存在则创建文件,文件光标定位在结尾
a+ 以读或者追加的形式打开文件,如果文件不存在,则创建文件,如果第一次是读数据,则光标定位在开头,否则定位在结尾
返回值:成功调用返回打开的文件地址,失败返回NULL,并置位错误码
2.4 fclose:关闭文件
#include <stdio.h>
int fclose(FILE *stream);
功能:关闭给定的文件指针
参数1:要关闭的文件指针
返回值:成功返回0,失败返回EOF,并置位错误码
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并置位错误码
2.6 有关错误码问题
1> 错误码是调用内核提供的函数时,如果调用出错,那么内核空间会向用户空间反馈一个错误信息
2> 错误信息千奇百怪,并且是字符串内容,为了方便起见,给这些错误信息,进行编号
3> 如果,内核空间函数调用出问题时,只需要反馈错误编号即可,这个错误编号就叫做错误码
4> 常见错误码:可以通过指令 vi -t EIO 查看
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
2.8 关于缓冲区
1> 标准IO提供了三种缓冲区:行缓存、全缓存、不缓存
2> 行缓存:有关标准输入、标准输出指针对应的缓冲区,其大小位1024字节
3> 全缓存:有关普通文件指针对应的缓冲区,其大小位4096字节
4> 不缓存:有关标准出错文件指针对应的缓冲区,其大小位 0
5> 行缓存的刷新时机
1.换行会刷新行缓存
2.程序结束后,会自动刷新行缓存
3.当文件指针关闭后,会刷新行缓存
4.当使用fflush函数刷新文件指针时,会刷新行缓存
5.当输入输出切换时,会刷新行缓存
6.当缓存区满了后,再放数据时,会刷新行缓存
6> 全缓存的刷新时机
1.程序结束后,会自动刷新全缓存
2.当文件指针关闭后,会刷新全缓存
3.当使用fflush函数刷新文件指针时,会刷新全缓存
4.当输入输出切换时,会刷新全缓存
5.当缓存区满了后,再放数据时,会刷新全缓存
二.作业
1> 使用fgets统计给定文件的行号
#include <myhead.h>
int main(int argc, char const *argv[])
{
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[128]="";
int count=0;
while (1)
{
bzero(buf,sizeof(buf));
if(fgets(buf,strlen(buf)-1,fp)==NULL)
{
count++;
}
if (fgets(buf,sizeof(buf),fp)==NULL)
{
break;//说明文件读取结束
}
}
printf("该文件一共有%d行\n",count);
fclose(fp);
return 0;
}
2> 使用fgets、fputs完成两个文件的拷贝
#include <myhead.h>
int main(int argc, char const *argv[])
{
if (argc!=3)
{
printf("input file error!!!\n");
printf("usage:./a.out filename\n");
return -1;
}
FILE*fp1=NULL;
FILE*fp2=NULL;
if (NULL==(fp1=fopen(argv[1],"r"))||NULL==(fp2=fopen(argv[2],"w")))
{
printf("拷贝失败\n");
return -1;
}
char buf[128]="";
while (1)
{
bzero(buf,sizeof(buf));
fgets(buf,strlen(buf),fp1);
if (fgets(buf,sizeof(buf),fp1)==NULL)
{
break;//说明文件读取结束
}
fputs(buf,fp2);
}
fclose(fp1);
fclose(fp2);
printf("拷贝成功\n");
return 0;
}