---------------------------------
open, read, write, close 的基础使用
一个概念: 文件描述符 file descriptor, int 型的,用来与要操作的文件相关联
通常有三个值是默认的:
0 标准输入
1 标准输出
2 标准错误
打开文件的过程,就是将文件于一个文件描述符相关联:
int open(const char *path, int oflags);
int open(const char *path, int oflags, mode_t mode);
输入 要打开的或者要新建的文件路径 path, 设置读写的方式 oflags(只读,只写,读写),如果是新建一个文件,那么还可以设置文件权限 mode。
打开成功返回 int 文件描述符
读文件
size_t read(int fildes, void *buf, size_t nbytes);
输入参数 open出来的文件描述符 fildes, 从 fildes 中读取 nbytes 的Byte 到 buf 中去,返回值是成功读到的字节数,如果失败返回 -1
写文件
size_t write(int fildes, const void *buf, size_t nbytes);
和读文件差不多的,从 buf 里面读去 nbyes 的字节,到 fildes 所代表的文件中。返回实际写入的字节数
关闭
int close(int fildes);
关闭打开的文件描述符 fildes
以上是最最基本的操作,已经可以写个小程序来自己实践一下了:
实践描述:一个文件拷贝程序,把一个文件里的内容,拷贝到一个新的文件里去
以下是使用时要注意的一些问题,不全
open
- 注意打开的文件的读写属性,不要去写 只读的 CD-ROM 设备
- 第二个参数 oflags 还可以组合一些设置,常用的有 O_TRUNC(删掉原文件里的内容), O_APPEND(跟在原文件后面写), O_CREAT, O_CREAT|O_EXCL(强制新建文件,如果已经存在,则open失败)
- 如果使用了 O_CREAT, 就在第三个可选参数中表明新文件的权限
- 打开文件描述符占用内核资源,因此一个进程里可同时打开的文件描述符也有上限,通常为 1024 个 setrlimit 函数可以改变这个值
close
- 对于不同的设备,close 导致的行为也不一样的: 对于 close 一个 socket 而言,所做得操作就是断开两台电脑之间的 socket 网络 ...
以下 from APUE
read
- 有多种情况可使实际读到的字节数少于要求读字节数:
• 读普通文件时,在读到要求字节数之前已到达了文件尾端。例如,若在到达文件尾端之前还有3 0个字节,而要求读1 0 0个字节,则read返回3 0,下一次再调用read时,它将返回0 (文件尾端)。
• 当从终端设备读时,通常一次最多读一行(第11章将介绍如何改变这一点)。
• 当从网络读时,网络中的缓冲机构可能造成返回值小于所要求读的字节数。
• 某些面向记录的设备,例如磁带,一次最多返回一个记录。
- 读操作从文件的当前位移量处开始,在成功返回之前,该位移量增加实际读得的字节数。
write
- write出错的一个常见原因是:磁盘已写满,或者超过了对一个给定进程的文件长度限制
- 对于普通文件,写操作从文件的当前位移量处开始。如果在打开该文件时,指定了O_APPEND选择项,则在每次写操作之前,将文件位移量设置在文件的当前结尾处。在一次成功写之后,该文件位移量增加实际写的字节数。
文件的属性 - 文件权限的设置
上面提到的用 open 函数打开文件的时候,可以在第二个参数里设置文件的读写方式,如果是新建文件,还可以在第三个参数中设置文件的权限。
int open(const char *path, int oflags, mode_t mode);
关于 oflags 的常用设置
首先,O_RDONLY(只读)、O_WRONLY(只写)、O_RDWR(可读可写) 三各种必须选择一个,通常这也是最简单的设置了。
注意这些设置还要考虑到所选文件或设备的权限,属性,对只读文件进行可读可写设置是没有什么意义的
还有一些常用的选项:
O_TRUNC - 如果是打开已经存在的文件,此选项将新写入的内容替代掉原来的内容
O_APPEND - 新写入的内容,将跟在原来内容的后面
O_CREAT - 如果要打开的文件不存在,则创建此文件,注意,要确保文件创建的路径存在,且允许在此文件夹下新建文件
O_CREAT | O_EXCL - 强制新建文件,如果文件已经存在,则 open 函数返回失败。
这里还补充一个不常用的调用: create 函数
其实就相当于 open 函数带上 O_CREAT|O_WRONLY|O_TRUNC.
关于 mode 设置
使用 O_CREAT 创建的文件,需要对第三个可选参数进行一些权限设置
如果有一些linux/UNIX 下文件权限的知识呢,这个设置就比较好理解了
S_IRUSR: Read permission, owner
S_IWUSR: Write permission, owner
S_IXUSR: Execute permission, owner
S_IRGRP: Read permission, group
S_IWGRP: Write permission, group
S_IXGRP: Execute permission, group
S_IROTH: Read permission, others
S_IWOTH: Write permission, others
S_IXOTH: Execute permission, others
eg: open ("hello.txt", O_CREAT, S_IRUSR|S_IXOTH);
这个设置就把 hello.txt 的权限设置为 文件所有者有读权限,其他每个人都有执行的权限 —— 此文件有且仅有此两项权限
上面提到了流式文件操作与非流式文件操作,以及它们之间的转换
非流式的文件操作,也就是常用的f开头的,如 fopen, fread 等等函数,这里不再赘述,说说两者的转换:
文件描述符和文件指针的转换
使用两个函数,可以轻松实现两者的转换关系,这在一些场合(有时候用流式函数方便,但有时候有需要用文件描述符函数做些底层工作)还是很有用的。
int fileno(FILE *stream);
The function fileno examines the argument stream and returns its integer descriptor.
FILE *fdopen(int fildes, const char *mode);
FILE *freopen(const char *path, const char *mode, FILE *stream);
使用的时候,还是有些注意点的~
fdopen 函数,将一个文件指针与 fildes 的文件描述符相联系, mode 参数的值( "r", "r+", "w", "w+", "a", "a+")),要与 fildes 所代表文件的读写权限一致
在 fdopen 里设置 "w" "w+" 并不会象 fopen 那样,截断已存在的数据,还是会接着尾端写。
此函数常用于由创建管道和网络通信通道函数获得的插述符。因为这些特殊类型的文件不能用标准I/O fopen函数打开,首先必须先调用设备专用函数以获得一个文件描述符,然后用fdopen使一个标准I/O流与该描述符相结合。
freopen在一个特定的流上(由f p指示)打开一个指定的文件(其路径名由pathname 指示),如若该流已经打开,则先关闭该流。此函数一般用于将一个指定的文件打开为一个预定义的流:标准输入、标准输出或标准出错。
接下来讲讲流式I/O读写控制中,对缓冲区的控制方式
有三种类型:
全缓冲, 行缓冲, 无缓冲
全缓冲 (block buffer): 也就是说保存下要发送的数据,直到填满一个块,在一起发出去;通常的文件读写使用的都是全缓冲方式
行缓冲: 也就是接受到换行符,才进行读写;常见的用于标准输入stdin,标准输出stdout
无缓冲: 无缓冲机制,收到一个,就读写一个;常见的如标准出错流 stderr
可以使用以下函数改变文件流的缓冲方式
int setvbuf(FILE *stream, char *buf, int mode , size_t size);
mode 可以是:
_IONBF unbuffered 无缓冲
_IOLBF line buffered 行缓冲
_IOFBF fully buffered 全缓冲
setvbuf 必须在 open 了文件而没有进行其他文件操作前使用
强制刷新流使用 fflush 函数,可以是缓冲区中的数据立即写入文件
int fflush(FILE *stream);
文件的定位:
底层 I/O 使用:
off_t lseek(int fildes, off_t offset, int whence);
此系统调用,对文件描述符的读写指针进行设置,结合offset 和 whence 来确定这个位置。
whence 定义了 offset 偏移值的用法
SEEK_SET offset 是个绝对位置
SEEK_CUR offset 是从当前指针位置算起的相对位置
SEEK_END offset 是从文件尾算起的位置。
lseek 返回值是从文件头算起到设置出的偏移值。
off_t 类型定义在 sys/types.h 里
-----------------------
定位标准I/O流:
ftell
fseek
对于二进制文件,位置指示从开始处计算,以字节为单位;
QUOTE
对于文本文件,它们的文件当前位置可能不以简单的字节位移量来度量。再一次,这主要
也是在非U N I X系统中,它们可能以不同的格式存放文本文件。为了定位一个文本文件,
whence一定要是SEEK_SET,而且offset只能有两种值: 0(表示反绕文件至其起始位置),或是对该文件的ftell所返回的值。使用rewind函数也可将一个流设置到文件的起始位置。
当程序可能需要移植到非 unix 系统上时,要使用 fgetpos 和 fsetpos
whence 定义了 offset 偏移值的用法
SEEK_SET offset 是个绝对位置
SEEK_CUR offset 是从当前指针位置算起的相对位置
SEEK_END offset 是从文件尾算起的位置。
lseek 返回值是从文件头算起到设置出的偏移值。
偶觉得会不会是ms文本文件的保存方式与unix不同
unix无论怎样 都是保存成二进制文件 不去处理里面的内容
而ms保存时会把rn转换成n 读出来时再把n转换成rn 如果按文本文件打开的话 (难道以前考虑节省用户磁盘空间)
fseek在windows下还是看看msdn说明 看它是针对内存中的流进行寻址还是针对磁盘上文件进行寻址 或是自己写个小程序试试
fstat stat 和 lstat 系统调用
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
int fstat(int fildes, struct stat *buf);
int stat(const char *path, struct stat *buf);
int lstat(const char *path, struct stat *buf);
fstat 函数可以获得与 fildes 相关联的文件的信息.
三个函数的第二个参数均是 stat 结构的,stat 在不同unix系统中有所不同,主要可以获得一些 文件权限,类型,访问时间,文件身份标示等等的信息。
其中涉及一些宏定义,可在 sys/types.h 中找到
例子:
struct stat statbuf;
mode_t modes;
stat("filename", &statbuf);
modes = statbuf.st_mode;
fcntl 可以用来改变文件的很多属性,如复制,获取和设置文件描述符,改变文件状态,文件加锁。
#include <fcntl.h>
int fcntl(int fildes, int cmd);
int fcntl(int fildes, int cmd, long arg);
fildes 就是要操作的文件描述符
cmd 决定对 fildes 进行什么样的操作
APUE:
fcntl 函数有五种功能:
• 复制一个现存的描述符(cmd=F_DUPFD)。—— 功能类似于 dup/dup2 但还是有些细节区别的;
• 获得/设置文件描述符标记(cmd = F_GETFD或F_SETFD)。
• 获得/设置文件状态标志(cmd = F_GETFL或F_SETFL)。
• 获得/设置异步I / O有权(cmd = F_GETOWN或F_SETOWN)。
• 获得/设置记录锁(cmd = F_GETLK , F_SETLK或F_SETLKW)。