本文中我们将讲述如下的内容,C文件中IO相关操作,认识文件相关系统调用接口。
首先我们来通过一些简单的代码简要的回顾C文件接口:
// 写文件
#include
#include
int main()
{
FILE *fp = fopen("myfile", "w");
if(!fp){
printf("fopen error!\n");
}
const char *msg = "hello world!\n";
int count = 5;
while(count--){
fwrite(msg, strlen(msg), 1, fp);
}
fclose(fp);
return 0;
}
// 读文件
#include
#include
int main()
{
FILE *fp = fopen("myfile", "r");
if(!fp){
printf("fopen error!\n");
}
char buf[1024];
const char *msg = "hello world!\n";
while(1){
//注意返回值和参数,此处有坑,仔细查看man手册关于该函数的说明
ssize_t s = fread(buf, 1, strlen(msg), fp);
if(s > 0){
buf[s] = 0;
printf("%s", buf);
}
if(feof(fp)){
break;
}
}
fclose(fp);
return 0;
}
打开文件的方式还有很多比如"w"、"r"、"a"、"w+"...可以通过不同的方式来对文件进行处理。
上述的代码都是语言级别的方案,下面我们将要来介绍一些系统方案:
首先看一下打开文件的方法open和它的返回值:
#include
#include
#include
int open(const char *pathname, int flags);
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
可以看到open函数会返回一个被称为文件描述符的整数值,下面就来演示一下open函数的用法,这个函数的第一个参数表示,文件的路径与文件名, 第二个参数flags有很多的选项,在讲述这个问题之前,首先要了解一些OS一般如何让用户给自己传递标志位。
在我们自己编写的程序中,一般我们会给函数传递不同的参数来表示不同的含义,例如 int func(int flag) // flag = 1、2、3,当我们想要传递多个标志位的时候,在函数中不可能传入很多个形参,这非常的不便捷。因为一个整数有四个字节,32个比特位,系统就会让一个或几个比特位表示一个标志位,这样一个int就可以最多表示32个标志位,这样就足够使用了,这种数据结构就是之前学习过的位图。通过一个简单地demo就可以了解标志位的作用:
#include
#define ONE 0X1
#define TWO 0x2
#define THREE 0x4
#define FOUR 0x8
#define FIVE 0x10
void Print(int flags)
{
if (flags & ONE) printf("hello 1\n"); // 充当不同的行为
if (flags & TWO) printf("hello 2\n");
if (flags & THREE) printf("hello 3\n");
if (flags & FOUR) printf("hello 4\n");
if (flags & FIVE) printf("hello 5\n");
}
int main()
{
printf("---------------------------\n");
Print(ONE); // 传入不同的指令参数
printf("---------------------------\n");
Print(TWO);
printf("---------------------------\n");
Print(FOUR);
printf("---------------------------\n");
Print(ONE|THREE);
printf("---------------------------\n");
Print(ONE|THREE|FIVE);
printf("---------------------------\n");
Print(ONE|TWO|THREE|FOUR|FIVE);
printf("---------------------------\n");
}
然后就可以得到这样的结果,非常的显而易见通过在一个形参内传入不同的指令得到不同的结果:
open函数中的flags也就是这样的效果。
#define LOG "log.txt"
int main()
{
int fd = open(LOG, O_CREAT | O_WRONLY); // O_CREAT 表示若文件不存在就创建文件 O_WRONLY 表示读取文件
if (fd == -1)
{
printf("fd:%d, errno:%d, errstring:%s\n", fd, errno, strerror(errno)); // 输入对应的fd,错误码和打印错误码描述
}
printf("fd:%d, errno:%d, errstring:%s\n", fd, errno, strerror(errno));
close(fd);
return 0;
}
O_CREAT
添加了O_CREAT之后就可得到正确的结果:
umask()
但是查看文件会发现出现了这样的现象,因为创建文件是需要对应的权限的,而在函数中我们没有基于权限,这时就需要用到open的第二个带有权限的函数:
umask(0) int fd = open(LOG, O_CREAT | O_WRONLY, 0666);
再次查看文件确实生成的需要的文件,但是这个文件的权限并不是我们设立的666,因为文件权限会受到umask的影响,可以调用umask来对其进行修正。
O_TRUNC
清空文件:
当我们对消息的内容进行修改的时候可以发现,输入了新的内容之后旧的内容没有删除
这说明原有的参数方法不够,需要在添加O_TRUNC来清空文件,编译后再次运行得到:
// O_CREAT | O_WRONLY 默认不会对原始文件内容清空 int fd = open(LOG, O_CREAT | O_WRONLY | O_TRUNC, 0666);
O_APPEND
追加写:
int fd = open(LOG, O_WRONLY | O_CREAT | O_APPEND, 0666); // 只要进行写入就需要 O_WRONLY 若是没有文件就需要 O_CREAT ,最后是写文件的策略 O_APPEND 追加写
// 写入
const char* msg = "hello world";
int cnt = 5;
while (cnt)
{
char line[128]; // 缓冲区
snprintf(line, sizeof(line), "%s, %d\n", msg, cnt); // 格式化写入到line中
write(fd, line, strlen(line) + 1);// 这里的strlen不需要+1,\0是C语言中的规定,不是文件的规定!\0解释出来会变成乱码
write(fd, line, strlen(line));
// write(fd, msg, strlen(msg));
cnt--;
}
这里我们会遇到这样的问题,strlen是否需要+1,假设先让其+1,经过编译可以得到输出是正常的,但是当通过vim查看时可以发现出现了错误:
下面简要的介绍read接口,和部分相关的代码:
系统的接口读取与写入文件是没有/0的,而C语言是有'/0'的,在系统与C语言进行交互的时候需要注意'\0'的问题。
int fd = open(LOG, O_RDONLY);
// 读取
char buffer[1024];
// 这里我们无法做到按行读取,这里是整体读取的
ssize_t n = read(fd, buffer, sizeof(buffer) - 1); // 使用系统接口来进行I/O时,一定要注意\0的问题
if (n > 0)
{
buffer[n] = '\0';
printf("%s\n", buffer);
}