工学1号馆

home

« | 返回首页 | »

Unix文件I/O函数

By Wu Yudong on March 08, 2016

本文主要总结一下Unix文件I/O函数

文件描述符

Unix shell把文件描述符0与进程的标准输入关联、把文件描述符1与进程的标准输出关联、把文件描述符2与进程的标准错误关联,将幻数分别定义成常量STDIN_FILENOSTDOUT_FILENOSTDERR_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_CURSEEK_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

No comments yet.
To verify that you are human, please fill in "七"(required)