本文主要总结一下Unix文件I/O函数
文件描述符
Unix shell把文件描述符0与进程的标准输入关联、把文件描述符1与进程的标准输出关联、把文件描述符2与进程的标准错误关联,将幻数分别定义成常量STDIN_FILENO
、STDOUT_FILENO
、STDERR_FILENO
,这些常量都放在头文件<unistd.h>中定义。
函数open和openat
函数open和openat可以打开或创建一个文件。
#include <fcntl.h> int open(const char *path, int oflag, ... /* mode_t mode */ ); int openat(int fd, const char *path, int oflag, ... /* mode_t mode */ ); //Both return: file descriptor if OK, −1 on error
path
是要打开或创建文件的名字
oflag
是由头文件<fcntl.h>中定义的常量“或”运算而成
fd
参数把函数open和openat区分开:
(1)path参数指定的是绝对路径名,fd被忽略,此时两者等价
(2)path参数指定的是相对路径名,fd参数指出了相对路径名在文件系统中的开始地址,fd参数是通过打开相对路径名所在的目录来获取
(3)path参数指定的是相对路径名,fd参数具有特殊值AT_ FDCWD,此时路径名在当前目录中获取,此时两函数操作类似
函数creat
也可以调用creat函数创建一个新文件
#include <fcntl.h> int creat(const char *path, mode_t mode); //Returns: file descriptor opened for write-only if OK, −1 on error
此函数等价于:
open(path, O_WRONLY | O_CREAT | O_TRUNC, mode);
creat的不足之处是它以只写的方式打开所创建的文件,所以创建一个临时文件并要写该文件,然后再读,需经过creat、close、open这几个步骤
现在可以使用下面的方式实现等价:
open(path, O_RDWR | O_CREAT | O_TRUNC, mode);
函数close
用来关闭一个打开文件
#include <unistd.h> int close(int fd); //Returns: 0 if OK, −1 on error
注意:关闭一个文件还会释放该进程加在该文件上的所有记录锁
函数lseek
每个打开的文件都有一个与其关联的“当前文件偏移量”,通常是一个非负数,用以度量从文件开始处计算的字节数。
#include <unistd.h> off_t lseek(int fd, off_t offset, int whence); //Returns: new file offset if OK, −1 on error
每一个已打开的文件都有一个读写位置,当打开文件时通常其读写位置是指向文件开头,若是以附加的方式打开文件(如O_APPEND),则读写位置会指向文件尾。当read()或write()时,读写位置会随之增加,lseek()便是用来控制该文件的读写位置。参数fd 为已打开的文件描述词,参数offset 为根据参数whence来移动读写位置的位移数。offset:偏移量,每一读写操作所需要移动的距离,单位是字节的数量,可正可负(向前移,向后移)。
whence为下列其中一种:(SEEK_SET
,SEEK_CUR
和SEEK_END
和依次为0,1和2).
SEEK_SET 将读写位置指向文件头后再增加offset个位移量。
SEEK_CUR 以目前的读写位置往后增加offset个位移量。
SEEK_END 将读写位置指向文件尾后再增加offset个位移量。
当whence 值为SEEK_CUR 或SEEK_END时,参数offet允许负值的出现。
常见的操作如下:
(1) 欲将读写位置移到文件开头时:
lseek(int fd, 0, SEEK_SET);
(2) 欲将读写位置移到文件尾时:
lseek(int fd, 0, SEEK_END);
(3) 想要取得目前文件位置时:
lseek(int fd, 0, SEEK_CUR);
当调用成功时则返回目前的读写位置,也就是距离文件开头多少个字节。若有错误则返回-1,errno 会存放错误代码。可能设置erron的错误代码:
EBADF: fildes不是一个打开的文件描述符。
ESPIPE:文件描述符被分配到一个管道、套接字或FIFO。
EINVAL:whence取值不当
举个例子:
用于测试对于标准输入能否设置偏移量
#include "apue.h" int main(void) { if(lseek(STDIN_FILENO, 0, SEEK_CUR) == -1) printf("cannot seek\n"); else printf("seek ok\n"); exit(0); }
运行如下:
wu@ubuntu:~/opt/Cproject/apue/cp1$ ./a.out < /etc/passwd
seek ok
wu@ubuntu:~/opt/Cproject/apue/cp1$ cat < /etc/passwd| ./a.out
cannot seek
文件的偏移量可以大于文件的当前长度,对该文件的下一次写将加长该文件,并在文件中构成一个空洞。文件中的空洞并不要求在磁盘上占据存储区,来看一个例子:
下面的程序创建一个具有空洞的文件
#include "apue.h" #include <fcntl.h> char buf1[] = "abcdefghi"; char buf2[] = "ABCDEFGHI"; int main(void) { int fd; if((fd = creat("file.hole", FILE_MODE)) < 0) err_sys("creat error"); if(write(fd, buf1, 10) != 10) err_sys("buf1 write error"); if(lseek(fd, 16384, SEEK_SET) == -1) err_sys("lseek error"); if(write(fd, buf2, 10) != 10) err_sys("buf2 write error"); exit(0); }
运行该程序:
wu@ubuntu:~/opt/Cproject/apue/cp1$ ./a.out
wu@ubuntu:~/opt/Cproject/apue/cp1$ ls -l file.hole
-rw-r–r– 1 wu wu 16394 Mar 7 18:31 file.hole
wu@ubuntu:~/opt/Cproject/apue/cp1$ od -c file.hole
0000000 a b c d e f g h i \0 \0 \0 \0 \0 \0 \0
0000020 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0
*
0040000 A B C D E F G H I \0
0040012
od命令观察该文件的实际内容,-c表示以字符的方式打印该文件内容。每一行的开头都以八进制的形式表示字节偏移量
函数read
read函数从打开文件中读取数据
#include <unistd.h> ssize_t read(int fd, void *buf, size_t nbytes); //Returns: number of bytes read, 0 if end of file, −1 on error
read函数返回时,返回值说明了buf中前多少个字节是刚读上来的。有些情况下,实际读到的字节数(返回值)会小于请求读的字节数count,例如:读常规文件时,在读到count个字节之前已到达文件末尾。例如,距文件末尾还有30个字节而请求读100个字节,则read返回30,下次read将返回0。
函数write
write函数向打开文件写数据
#include <unistd.h> ssize_t write(int fd, const void *buf, size_t nbytes); //Returns: number of bytes written if OK, −1 on error
其返回值通常与参数nbytes的值相同,否则表示出错
举个例子:
使用read和write函数复制一个文件
#include "apue.h" #define BUFFSIZE 4096 int main(void) { int n; char buf[BUFFSIZE]; while((n = read(STDIN_FILENO, buf, BUFFSIZE)) > 0) { if(write(STDIN_FILENO, buf, n) != n) err_sys("write error"); } if(n < 0) err_sys("read error"); exit(0); }
Comments