C语言文件IO介绍
文件操作库函数的简单使用
C语言中的文件操作函数如下:
文件操作函数 | 功能 |
fopen | 打开文件 |
fclose | 关闭文件 |
fputc | 写入一个字符 |
fgetc | 读取一个字符 |
fputs | 写入一个字符串 |
fgets | 读取一个字符串 |
fprintf | 格式化写入数据 |
fcanf | 格式化读取数据 |
fwrite | 向二进制文件写入数据 |
fread | 从二进制文件读取数据 |
fseek | 设置文件指针的位置 |
ftell | 计算当前文件指针相对于起始位置的偏移量 |
rewind | 设置文件指针到文件的起始位置 |
ferror | 判断文件操作过程中是否发生错误 |
feof | 判断文件指针是否读取到文件末尾 |
先看一下C语言的两个库函数
size_t fwrite( const void *buffer, size_t size, size_t count, FILE *stream );// 写文件 size_t fread( void *buffer, size_t size, size_t count, FILE *stream );// 读文件
fread: 第一个参数是从读取数据放到这,第二个参数是一次读入多少个字节的大小的数据,第三个参数是最多读几次,第四个参数是从这个流读数据。返回值是代表这次实际读的次数。只适用于文件流
fwrite: 第一个参数是从buffer获得数据,第二个参数是一次写入多少个字节的大小的数据,第三个参数是最多写几次,第四个参数是写数据到这个流。返回值是代表这次实际写的次数。只适用于文件流。
对文件进行写入操作示例:
#include<stdio.h> #include<string.h> int main() { FILE* fp=fopen("log.txt","w"); if(fp==NULL) { perror("open file fail!\n"); exit(-1); } const char* msg="hello world!\n"; int cnt=5; while(cnt--) { fwrite(msg,strlen(msg),l,fp); } fclose(fp); return 0; }
运行结果:
对文件进行读取操作示例
#include <stdio.h> #include <string.h> int main() { FILE* fp = fopen("log.txt", "r"); if (fp == NULL){ perror("open file fail"); return 1; } char buf[256] = {0}; int ret = 0; while ((ret = fread(buf, 1, 13, fp))){ printf("%s", buf); } fclose(fp); return 0; }
运行结果
stdin&stdout&stderr
Linux下一切皆文件,硬件设备也是被当做文件看待的,也就是说这些硬件设备也是可以通过IO打开的,并且进行读写。那他们是怎么操作的?一般来说,C语言程序运行起来,都会默认打开3个流,分别是:
- stdin:标准输入流(键盘)
- stdout:标准输出流(显示器)
- stderr:标准错误流(显示器)
查看man手册我们就可以发现,stdin,stdout以及stderr这三个家伙实际上FILE*类型的。
extern FILE* stdin; extern FILE* stdout; extern FILE* stderr;
我们不需要考虑要打开键盘和屏幕这些流。这也是为什么我在打印数据到屏幕或从键盘上输入数据时,即使我们没有打开这些流,我们也可以执行这些操作的原因。
将文件操作中写文件的库函数传参进行更改,我们选择不传文件,而是传一个stdout,因为它的类型是FILE*,因此我们也可以把数据写到屏幕上。
#include <stdio.h> #include <string.h> int main() { FILE* fp = fopen("log.txt", "w"); if (fp == NULL){ perror("open file fail"); return -1; } const char* msg = "hello world!\n"; int count = 5; while (count--){ fwrite(msg, strlen(msg), 1, stdout); } fclose(fp); return 0; }
运行结果如下所示:
总结:不止C语言当中有标准输入流,标准输出流和标准错误流,C++当中也有对应的cin,cout和cerr,其他语言当中都有类似的概念。实际上这种特性并不是某种语言所特有的,而是由操作系统所支持的。
系统文件I/O
操作系统除了C语言接口,C++接口或是其他语言接口外,操作系统也有一套系统接口来进行文件的访问。
相比于C库函数或其他语言的库函数而言,系统调用接口更贴近底层,实际上这些语言的库函数都是对系统接口进行了封装。
我们在Linux平台下运行C代码时,C库函数就是对Linux系统调用接口进行的封装,在Windows平台下运行C代码时,C库函数就是对Windows系统调用接口进行的封装,这样做使得语言有了跨平台性,也方便进行二次开发。
系统调用接口的介绍
open
作用:打开一个文件
函数原型:
1. int open(const char* pathname,int flags); 2. int open(const char* pathname,int flags,mode_t mode);
参数介绍:
- pathname:要打开或创建的目标文件的路径名
- flags:打开文件时,可以传入多个参数,用传入的参数进行或运算,得出flags
- O_RDONLY:只读打开
- O_WRONLY:只写打开
- O_RDWR:读,写打开这三个常量,必须指定一个且只能指定一个
- O_CREAT:若文件存在,则创建它。需要使用mode选项,来指明新文件的访问权限。
- O_APPEND:追加写
说明:这里的每一个选项都只有一个比特位是为1的,其余都是0,所以将这些选项组合就是对这些选项进行或运算,然后传入flags.
- mode:文件权限。在新文件被创建时,参数mode具体指明了使用权限。他通常也会被umask修改。所以一般新建文件的权限为(mode&~umask).
- 返回值:
- 成功:返回新的文件描述符
- 失败:-1
实例演示:open函数的使用,研究函数的返回值
#include <stdio.h> #include <stdlib.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #include <string.h> int main() { int fd1 = open("log.txt1", O_RDONLY|O_CREAT, 0664); int fd2 = open("log.txt2", O_RDONLY|O_CREAT, 0664); int fd3 = open("log.txt3", O_RDONLY|O_CREAT, 0664); int fd4 = open("log.txt4", O_RDONLY|O_CREAT, 0664); int fd5 = open("log.txt5", O_RDONLY|O_CREAT, 0664); printf("fd1:%d\n", fd1); printf("fd2:%d\n", fd2); printf("fd3:%d\n", fd3); printf("fd4:%d\n", fd4); printf("fd5:%d\n", fd5); return 0; }
运行结果如下:
观察运行结果,返回值fd是从3开始分配,且是递增的,大家看到这一串数字很容易想到数组下标。但0,1,2跑哪去了?其实在Linux下,进程会默认把3个文件描述符分配(0,1和2)给标准输入,标准输出和标准错误,所以,后序如果打开文件,文件描述符就是从3开始分配的。
close
作用:关闭文件
函数原型:
int close(int fd);
使用close函数传入需要关闭文件的文件描述符即可,若关闭文件成功则返回0,若关闭文件失败则返回-1。
write
函数原型:
ssize_t write(int fildes,const void* buf,size_t nbyte);
函数参数:
- fd:在文件描述符为fd的文件中进行写入
- buf:从buf位置开始读取数据
- nbyte:从buf位置开始读取nbyte个字节到文件中
- 函数返回值:
- 成功:返回实际写入数据的字节数
- 失败:返回-1
实例演示:
#include <stdio.h> #include <stdlib.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #include <string.h> int main() { int fd = open("log.txt", O_WRONLY|O_CREAT, 0664); char buf[15] = "hello world\n"; write(fd, buf, sizeof(buf)/sizeof(buf[0])); close(fd); return 0; }
代码运行结果如下:
read
作用:读文件
函数原型:
ssize_t read(int fd,void* buf,size_t count);
函数参数:
- fd:在文件描述符为fd的文件中开始读
- buf:把读得内容从buf的位置开始存放
- count:从buf位置开始存放count个字节
函数返回值:
- 成功:返回实际读取数据的字节数
- 失败;返回-1
实例演示:f
#include <stdio.h> #include <string.h> #include <unistd.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> int main() { int fd = open("log.txt", O_RDONLY); if (fd < 0){ perror("open"); return 1; } char ch; while (1){ ssize_t s = read(fd, &ch, 1); if (s <= 0){ break; } write(1, &ch, 1); //向文件描述符为1的文件写入数据,即向显示器写入数据 } close(fd); return 0; }
运行结果如下所示: