C语言文件管理之一 文件的打开和写入 fopen函数和打开模式

C语言的文件管理,其实就是指文件相关的一些系统调用的使用。这中间包含了很多文件操作的设计原理。
和文件相关的API都是以f开头的,比如fopen,fwrite,fread,fprintf等。要操作文件,第一步就是要打开文件,C语言提供了fopen函数来完成这个操作。

1.打开文件fopen函数

先来看一下fopen函数的原型:

FILE *fopen (const char *filename,const char *modes)

第一个参数插入文件名或者文件路径。第二个参数传入打开方式,这个参数还是比较复杂的,而且有许多关联的疑问,下面会一一提出并回答。
先来说说,modes这个参数所支持的内容。在C语言标准里面这个modes就6种形式——r,w,a,r+,w+,a+。
其它的比如b,t,x等都是不符合标准的,也就是不具备可移植性。特别是微软公司的C版本,有很多自己的实现,这些都是只能在windows下才能运行的,所以不讨论。也是为了简洁性,在理解了最标准的内容后,再看这些额外的内容也是比较容易的。
下面列出的声明来自GNU man手册是符合C语言标准的最基本的6种模式。

mode	Access
"r"		打开以便读取。 如果文件不存在或找不到, fopen 调用将失败。
"w"		打开用于写入的空文件。 如果给定文件存在,则其内容会被销毁。
"a"		在文件末尾打开以进行写入(追加),在新数据写入到文件之前不移除文件末尾 (EOF) 标记。 创建文件(如果文件不存在)。
"r+"	打开以便读取和写入。 文件必须存在。
"w+"	打开用于读取和写入的空文件。 如果文件存在,则其内容会被销毁。
"a+"	打开以进行读取和追加。 追加操作包括在新数据写入文件之前移除 EOF 标记。 写入完成后,EOF 标记不会还原。 创建文件(如果文件不存在)。

总的来说,其实就3种:r,w,a。
有下面的特性:
1 . 使用r和r+的时候,文件必须已经存在了,不然报错。
这也好理解,要读文件,文件必须要存在啊
2.使用w和w+的时候,如果文件不存在,会自动帮你创建文件。
这也好理解,我是要写文件,文件不存在就帮你创建,存在就直接往里面写东西。
在实际使用中会有非常多的疑问,下面以问题的形式一一回答。
问题一:r和r+有什么区别?
用r+打开的文件可以读也可以写。这确实非常奇怪。相对于是“rw”,这里只是比喻,并不能这样写。比如下面的代码。

int main() {
	//打开文件
    FILE *file = fopen("test.txt", "r");
    if (file == NULL) {
        perror("fopen");
        EXIT_FAILURE;
    }

    char buf[] = "abcd";
	//写入内容
    size_t ret = fwrite(buf, sizeof(buf) - 1, 1, file);

    printf("ret=%ld\n", ret);
    if (ret != sizeof(buf)) {
        perror("fwrite");
        EXIT_FAILURE;
    }
    fclose(file);
    return 0;
}

如果使用的是r,那么fwrite函数(fwite函数后面会介绍,这里先知道他是可以用来写数据到文件就可以了)。执行后返回0,这是一个错误代码,perror会输出错误内容。

ret=0
fwrite: Bad file descriptor

如果使用r+,那么就可以使用fwrite函数对这个文件进行写入。并不会报错,程序正常退出。r+既可以读也可以写,这非常的奇怪。
所以区别就是有没有额外的写功能。
如果还是困惑可以看问题二。
问题二:r+和w+的区别?或者为什么r+既可以写又可以读?
这个问题可以接着问题一。为什么明明取名字叫做r+,却可以写内容呢?而且6个模式里面还有一个w+,也是即可以读也可以写?先看下下面的例子:

int main() {
    FILE *file = fopen("test.txt", "w");
    if (file == NULL) {
        perror("fopen");
        EXIT_FAILURE;
    }

    char buf[4] ;

    //size_t ret = fwrite(buf, sizeof(buf) - 1, 1, file);
    size_t ret=fread(buf, sizeof(buf),1,file);

    printf("ret=%ld\n", ret);
    if (ret != sizeof(buf)) {
        perror("fwrite");
        EXIT_FAILURE;
    }
    fclose(file);
    return 0;
}

这个例子和第一个问题类似,只是模式改为了w,而且使用了fread函数来读取数据(只要知道fread函数是用来读数据就行了,先不用深究)。会报下面的错误。

ret=0
fwrite: Bad file descriptor

可见,声明为w是没有读的能力的,如果改为w+就不会报错了。
所以r+和w+几乎是一样的,可以理解为“rw”和"wr"。只是强调点不同。
但是两者还是有区别的,r+本质上还是强调读,文件必须存在。w+本质上还是强调写,文件不存在会创建。

问题三:a和a+又是怎么回事?以及和w+的区别。
a的本质还是写,和w是差不多的。只是写的位置不同,w是会清空内容然后开始写,而a是会在末尾接着写。至于有没有+号,和前面是一样的,这里是有没有读能力。
例子和前面也差不多:

int main() {
    FILE *file = fopen("test.txt", "a");
    if (file == NULL) {
        perror("fopen");
        EXIT_FAILURE;
    }

    char buf[4] ;

    size_t ret=fread(buf, sizeof(buf),1,file);

    printf("ret=%ld\n", ret);
    if (ret != sizeof(buf)) {
        perror("fwrite");
        EXIT_FAILURE;
    }
    fclose(file);
    return 0;
}

使用a然后执行fread函数,是会发生错误的。perror输出内容和前面一样。可见a是没有读能力的,他的本质是写,如果用a+就有读能力了。
问题四:为什么a是往文件末尾插入?我想往文件开头或者任意位置插入怎么办?
六个模式里面是没有办法往开头插入内容的。虽然w是往开头插入,但是会把内容清空。只有a能实现插入功能,而且是往文件末尾插入。首先,我们要知道,写数据比读数据更耗性能。插入意味着插入位置后面的内容全部要移动。这其实是比较复杂的功能,本着unix简单的原则,提供fseek函数来单独完成这样的功能。所以要想在任意位置插入内容,用fseek函数(这里不深入讲,单独写一篇讲fseek函数)。fopen函数不管这个事情,因为负担太重。

  • 3
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值