今天正式进入应用层的学习,那么梦开始的地方就是文件I/O,接下来几天的时间都会围绕这部分内容展开,主要内容如下:
三连即可提高学习效率
🧑🏻作者简介:一个学嵌入式的年轻人
✨联系方式:2201891280(QQ)
📔源码地址:https://gitee.com/xingleigao/study_qianrushi
⏳全文大约阅读时间: 120min
文章目录
文件基础
在Linux下,一切皆文件
概念: 一组相关数据的有序集合
文件类型:
- 常规文件 r
- 目录文件 d
- 字符设备文件 c(输入输出设备)
- 块设备文件 b(输入输出设备)
- 管道文件 p(进程通信)
- 套接字文件 s(网络编程)
- 符号链接文件 l(类似于快捷方式)
系统调用相关和库函数的关系
内核实现了硬件和软件的隔离。
标准I/O
介绍
标准I/O由ANSI C标准定义
主流操作系统上都实现了C库
标准I/O通过缓冲机制减少系统调用,实现更高的效率(比如我们熟知的printf就是行缓冲。如果不\n
就可能出错之前输出不了)
流
- FILE
标准IO用一个结构体类型来存放打开的文件的相关信息
标准I/O的所有操作都是围绕FILE来进行- 流(stream)
FILE又被称为流(stream)
文本流/二进制流windows和Linux文件的区别
Windows:
- 二进制流: 换行符: ‘\n’
- 文本流: 换行符 :‘\r’ ‘\n’
Linux
- 换行符 : ‘\n’
流的缓冲类型
- 全缓冲
当流的缓冲区无数据或无空间时才执行实际I/O操作- 行缓冲(printf scnaf)
当在输入和输出中遇到换行符(‘\n’)时,进行I/O操作
当流和一个终端关联时,典型的行缓冲- 无缓冲(ttl)
数据直接写入文件,流不进行缓冲
三个特殊流
名称 文件描述符 文件指针 标准输入流 0 STDIN_FILENO stdin 标准输出流 1 STDOUT_FILENO stdout 标准错误流 2 STDERR_FILENO stderr 错误流默认无缓冲,stdin和stdout是行缓冲。
可以用下面的代码看print的缓存大小:
#include <stdio.h> #include <stdlib.h> #define N 100 int main(int argc, const char *argv[]){ int i; char a = 'a'; if(argc != 2){ printf("Usage:%s Number\n",argv[0]); return 0; } for(i = 0; i < atoi(argv[1]);++i) printf("%c",a); while(1); return 0; }
打开和关闭流
打开文件
FILE *fopen (const char *path, const char *mode);
成功返回指针
关于mode函数
参数 含义 ”r"、“rb” 以只读方式文件,文件必须存在。 “r+”、“r+b” 以读写方式打开文件,文件必须存在 “w”、“wb” 以只写方式打开文件,若文件存在则文件长度清0。不存在则创建。 ”w+"、“w+b” 以读写方式打开文件,其他同"w" “a"、“ab” 以只写方式打开文件,若文件不存在创建;向文件写入的数据被追加到文件末尾。 “a+”、“a+b” 以读写方式打开文件。其他同"a"
新建文件权限
fopen() 创建的文件访问权限是0666(rw-rw-rw-)
Linux系统中umask设定会影响文件的访问权限,其规则为(0666 & ~umask)
Root用户是 022 普通用户是002
其实就是0666-022
用户可以通过umask函数或者命令修改相关设定
相关示例代码在:/io/1.stand_io/2.open_close/2.fopen.c
处理错误信息
extern int errno; void perror(const char *s); char *strerror(int errno);
- errno 存放错误号,由系统生成
- perror先输出字符串s,再输出错误号对应的错误信息
- strerror根据错误号返回对应的错误信息
相关示例代码在:/io/1.stand_io/2.open_close/2.perror.c
关闭文件流
int fclose(FILE *stream);
- fclose()调用成功返回0,失败返回EOF,并设置errno
- 流关闭时自动刷新缓冲中的数据并释放缓冲区
- 当一个程序正常终止时,所有打开的流都会被关闭。
- 流一旦关闭后就不能执行任何操作
相关示例代码在:/io/1.stand_io/2.open_close/2.close.c
程序中能够打开的文件或流的个数有限制,如何测试?
思路:循环打开流,成功则计数器累加,直到出错为止
示例代码/io/1.stand_io/2.open_close/2.fopen_num.c
每个程序默认有三个打开表项,就是stdin
、stdout
、stderror
三个文件
所以最多的就是1024-3 = 1021
流操作
字符的输入输出
字符的输入
#include <stdio.h> int fgetc(FILE *stream); int getc(FILE *stream); //宏 int getchar(void);
- 成功时返回读取的字符;若到文件末尾或出错时返回EOF
- getchar()等同于fgetc(stdin)
- getc和fgetc区别是一个是宏一个是函数
建议使用fgetc()
示例代码:#include <stdio.h> int main(){ char ch; ch = fgetc(stdin); printf("read char:%c\n",ch); return 0; }
如果是从文件读入就是:
#include <stdio.h> int main(){ char ch; FILE *fp; fp = fopen("1.txt","r"); if(fp != NULL){ ch = fgetc(fp); printf("read char:%c\n",ch); }else{ perror("open"); } return 0; }
字符的输出
#include <stdio.h> int fputc(int c, FILE *stream); int putc(int c, FILE *stream); int putchar(int c);
- 成功时返回写入的字符;出错时返回EOF
- putchar©等同于fputc(c, stdout)
相关示例代码在/io/1.stand_io/2.open_close
思考题:
如何利用fgetc / fputc实现文件的复制?
通过命令行参数传递源文件和目标文件名
通过fgetc返回值判断是否读到文件末尾
示例代码在io/1.stand_io/2.open_close/fgetc_fputc_copy.c
补充一个命令:diff 1.txt 2.txt
可以对比两个文件的差异。
行输入输出
字符的输入
#include <stdio.h> char *gets(char *s);//不要用!!!!! char *fgets(char *s, int size, FILE *stream);
成功时返回s,到文件末尾或出错时返回NULL
gets不推荐使用,容易造成缓冲区溢出
已经被废除!
遇到’\n’或已输入size-1
个字符时返回,总是包含’\0’
字符的输出
#include <stdio.h> int puts(const char *s); int fputs(const char *s, FILE *stream);
成功时返回输出的字符个数;出错时返回EOF
puts将缓冲区s中的字符串输出到stdout,并追加’\n’
fputs将缓冲区s中的字符串输出到stream,不追加 ‘\n’
对象的输入输出
#include <stdio.h> size_t fread(void *ptr, size_t size, size_t n, FILE *fp); size_t fwrite(const void *ptr, size_t size, size_t n, FILE *fp);
成功返回读写的对象个数;出错时返回EOF
既可以读写文本文件,也可以读写数据文件
效率高
相关的操作在/io/1.stand_io/2.open_close
刷新流和定位
刷新流
#include <stdio.h> int fflush(FILE *fp);
成功时返回0;出错时返回EOF
将流缓冲区中的数据写入实际的文件
Linux下只能刷新输出缓冲区,输入缓冲区丢弃
示例代码#include <stdio.h> int main(){ int i = 0; for(i = 0; i < 100; ++i){ printf("a"); fflush(stdout); } while(1); return 0; }
定位流
看下图,当进行写操作的时候,我们的指针会不断的往后走,如果我们想要回到b的位置进行重写,就是流定位。
#include <stdio.h> long ftell(FILE *stream); long fseek(FILE *stream, long offset, int whence); void rewind(FILE *stream);//回到文件头
ftell() 成功时返回流的当前读写位置,出错时返回EOF
fseek()定位一个流,成功时返回0,出错时返回EOF
whence参数:SEEK_SET/SEEK_CUR/SEEK_END
示例程序:#include <stdio.h> int main(){ FILE *fp; int st; char buf[16]; fp = fopen("1.txt","a+"); if(fp){ st = ftell(fp); printf("st:%d\n", st); fputs("abcd",fp); st = ftell(fp); printf("st:%d\n", st); //fseek(fp, 1, SEEK_SET); rewind(fp); fgets(buf, 5, fp); printf("buf:%s\n",buf); } return 0; }
判断流是否出错和结束
#include <stdio.h> int ferror(FILE *stream); int feof(FILE *stream);
- ferror()返回1表示流出错;否则返回0
- feof()返回1表示文件已到末尾;否则返回0
格式化输出和输入
格式化输出
#include <stdio.h> int printf(const char *fmt, …); int fprintf(FILE *stream, const char *fmt, …); int sprintf(char *s, const char *fmt, …);
成功时返回输出的字符个数;出错时返回EOF
使用起来很方便,强烈推荐!
示例代码详见:/io/1.stand_io/2.open_close/sprintf.c
格式化输入
int fscanf(FILE *stream, const char *format, ...); int sscanf(const char *str, const char *format, ...);
示例代码详见:
/io/1.stand_io/2.open_close/scanf.c
思考和练习
每隔1秒向文件test.txt中写入当前系统时间,格式如下:
1, 2014-10-15 15:16:42 2, 2014-10-15 15:16:43
该程序无限循环,直到按Ctrl-C中断程序
提示:
- time()用来获取系统时间(秒数)
- localtime()将系统时间转换成本地时间
- sleep()实现程序睡眠
示例代码:/io/1.stand_io/2.open_close/demo.c
写在最后
文件IO没特别多的东西,今天已经过了一大半了,明天我就会更新完,我尽量一天一更,大家和我一起变强呀!最后三连即可提高学习效率!!!
另外我在更新的就是算法笔记的一些例题笔记,这个系列是用于提高我的算法能力,如果有兴趣对算法领域感兴趣找不到合适的入门文章也可以追更,如果我更新的太慢了请大家点赞收藏,一键三连才能更有更新的动力呀0.0