来源:公众号【编程珠玑】 作者:守望先生 ID:shouwangxiansheng 我们都听过Linux下一切皆文件,实际上无论是普通的文件读写,还是网络IO读写,它们都有着类似的操作过程。本文通过基本文件IO操作,来了解Linux“一切文件”的读写。当然过程中穿插着很多其他内容。 文件I/O过程在介绍具体的函数使用之前,我必须说明一下文件I/O的基本过程。它们类似过程如下:
文件描述符是什么?你可以认为是一个对文件进行操作的凭据,你只有通过它才能对文件进行读写。它是一个非负整数。通常0是标准输入,1是标准输出,2是标准错误(参考《如何理解Linux shell中“2>&1”》)。正是有了它们,你的简单程序才可以从控制台读入数据,输出日志,输出错误打印等等。 记得很小的时候,家里连压水的工具都没有,需要用水的时候,都是用一个小点的桶从井里打水。 类比文件I/O操作,打开井盖,拿到绑着绳子的水桶,就像是打开文件,获取文件描述符;而打水的过程,就像对文件进行读写;最后需要的时候,又把桶放回去,并盖上井盖;而这就像关闭文件描述符。 当然了,如果嫌弃里面的小桶打水太慢,有的人可能会用一担大桶用来装水,装满一担后,再挑走使用。而这个过程就像使用了缓冲。(参考《不可不知的三种缓冲》)。 说了这么多废话,文件I/O到底怎么操作呢?本文介绍的是不带缓冲的I/O函数。 打开文件,获取文件描述符主要函数: #include<fcntl.h> 参数解释:
这里的文件名应该不用过多解释,但是flags需要做一些说明,
而下面的选项是可选的:
当打开成功时返回文件描述符,否则返回-1,并且设置errno。 读写操作读写操作主要有两个函数:
参数说明:
这里的fd就是前面拿到的文件描述符。篇幅有限, 本文暂不涉及具体的读写介绍。 关闭文件调用close函数即可,它的参数是前面打开的时候获得的文件描述符 #include <unistd.h> 成功返回0,失败则返回-1,并且会设置errno。 实例以上都太过理论化了,那么理论结合实际来看看,究竟是怎样的。 打开一个不存在的文件这是最简单的情况,现在假设,当前目录下没有test.txt
运行报错: open failed: No such file or directory 还记得前面说的,如果出错就会设置errno吗?当open返回-1(很多系统接口类似)时,就会设置errno,这个时候就可以调用perror接口打印对应的错误信息。便于我们定位问题。即:
上面两种方式都可以打印出错误信息,区别在于,前者输出到标准错误,后者输出到标准输出。 还记得在《不可不知的三种缓冲》中说的吗?标准错误通常是不带缓冲的。 打开一个文件,不存在时创建既然不存在时,会打开失败,那么不存在就创建好了,这就用到了O_CREATE标志。因此修改open函数那一行: int fd = open('test.txt',O_WRONLY | O_CREAT); 运行结果:
并且会在test.txt发现写入的内容。 注意到,多个标志使用|构成flags参数。 打开一个文件,存在时截断好了,前面已经实现了文件不存在时,创建,存在时也可以正常打开,如果存在时,又不想要原先的内容?那就需要用到O_TRUNC标志。 int fd = open('test.txt',O_WRONLY | O_CREAT | O_TRUNC); 现在假设test.txt文件存在,且里面有内容,再次运行后,发现打开文件正常,且内容只有新加入的,而没有之前存在的。 在打开的文件后追加内容如果想在打开的文件后追加内容,那么可以使用O_APPEND标志:
这样如果原来test.txt中有内容,则可以往文件中追加内容。 只读打开的文件进行写操作前面提到了5个打开标志,如果以只读方式尝试写会怎样? int fd = open('test.txt',O_RDONLY); 你会发现:
以只读方式打开,却尝试写,自然是会写失败了。因此对应的操作要设置对应的标志位,否则会失败。 总结以上就是文件I/O的基本操作。关键就三个步骤:
其中的模式无非是前面提到的一些模式,如读,写,或可读可写,而操作,常见为写入内容,读出内容等等,关闭就更好理解了。以上。 错误处理原则: |
|