文件IO的基础操作(一)
问题:怎么访问文件
在磁盘上有一文件data.dat,我们该如何去访问它呢?(1)在unix系统中,可以使用函数(open),传递一个文件,系统打开文件,加载文件数据, 返回一个ID, ID:文件描述符号.file description (fd),每个程序执行的时候都有一个目录,存放打开的文件描述符号.
(2)然后根据文件ID,得到数据.
(3)最后, 传递ID告诉系统释放文件.
1.打开文件
头文件:#include <sys/types.h> #include <sys/stat.h> #include <fcntl.h>定义函数:
int open(const char * pathname, int flags);
int open(const char * pathname, int flags, mode_t mode);
函数说明:
参数 pathname 指向欲打开的文件路径字符串.
参数flags 指文件的打开方式.
参数mode指文件的访问权限,在这里不讨论它.
返回值:若成功则返回文件描述符,若出错则返回-1.
下列是参数flags 所能使用的flags(这些常量定义在<fcntl.h>头文件):
O_RDONLY 以只读方式打开文件
O_WRONLY 以只写方式打开文件
O_RDWR 以可读写方式打开文件.
大多数实现将O_RDONLY定义为0,O_WRONLY定义为1,O_RDWR定义为2,以与早期的程序兼容.
下面是一个简单的例子1,打开一个文件:
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
void main()
{
int fd;
fd = open("data.txt",O_RDWR);
if(fd==-1)
{
printf("open file error:%m\n");
exit(-1);
}
printf("file description is:%d\n",fd);
}
输出::file description is:3 (data.txt文件存在)
:open file error:No such file or directory (data.txt文件不存在)
注意:由open函数返回的文件描述符一定是最小的未用描述符数值,由于0,1,2已经有标准输入,标准输出和标准出错输出占用,所以现在系统返回最小的文件描述符为3.
现在假设先关闭标准出错输出2,然后再打开文件,那返回的结果会是2.例子2:
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
void main()
{
int fd;
close(2); //关闭文件
fd = open("data.txt",O_RDWR);
if(fd==-1)
{
printf("open file error:%m\n");
exit(-1);
}
printf("file description is:%d\n",fd);
}
输出::file description is:2 (文件存在)
2.创建文件
方法一:当文件不存在时,如果用例子1的程序去打开会出错,假如我们想在文件不存在的时候也可以正常“打开”,这时该怎么办呢?孔明有靓招.例子3.
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
void main()
{
int fd;
fd = open("data.txt",O_RDWR|O_CREAT,0666);
if(fd==-1)
{
printf("open file error:%m\n");
exit(-1);
}
printf("file description is:%d\n",fd);
}
输出::file description is:3 (文件存在或不存在都可以运行)
历史回顾:
在早期的unix系统中,open函数的第二个参数flags只能是0、1或2.没有办法打开一个不存在的文件.但时代在改变,现在完全可以做到了.
上述三种flags(O_RDONLY、O_WRONLY和O_RDWR)是互斥的,不可同时使用, 也就是说在这三个常量中必须指定一个且只能指定一个.但可与下列的flags利用OR(|)运算符组合,前面两个(O_CREAT和O_EXCL)使用较为频繁.
O_CREAT 若欲打开的文件不存在则自动建立该文件.
O_EXCL 如果O_CREAT 也被设置, 此指令会去检查文件是否存在. 文件若不存在则建立该文件, 否则将导致打开文件错误. 此外, 若O_CREAT 与O_EXCL 同时设置, 并且欲打开的文件为符号连接, 则会打开文件失败.
O_NOCTTY 如果欲打开的文件为终端机设备时, 则不会将该终端机当成进程控制终端机.
O_TRUNC 若文件存在并且以可写的方式打开时, 此flags会令文件长度清为0, 而原来存于该文件的资料也会消失.
O_APPEND 当读写文件时会从文件尾开始移动, 也就是所写入的数据会以附加的方式加入到文件后面.
O_NONBLOCK 以不可阻断的方式打开文件, 也就是无论有无数据读取或等待, 都会立即返回进程之中.
O_NDELAY 同O_NONBLOCK.
O_SYNC 以同步的方式打开文件.
O_NOFOLLOW 如果参数pathname 所指的文件为一符号连接, 则会令打开文件失败.
O_DIRECTORY 如果参数pathname 所指的文件并非为一目录, 则会令打开文件失败.注:此为Linux2. 2 以后特有的flags, 以避免一些系统安全问题.
方法二:
系统也可调用creat函数创建一个新的文件.
头文件:#include<sys/types.h> #include<sys/stat.h> #include<fcntl.h>
函数定义:int creat(const char *pathname,mode_t mode);
函数说明:
参数pathname指向欲建立的文件路径字符串.
参数mode指文件的访问权限,在这里不讨论它.
返回值:若成功则返回为只写打开的文件描述符,若出错则返回-1.
例子4:创建一个文件
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
void main()
{
int fd;
fd = creat("data.txt",0666);
if(fd==-1)
{
printf("creat file error:%m\n");
exit(-1);
}
printf("file description is:%d\n",fd);
}
输出::file description is:3 (文件不存在也可以)
creat函数成功创建并打开了文件,但是creat函数有一个不足之处是它以只写方式打开创建文件.在提供open新版本之前,如果要创建一个临时文件,并要先写该文件,然后又读该文件,则必须先调用creat、close,燃火调用open.所creat最终是被时代给“淘汰”了.
新版本的open函数可以取代creat去完成它的使命.具体调用方式如下:
open(const char *pathname,(O_CREAT|O_WRONLY|O_TRUNC));
3.关闭文件
在以上的例子中,打开的文件没有关闭,虽然程序退出后系统会自动清理关闭它所打开的文件,但是在程序中最好关闭掉已不再需要的文件.系统可调用close函数关闭一个打开的文件,传递文件的fd即可:头文件:#include<unistd.h>
函数定义:int close(int filedes);
函数说明:
当使用完文件后若已不再需要则可使用close函数关闭该文件,close函数会让数据写会磁盘,并释放该文件所占用资源,文件与其文件号之间的关联将终结。参数fd为先前由open函数或creat函数所返回的文件描述符.例子5:
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
void main()
{
int fd;
fd = open("data.txt",O_RDWR|O_CREAT,0666);
if(fd==-1)
{
printf("open file error:%m\n");
exit(-1);
}
printf("file description is:%d\n",fd);
close(fd);
}
输出::file description is:3
4.错误代码
程序运行,出错总是难免的,人生的航向难道不是在不断的纠正错误中前行吗?1) open函数出错代码:
EEXIST 参数pathname所指文件已存在,却使用了O_CREAT和O_EXCL作为flgas.
EACCESS 参数pathname 所指的文件不符合所要求测试的权限.
EROFS 欲测试写入权限的文件存在于只读文件系统内.
EFAULT 参数pathname 指针超出可存取内存空间.
EINVAL 参数mode 不正确.
ENAMETOOLONG 参数 pathname 太长.
ENOTDIR 参数pathname 不是目录.
ENOMEM 核心内存不足.
ELOOP 参数pathname 有过多符号连接问题.
EIO I/O 存取错误.
例子6:演示EEXIST错误代码:
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
void main()
{
int fd;
fd = open("data.txt",O_RDWR|O_CREAT|O_EXCL,0666);
if(fd==-1)
{
printf("open file error:%m\n");
exit(-1);
}
printf("file description is:%d\n",fd);
close(fd);
}
输出::open file error:File exists (文件已存在)
2) creat函数出错代码:
EEXIST参数:pathname 所指的文件已存在 (验证文件存在,也可以正常创建).
EACCESS参数:pathname 所指定的文件不符合所要求测试的权限
EROFS:欲打开写入权限的文件存在于只读文件系统内
EFAULT参数:pathname 指针超出可存取的内存空间
EINVAL参数:mode 不正确.
ENAMETOOLONG参数:pathname 太长.
ENOTDIR 参数:pathname 为一目录
ENOMEM :核心内存不足.
ELOOP 参数:pathname 有过多符号连接问题.
EMFILE:已达到进程可同时打开的文件数上限
ENFILE:已达到系统可同时打开的文件数上限.
例子7:文件存在,但还是可以正常创建文件,且新的文件会覆盖旧的文件.
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
void main()
{
int fd;
fd = creat("data.txt",0666);
if(fd==-1)
{
printf("creat file error:%m\n");
exit(-1);
}
printf("file description is:%d\n",fd);
printf("creat file:%m\n");
close(fd);
}
输出::file description is:3
:creat file:Success
程序编译运行,无论文件是否存在,都可以正常输出.
3) close函数出错代码:
EBADF 参数fd 非有效的文件描述符或该文件已关闭.
EIO I/O 存取错误.
例子8:演示EBADF出错
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
void main()
{
int fd;
fd = open("data.txt",O_RDWR|O_CREAT,0666);
if(fd==-1)
{
printf("open file error:%m\n");
exit(-1);
}
printf("file description is:%d\n",fd);
close(fd);
if(close(fd)==-1)
printf("close file error:%m\n");
}
输出::file description is:3
:close file error:Bad file descriptor