“这是我参与8月更文挑战的第13天,活动详情查看:8月更文挑战”
标准IO
标准IO的定义
标准IO 是由标准c库提供的API接口,是在文件IO的基础上封装出来的函数接口。
📌第一个原因是为了增加可移植性。并且,标准IO在文件IO的基础上封装了一个叫缓冲区的东西。提高内核的效率。用来存放一些不是很着急的数据,等到缓冲区满,调用一次文件IO完成写入工作或者读取工作。
有些数据是特别着急的数据比如说:下发的命令,实时的监控数据呀 必须使用文件IO 。
伪代码: fopen(参数) { if(is win) { _open(参数); }esle if (is linux) { open(参数); } }
文件流指针
文件流指针:是一个文件的标志,是在文件描述符上封装出来的一个结构体指针,缓冲区就在这个流指针中。
流:流指针----文件流指针---这个流
追代码
安装一个索引搜索文件 tags (搜索引擎)
第一步:创建目标目录的引擎文件--tags (想搜索哪个目录就在哪个目录下创建)
ctags -R //生成一个引擎文件叫tags 它里面就包含目录的信息
第二步:搜索
vi -t + 目标字符串 例如:vi -t FILE
第三步:选择准确的目录下的文件
第四步:翻阅文档 --代码跳转
追代码:光标点击想要跳转的字符串,ctrl + ] 进项跳转搜索
返回上一次:ctrl +t
设置全局tags
编辑家目录的vim配置文件**.vimrc** 在末尾加上tags的路径如下:
set tags+=/user/include/tags
标准io函数接口
1.fopen
-
原型:
FILE *fopen(const char *pathname, const char *mode); -
功能:打开一个文件
-
参数:
pathname:要打开的文件的路径及名称
mode :打开文件的方式
具体解释 open() flag r 以只读的方式打开一个文件,文件必须存在 O_RDONLY r+ 以读写的方式打开这个文件,文件必须存在 O_RDWR w 如果文件存在则清空写入,如果文件不存在则创建写入 O_WRONLY|O_CREAT|O_TRUNC w+ 如果文件存在则清空读写,如果文件不存在则创建读写 O_RDWR|O_CREAT|O_TRUNC a 如果文件存在则追加写入,如果文件不存在则先创建在写入 O_WEONLY|O_CREAT|O_APPEND a+ 如果文件不存在则创建文件并追加写入和读取。 读取时从文件开头开始读取,写入时从文件的末尾开始写入 lseek偏移后不能进行写入操作,如果进入写入操作,则追加在末尾写入(不管写之前进行了如何偏移) O_RDWR|O_CREAT|O_APPEND 注意:有可能以后会遇到 rb+ ,b代表执行二进制操作,linux下无区分
-
返回值:
成功会返回一个文件的流指针
失败返回NULL
-
代码:
#include <stdio.h>
int main(int argc, const char *argv[])
{
//打开一个文件需要定义一个文件流指针来指向fopen返回的已经封装好的文件流指针
//FILE * fp = fopen("./1.txt","r");
FILE * fp = fopen("./1.txt","w");
if (NULL == fp)
{
perror("fopen");
return -1;
}
printf("打开文件是成功的\n");
return 0;
}
2.fclose
-
原型:
int fclose(FILE *stream); -
功能:关闭一个文件流指针//原因:文件流指针里也绑定了一个文件的描述符
-
参数:目标文件流指针
-
返回值:
成功返回:
0失败返回:
EOF == -1
3.fgetc
-
原型:int fgetc(FILE *stream);
-
功能:从指定文件流指针中获取一个字符
-
参数:目标流指针
-
返回值:
成功返回 获取到的字符转化成的int类型的数据
失败返回 EOF 到达文件末尾 EOF
📢注意:获取完,指针会后移一位
int buf = fgetc(fp);
printf("%c\n",buf);
printf("%d\n",sizeof(buf));
linux@ubuntu:~/test/IO/test$ ./a.out
h
4
4.feof
-
原型:nt feof(FILE *stream);
-
功能:判断是否到达了文件的末尾
-
参数:目标文件流指针
-
返回值:
如果到达了文件的末尾返回一个非0值
其他情况返回0
5.ferror
-
原型:int ferror(FILE *stream);
-
功能:判断文件操作是否出错
-
参数:目标文件流指针
-
返回值:
如果文件操作失败了,返回一个非0值
其他情况返回0
-
代码:
#include <stdio.h>
int main(int argc, const char *argv[])
{
//打开一个文件以r+方式
FILE * fp = fopen("./1.txt","r");
if (NULL == fp)
{
perror("fopen");
return -1;
}
//开始做循环的读取
while(1)
{
int ret = fgetc(fp);
if (EOF == ret)
{
//到达了文件的末尾或者说函数失败
if (feof(fp))
{
printf("到达了文件的末尾\n");
break;
}else{
printf("获取失败\n");\
return -1;
}
}
printf("获取到的数据为%d---所对应的字符为%c\n",ret,ret);
}
if( fclose(fp) == EOF)
{
printf("关闭文件失败\n");
return -1;
}
return 0;
}
linux@ubuntu:~/test/IO/test$ ./a.out
获取到的数据为104---所对应的字符为h
获取到的数据为101---所对应的字符为e
获取到的数据为108---所对应的字符为l
获取到的数据为108---所对应的字符为l
获取到的数据为111---所对应的字符为o
获取到的数据为32---所对应的字符为
获取到的数据为119---所对应的字符为w
获取到的数据为111---所对应的字符为o
获取到的数据为114---所对应的字符为r
获取到的数据为108---所对应的字符为l
获取到的数据为100---所对应的字符为d
获取到的数据为10---所对应的字符为
//vim的'\n'
到达了文件的末尾
6.fputc
-
原型:
int fputc(int c, FILE *stream); -
功能:向指定的一个文件中输出一个字符
-
参数:
① c:想要写入的字符,将int类型的数据转换成unsignedchar类型的数据写入
② stream :目标文件流指针
-
返回值:
成功返回:写入的字符转换成的int类型的数据
失败返回:EOF
-
代码:
#include <stdio.h>
int main(int argc, const char *argv[])
{
//打开文件
FILE * fp = fopen("./1.txt","w");
if (NULL == fp)
{
perror("fopen");
return -1;
}
//开始像文件中写入数据
char buf[123] = "hello world";
int i = 0;
while(1)
{
fputc(buf[i],fp);
if ((strlen(buf) -1 ) == i)
{
break;
}
i++;//遍历数组使用
}
fclose(fp);
return 0;
}
练习:用fgetc 和fputc去完成文件的复制粘贴
7.fgets
-
原型:
char *fgets(char *s, int size, FILE *stream); -
功能:行读取
-
参数:
① s:读取到的数据存放的地址
② size:读取的字节个数
1.当\n之前的字节个数小于size,遇到换行符结束读取,但是换行符也会被当做一个字符被读到内存之中,并且会在最后一个字符后添加'\0'.
2.当\n之前的字节个数大于size,会读到size个大小的字节时停止读取。会在末尾加上一个'\0'。会将最后一个字节的字符给覆盖掉。机制会在读取完之后读写指针向前偏移一个字节。
hello, world
size = 5 : fgets---> hello, 紧接着继续size = 5:fgets--->, wor,
//这里的覆盖就是这个意思,会把o 覆盖
③stream:文件流指针
4. 返回值:
成功返回:读到的字符串的首地址
失败返回:NULL
读到文件末尾:返回NULL
5. 代码:
#include <stdio.h>
int main(int argc, const char *argv[])
{
FILE * fp = fopen("./1.txt","r");
if (NULL ==fp)
{
perror("fopen");
return -1;
}
//行读取
while(1)
{
char buf[123] = {0};
if (fgets(buf,11,fp) == NULL)
{
//失败或者读到了文件的末尾
if (feof(fp))
{
printf("读到了文件的末尾\n");
break;
}else{
perror("fgets");
return -1;
}
}
printf("buf = %s\n",buf);
}
return 0;
}
if (fgets(buf,11,fp) == NULL)
linux@ubuntu:~/test/IO/test$ ./a.out
buf = hello worl //hello wor'\0'd
buf = d //d'\n''\0'
读到了文件的末尾
if (fgets(buf,12,fp) == NULL)
linux@ubuntu:~/test/IO/test$ ./a.out
buf = hello world //hello world'\0'
buf = //'\n''\0'
读到了文件的末尾
if (fgets(buf,13,fp) == NULL)
linux@ubuntu:~/test/IO/test$ ./a.out
buf = hello world 、//hello world'\n''\0'
读到了文件的末尾
8.fputs
-
功能:向一个文件中进行字符串输入
-
原型:
int fputs(const char *s, FILE *stream);📢注意:遇到'\0'停止
-
参数:
① s:想要写入的数据的地址
② stream:想要写入哪个文件
-
返回值:
成功返回一个非负数
失败返回EOF(-1)
-
代码:
#include <stdio.h>
int main(int argc, const char *argv[])
{
FILE * fp = fopen("./1.txt","w");
if (NULL ==fp)
{
perror("fopen");
return -1;
}
//找到一个想要写入的数据的地址
char buf[123] = "hello world";
if (EOF == fputs(buf,fp))
{
perror("fputs");
return -1;
}
return 0;
}
📝练习:请编写一个代码,用来测试文件的行数。
代码:
#include <stdio.h>
#include <string.h>
int main(int argc, const char *argv[])
{
if (argc != 2)
{
printf("参数输入错误\n");
return -1;
}
//打开文件,以只读的方式打开文件
FILE * fp = fopen(argv[1],"r");
if (NULL == fp)
{
perror("fopen");
return -1;
}
//开始计算
int count = 0;//用作计数使用
while(1)
{
char buf[123] = {0};
if (NULL == fgets(buf,sizeof(buf),fp))
{
//出错或者到了文件的末尾
if (feof(fp))
{
printf("到达了文件的末尾\n");
break;
}else{
perror("fgets");
return -1;
}
}
if (buf[strlen(buf) -1] == '\n')
{
count++;
}
}
printf("该文件行数为%d\n",count);
return 0;
}
linux@ubuntu:~/test/IO/test$ ./a.out 1.txt
到达了文件的末尾
该文件行数为0
linux@ubuntu:~/test/IO/test$ vi 1.txt
//这里我进去vim编辑器输入之后再删除,文件行数会为1
linux@ubuntu:~/test/IO/test$ ./a.out 1.txt
到达了文件的末尾
该文件行数为1
9.fread
-
原型:
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream); -
功能:二进制读取文件
-
参数:
① ptr :存放读到的数据的地址
② size:对象的大小
③ nmemb:对象的个数 📢注意:总的读取的字节个数为 对象的大小*对象的个数
④ stream:目标文件流指针
-
返回值:
成功返回读到的对象的个数
失败返回一个 0
10.fwrite
-
原型:size_t fwrite(const void *ptr, size_t size, size_t nmemb,FILE *stream);
-
功能:二进制写入文件
-
参数:
① ptr :想要写入的数据的首地址
② size:对象的大小
③ nmemb :对象的个数
④ stream :目标文件流指针
-
返回值:
成功返回写入的对象的大小
失败返回一个小的对象个数
-
代码:
#include <stdio.h>
#include <string.h>
//定义一个信息结构体
struct age
{
char name[20];
char sex;
int age;
char phone[12];
};
struct age myage1;//定义一个录入时使用的结构体
struct age myage2;//定义一个读取时使用的结构体
int main(int argc, const char *argv[])
{
STAT:
//搭建界面
printf("************************************\n");
printf("**1.录入信息****2.读取信息*****3.退出**\n");
printf("************************************\n");
//用户开始输入命令
printf("input >> ");
int input = 0;
scanf("%d",&input);
if(input >3 || input <= 0)
{
goto STAT;
}
//开始写入功能
if (1 == input)
{
//录入信息
printf("请输入姓名\n");
scanf("%s",myage1.name);
getchar();
printf("请输入性别\n");
scanf("%c",&myage1.sex);
getchar();
printf("请输入年龄\n");
scanf("%d",&myage1.age);
printf("请输入手机号\n");
scanf("%s",myage1.phone);
getchar();
//开始写入文件-------------------------------
FILE *fp = fopen("./.1.txt","a");
if (NULL == fp)
{
perror("fopen");
return -1;
}
//开始写入
if(1 != fwrite(&myage1,sizeof(myage1),1,fp))
{
perror("fwrite");
return -1;
}
fclose(fp);
printf("写入信息完成,正在跳转到菜单\n");
goto STAT;
}else if (2 == input)
{
char username[23] = {0};
//读取信息
printf("请输入要查询人的姓名\n");
scanf("%s",username);
getchar();
//开始对文件进行读取并且匹配
FILE *fp = fopen("./.1.txt","r");
if (NULL == fp)
{
perror("fopen");
return -1;
}
while(1)//循环读取
{
if(0 == fread(&myage2,sizeof(myage2),1,fp))
{
if (feof(fp))
{
printf("读到了文件的末尾\n");
break;
}else{
perror("fread");
return -1;
}
}
//进行name匹配
if (strcmp(username,myage2.name) == 0 )
{
printf("姓名为%s\n",myage2.name);
printf("性别为%c\n",myage2.sex);
printf("年龄为%d\n",myage2.age);
printf("手机号为%s\n",myage2.phone);
}
}
}else {
//退出
return 0;
}
return 0;
}
11.fseek
-
原型:
int fseek(FILE *stream, long offset, int whence); -
功能:读写指针的偏移
-
参数:
① stream :目标文件流指针
② offset:
该数为负数,向前进行偏移,如果偏移出了文件的开头,会报错返回如果
该数为正数,向后进行偏移,如果偏移出了文件的末尾,会扩大文件,用'\0'来做填充。那么此类的文件被称为 空洞文件
📢注意:如果偏移后没有对其进行任何写入操作,内核认为该偏移无效,不会扩大文件大小。
③ whence:基准位置-----根据哪一个位置进行偏移
SEEK_SET(0): 根据文件开头进行偏移 SEEK_CUR(1): 根据用户当前位置进行偏移 SEEK_END(2): 根据文件末尾进行偏移
-
返回值:
成功返回 0
失败返回 -1
-
代码:
#include <stdio.h>
char ch = 0;
int main(int argc, const char *argv[])
{
//打开一个文件
FILE *fp = fopen("./1.txt","r+");
if (NULL ==fp)
{
perror("fopen");
return -1;
}
//文件中的数据为hello world
//从末尾向后偏移--空洞文件
fseek(fp,2000,SEEK_END);
fputc('a',fp);
#if 0
//开始进行指针偏移
ch = fgetc(fp);
printf("ch = %c \n",ch);
fseek(fp,1,SEEK_CUR);//从当前位置向后偏移一个字节
ch = fgetc(fp);
printf("ch = %c \n",ch);//--->l
fseek(fp,-1 ,SEEK_END);
ch = fgetc(fp);
printf("ch = %c \n",ch);//--->d
#endif
return 0;
}
例如:
1.txt文件
📢注意:d不是文件的末尾,‘\n’才是
fseek(fp,1,SEEK_SET);//从当文件开头向后偏移一个字节
ch = fgetc(fp);
printf("ch = %c \n",ch);
linux@ubuntu:~/test/IO/fseek$ ./a.out
ch = e
fseek(fp,10,SEEK_SET);
linux@ubuntu:~/test/IO/fseek$ ./a.out
ch = d
fseek(fp,11,SEEK_SET);
linux@ubuntu:~/test/IO/fseek$ ./a.out
ch =
//📢注意这里有个空行,因为vim编辑器会自动加一个'\n'
fseek(fp,12,SEEK_SET);
linux@ubuntu:~/test/IO/fseek$ ./a.out
ch = �
fseek(fp,13,SEEK_SET);
linux@ubuntu:~/test/IO/fseek$ ./a.out
ch = �
fseek(fp,1,SEEK_CUR);//从当前位置向后偏移一个字节
ch = fgetc(fp);
printf("ch = %c \n",ch);
linux@ubuntu:~/test/IO/fseek$ ./a.out
ch = e
fseek(fp,0 ,SEEK_END);
ch = fgetc(fp);
printf("ch = %c \n",ch);
linux@ubuntu:~/test/IO/fseek$ ./a.out
ch = �
fseek(fp,-1 ,SEEK_END);
ch = fgetc(fp);
printf("ch = %c \n",ch);
linux@ubuntu:~/test/IO/fseek$ ./a.out
ch =
fseek(fp,-2 ,SEEK_END);
ch = fgetc(fp);
printf("ch = %c \n",ch);
linux@ubuntu:~/test/IO/fseek$ ./a.out
ch = d
12.ftell
-
原型:
long ftell(FILE *stream); -
功能:获取当前读写指针的偏移量
-
参数:目标文件流指针
-
返回值:
成功返回偏移量(是根据文件开头来计算的)
失败返回-1
-
使用场景:通常与
fseek(fp,0,SEEK_END)来配合使用测量文件当前的大小 -
代码:
#include <stdio.h>
int main(int argc, const char *argv[])
{
FILE *fp = fopen("./1.txt","r+");
if (NULL ==fp)
{
perror("fopen");
return -1;
}
//从末尾向后偏移--空洞文件
fseek(fp,2000,SEEK_END);
printf("当前的偏移量为%ld\n",ftell(fp));
fputc('a',fp);
printf("当前的偏移量为%ld\n",ftell(fp));
return 0;
}
linux@ubuntu:~/test/IO/test$ ./a.out
当前的偏移量为2006
当前的偏移量为2007
linux@ubuntu:~/test/IO/test$ ./a.out
当前的偏移量为4007
当前的偏移量为4008
linux@ubuntu:~/test/IO/test$ ./a.out
当前的偏移量为6008
当前的偏移量为6009
13.rewind
- 原型: void rewind(FILE *stream);
- 功能:直接将文件读写指针偏移到文件开头 类似 :fseek(stream, 0L, SEEK_SET)
- 参数:目标文件流指针
- 返回值:无
- 代码:
rewind(fp);
printf("当前的偏移量为%ld\n",ftell(fp));
14.sprintf
-
原型:int sprintf(char *str, const char *format, ...);
-
功能:向一个固定的地址中存放字符串(一般用于字符串的拼接)
-
参数:
① str :要存放格式化完成的字符串的地址
② format:格式化字符串{固定的字符串和占位符}
③ ... :可变参数(一般放置变量)
-
返回值:成功返回输出的字节个数 失败返回负数
-
代码:
#include <stdio.h>
int main(int argc, const char *argv[])
{
//name age sex phone
char name[20]="zhangsan";
int age = 18;
char sex = 'w';
char phone[12] = "12345678922";
char buf[123] = {0};
//开始拼接
sprintf(buf,"name:%s--age:%d--sex:%c--phone:%s",name,age,sex,phone);
printf("buf = %s \n",buf);
return 0;
}
linux@ubuntu:~/test/IO/test$ ./a.out
buf = name:zhangsan--age:18--sex:w--phone:12345678922
15.snprintf
-
原型:int snprintf(char *str, size_t size, const char *format, ...);
-
功能:按照固定的大小去格式化字符串输出到字符地址中
-
参数:
① size :大小 (规定要写入str这片地址中的字节大小)
② str :要存放格式化完成的字符串的地址
③ format:格式化字符串{固定的字符串和占位符}
④ ... :可变参数(一般放置变量)
-
返回值:
成功返回:返回目标字符串的总大小
失败返回 :负数
16.fprintf
-
原型:
int fprintf(FILE *stream, const char *format, ...); -
功能:格式化输出字符串到文件中(一般用于书写日志文件)
-
参数:
① stream:目标文件流指针
② str :要存放格式化完成的字符串的地址
③ format:格式化字符串{固定的字符串和占位符}
④ ... :可变参数(一般放置变量)
-
返回值:
成功返回:返回输出的字节大小
失败返回:负数
-
代码:
#include <stdio.h>
int main(int argc, const char *argv[])
{
//name age sex phone
char name[20]="zhangsan";
int age = 18;
char sex = 'w';
char phone[12] = "12345678922";
//打开文件
FILE * fp = fopen("./1.txt","w");
if(NULL == fp)
{
perror("fopen");
return -1;
}
fprintf(fp,"name:%s--age:%d--sex:%c--phone:%s",name,age,sex,phone);
return 0;
}
写简单的日志文件
#include <stdio.h>
#include <time.h>
int main(int argc, const char *argv[])
{
//打开一个文件
FILE *fp = fopen("./1.txt","a");
if(NULL == fp)
{
perror("fopen");
return -1;
}
while(1)
{
//获取当前的距离1970年的秒数
time_t mytime = time(NULL);
//开始转换时间
struct tm * mytm = localtime(&mytime);
fprintf(fp,"当前的时间为:%d年-%d月-%d日--%d:%d:%d\n",
mytm->tm_year+1900,mytm->tm_mon+1,mytm->tm_mday,
mytm->tm_hour,mytm->tm_min,mytm->tm_sec);
fflush(fp);
sleep(1);
}
return 0;
}
tail -f 1.txt
linux@ubuntu:~/test/test$ tail -f 1.txt
当前的时间为:2021年-8月-25日--15:47:5
当前的时间为:2021年-8月-25日--15:47:6
当前的时间为:2021年-8月-25日--15:47:7
当前的时间为:2021年-8月-25日--15:47:8
当前的时间为:2021年-8月-25日--15:47:9